mirror of
https://github.com/python/cpython.git
synced 2025-11-12 07:02:33 +00:00
Fix typos in gcmodule.c and restructure comments for clarity (GH-17983)
This commit is contained in:
parent
d23f78267a
commit
97f1267a54
1 changed files with 36 additions and 44 deletions
|
|
@ -609,7 +609,7 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
|
||||||
// NEXT_MASK_UNREACHABLE flag, we set it unconditionally.
|
// NEXT_MASK_UNREACHABLE flag, we set it unconditionally.
|
||||||
// But this may pollute the unreachable list head's 'next' pointer
|
// But this may pollute the unreachable list head's 'next' pointer
|
||||||
// too. That's semantically senseless but expedient here - the
|
// too. That's semantically senseless but expedient here - the
|
||||||
// damage is repaired when this fumction ends.
|
// damage is repaired when this function ends.
|
||||||
last->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)gc);
|
last->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)gc);
|
||||||
_PyGCHead_SET_PREV(gc, last);
|
_PyGCHead_SET_PREV(gc, last);
|
||||||
gc->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)unreachable);
|
gc->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)unreachable);
|
||||||
|
|
@ -1039,7 +1039,7 @@ clear_freelists(void)
|
||||||
(void)PyContext_ClearFreeList();
|
(void)PyContext_ClearFreeList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show stats for objects in each gennerations.
|
// Show stats for objects in each generations
|
||||||
static void
|
static void
|
||||||
show_stats_each_generations(GCState *gcstate)
|
show_stats_each_generations(GCState *gcstate)
|
||||||
{
|
{
|
||||||
|
|
@ -1058,17 +1058,17 @@ show_stats_each_generations(GCState *gcstate)
|
||||||
buf, gc_list_size(&gcstate->permanent_generation.head));
|
buf, gc_list_size(&gcstate->permanent_generation.head));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deduce wich objects among "base" are unreachable from outside the list
|
/* Deduce which objects among "base" are unreachable from outside the list
|
||||||
and move them to 'unreachable'. The process consist in the following steps:
|
and move them to 'unreachable'. The process consist in the following steps:
|
||||||
|
|
||||||
1. Copy all reference counts to a different field (gc_prev is used to hold
|
1. Copy all reference counts to a different field (gc_prev is used to hold
|
||||||
this copy to save memory).
|
this copy to save memory).
|
||||||
2. Traverse all objects in "base" and visit all referred objects using
|
2. Traverse all objects in "base" and visit all referred objects using
|
||||||
"tp_traverse" and for every visited object, substract 1 to the reference
|
"tp_traverse" and for every visited object, subtract 1 to the reference
|
||||||
count (the one that we copied in the previous step). After this step, all
|
count (the one that we copied in the previous step). After this step, all
|
||||||
objects that can be reached directly from outside must have strictly positive
|
objects that can be reached directly from outside must have strictly positive
|
||||||
reference count, while all unreachable objects must have a count of exactly 0.
|
reference count, while all unreachable objects must have a count of exactly 0.
|
||||||
3. Indentify all unreachable objects (the ones with 0 reference count) and move
|
3. Identify all unreachable objects (the ones with 0 reference count) and move
|
||||||
them to the "unreachable" list. This step also needs to move back to "base" all
|
them to the "unreachable" list. This step also needs to move back to "base" all
|
||||||
objects that were initially marked as unreachable but are referred transitively
|
objects that were initially marked as unreachable but are referred transitively
|
||||||
by the reachable objects (the ones with strictly positive reference count).
|
by the reachable objects (the ones with strictly positive reference count).
|
||||||
|
|
@ -1098,10 +1098,38 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
|
||||||
|
|
||||||
/* Leave everything reachable from outside base in base, and move
|
/* Leave everything reachable from outside base in base, and move
|
||||||
* everything else (in base) to unreachable.
|
* everything else (in base) to unreachable.
|
||||||
|
*
|
||||||
* NOTE: This used to move the reachable objects into a reachable
|
* NOTE: This used to move the reachable objects into a reachable
|
||||||
* set instead. But most things usually turn out to be reachable,
|
* set instead. But most things usually turn out to be reachable,
|
||||||
* so it's more efficient to move the unreachable things. See note
|
* so it's more efficient to move the unreachable things. It "sounds slick"
|
||||||
^ [REACHABLE OR UNREACHABLE?] at the file end.
|
* to move the unreachable objects, until you think about it - the reason it
|
||||||
|
* pays isn't actually obvious.
|
||||||
|
*
|
||||||
|
* Suppose we create objects A, B, C in that order. They appear in the young
|
||||||
|
* generation in the same order. If B points to A, and C to B, and C is
|
||||||
|
* reachable from outside, then the adjusted refcounts will be 0, 0, and 1
|
||||||
|
* respectively.
|
||||||
|
*
|
||||||
|
* When move_unreachable finds A, A is moved to the unreachable list. The
|
||||||
|
* same for B when it's first encountered. Then C is traversed, B is moved
|
||||||
|
* _back_ to the reachable list. B is eventually traversed, and then A is
|
||||||
|
* moved back to the reachable list.
|
||||||
|
*
|
||||||
|
* So instead of not moving at all, the reachable objects B and A are moved
|
||||||
|
* twice each. Why is this a win? A straightforward algorithm to move the
|
||||||
|
* reachable objects instead would move A, B, and C once each.
|
||||||
|
*
|
||||||
|
* The key is that this dance leaves the objects in order C, B, A - it's
|
||||||
|
* reversed from the original order. On all _subsequent_ scans, none of
|
||||||
|
* them will move. Since most objects aren't in cycles, this can save an
|
||||||
|
* unbounded number of moves across an unbounded number of later collections.
|
||||||
|
* It can cost more only the first time the chain is scanned.
|
||||||
|
*
|
||||||
|
* Drawback: move_unreachable is also used to find out what's still trash
|
||||||
|
* after finalizers may resurrect objects. In _that_ case most unreachable
|
||||||
|
* objects will remain unreachable, so it would be more efficient to move
|
||||||
|
* the reachable objects instead. But this is a one-time cost, probably not
|
||||||
|
* worth complicating the code to speed just a little.
|
||||||
*/
|
*/
|
||||||
gc_list_init(unreachable);
|
gc_list_init(unreachable);
|
||||||
move_unreachable(base, unreachable); // gc_prev is pointer again
|
move_unreachable(base, unreachable); // gc_prev is pointer again
|
||||||
|
|
@ -1197,7 +1225,7 @@ collect(PyThreadState *tstate, int generation,
|
||||||
gc_list_merge(young, old);
|
gc_list_merge(young, old);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* We only untrack dicts in full collections, to avoid quadratic
|
/* We only un-track dicts in full collections, to avoid quadratic
|
||||||
dict build-up. See issue #14775. */
|
dict build-up. See issue #14775. */
|
||||||
untrack_dicts(young);
|
untrack_dicts(young);
|
||||||
gcstate->long_lived_pending = 0;
|
gcstate->long_lived_pending = 0;
|
||||||
|
|
@ -2269,39 +2297,3 @@ PyObject_GC_Del(void *op)
|
||||||
}
|
}
|
||||||
PyObject_FREE(g);
|
PyObject_FREE(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------
|
|
||||||
Notes
|
|
||||||
|
|
||||||
[REACHABLE OR UNREACHABLE?]
|
|
||||||
|
|
||||||
It "sounds slick" to move the unreachable objects, until you think about
|
|
||||||
it - the reason it pays isn't actually obvious.
|
|
||||||
|
|
||||||
Suppose we create objects A, B, C in that order. They appear in the young
|
|
||||||
generation in the same order. If B points to A, and C to B, and C is
|
|
||||||
reachable from outside, then the adjusted refcounts will be 0, 0, and 1
|
|
||||||
respectively.
|
|
||||||
|
|
||||||
When move_unreachable finds A, A is moved to the unreachable list. The
|
|
||||||
same for B when it's first encountered. Then C is traversed, B is moved
|
|
||||||
_back_ to the reachable list. B is eventually traversed, and then A is
|
|
||||||
moved back to the reachable list.
|
|
||||||
|
|
||||||
So instead of not moving at all, the reachable objects B and A are moved
|
|
||||||
twice each. Why is this a win? A straightforward algorithm to move the
|
|
||||||
reachable objects instead would move A, B, and C once each.
|
|
||||||
|
|
||||||
The key is that this dance leaves the objects in order C, B, A - it's
|
|
||||||
reversed from the original order. On all _subsequent_ scans, none of
|
|
||||||
them will move. Since most objects aren't in cycles, this can save an
|
|
||||||
unbounded number of moves across an unbounded number of later collections.
|
|
||||||
It can cost more only the first time the chain is scanned.
|
|
||||||
|
|
||||||
Drawback: move_unreachable is also used to find out what's still trash
|
|
||||||
after finalizers may resurrect objects. In _that_ case most unreachable
|
|
||||||
objects will remain unreachable, so it would be more efficient to move
|
|
||||||
the reachable objects instead. But this is a one-time cost, probably not
|
|
||||||
worth complicating the code to speed just a little.
|
|
||||||
------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue