@@ -201,16 +201,14 @@ When the nursery is full, the unreachable cycles in the oldest aging space
201201 reachable to visited. They are reachable and cannot be garbage.
202202 """
203203 moved_to_visited = 0
204- while reachable:
204+ while reachable and moved_to_visited < limit :
205205 root = reachable.pop()
206206 visited.append(root)
207207 moved_to_visited += 1
208208 for obj in gc.get_referents(root):
209209 if obj in pending:
210210 pending.remove(obj)
211211 reachable.append(obj)
212- if moved_to_visited >= limit:
213- return moved_to_visited
214212 return moved_to_visited
215213
216214 def old_collection():
@@ -230,14 +228,14 @@ When the nursery is full, the unreachable cycles in the oldest aging space
230228 # form transitive closure starting at obj, taking objects from pending
231229 increment = form_transitive_closure(obj, pending)
232230 candidates = len(increment)
231+ work_to_do -= candidates
233232 survivors = collect_cycles(increment)
234- work_to_do -= survivors
235- collected = candidates - survivors
233+ collected = candidates - len( survivors)
234+ visited_space.extend( survivors)
236235 # If we are collecting lots of objects, that means
237- # there is a lot of cycle garbage and we need to
238- # sweep the heap faster.
236+ # there is a lot of cycle garbage and we should sweep
237+ # the heap faster to keep the amount of garbage down
239238 work_to_do += 2 * collected
240- visited_space.extend(survivors)
241239
242240The legacy collector
243241--------------------
@@ -283,11 +281,17 @@ of half spaces is always rounded up to an even number.
283281Performance
284282-----------
285283
286- Performance is improved relative to the current collector.
287- The performance improvements come from doing less work in the young
288- generations (one collection per object, not two) and doing less work
289- in the old generation due to the lower survivor rate from the young
290- generations.
284+ The new collector reduces the overhead of cyclic garbage collection by almost
285+ half, although the exact amount depends on the application.
286+
287+ By allowing objects longer to die, the effectiveness of the collector is
288+ improved. This allows it to collect the same amount of garbage for less work.
289+ Performance is further improved by scanning fewer objects during collections:
290+
291+ * In the young generation: each object is only scanned once, instead of twice
292+ in the generational GC
293+ * In the old generation: objects are scanned at a lower rate, only increasing
294+ that rate when necessary to collect excess garbage
291295
292296Peak Memory Consumption
293297-----------------------
@@ -322,10 +326,10 @@ Calling ``gc.collect()`` is equivalent to calling ``gc.collect(2)``.
322326================== ==============================================
323327 Argument Effect
324328================== ==============================================
325- 0 Perform a young collection,
326- collecting the oldest aging space
327- 1 Collect an increment of the old generation
328- 2 or no argument Collect the whole heap
329+ 0 Perform a young collection,
330+ collecting the oldest aging space
331+ 1 Collect an increment of the old generation
332+ 2 or no argument Collect the whole heap
329333================== ==============================================
330334
331335Choosing the legacy generational collector
@@ -398,24 +402,6 @@ collections will usually mean shorter pauses per collection.
398402Future work
399403===========
400404
401- Further reducing pause times
402- ----------------------------
403-
404- While the reference implementation is 1-2% faster than main (with the
405- generational GC), it can still have long pause times on large object graphs.
406- Many of the benchmarks have a single large tree as their object graph, and this
407- can result in long pauses, as an increment starting at the root of the tree
408- will contain almost the whole heap.
409-
410- This could be improved in a few ways:
411-
412- * Sorting the increments as they are either created or sent to the old
413- generation, so that the objects farthest from the root are picked first in
414- the next collection.
415- * Traversing the stack prior to increment formation to skip reachable objects.
416- This will complicate the algorithm, but could save significant amounts of
417- work in some cases.
418-
419405Porting to the free-threaded build
420406----------------------------------
421407
@@ -431,6 +417,18 @@ Porting the incremental GC to the free-threaded build will need a few changes:
431417 external arrays for the young generation and the increments. The
432418 free-threaded GC already needs to do this to partition garbage and survivors.
433419
420+ Further reducing pause times
421+ ----------------------------
422+
423+ While the reference implementation generally has shorter pause times, it
424+ can still have long pause times if the transitive closure needed for an
425+ increment is large.
426+
427+ It may be possible to scan increments over multiple collections, keeping
428+ each pause short. This would be challenging as the program may transform the
429+ object graph of the increment between collections, but garbage cycles cannot be
430+ modified by the program so this might be possible. There is extensive research
431+ on concurrent collectors which also have to handle similar problems.
434432
435433Reference Implementation
436434========================
0 commit comments