Skip to content

Commit 50803e5

Browse files
authored
PEP 848: Clarifications and bug fixes (#5147)
Fix bugs in the Python code for the algorithm and clarify that code Remove from future work, work that has already been done. Improve explanation of why performance is better
1 parent ce48c4e commit 50803e5

1 file changed

Lines changed: 33 additions & 35 deletions

File tree

‎peps/pep-0848.rst‎

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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

242240
The legacy collector
243241
--------------------
@@ -283,11 +281,17 @@ of half spaces is always rounded up to an even number.
283281
Performance
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

292296
Peak 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

331335
Choosing the legacy generational collector
@@ -398,24 +402,6 @@ collections will usually mean shorter pauses per collection.
398402
Future 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-
419405
Porting 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

435433
Reference Implementation
436434
========================

0 commit comments

Comments
 (0)