Commit a2a2988a authored by Neil Schemenauer's avatar Neil Schemenauer

Move all data for a single generation into a structure. The set of

generations is now an array.  This cleans up some code and makes it easy
to change the number of generations.  Also, implemented a
gc_list_is_empty() function.  This makes the logic a little clearer in
places.  The performance impact of these changes should be negligible.

One functional change is that allocation/collection counters are always
zeroed at the start of a collection.  This should fix SF bug #551915.
This change is too big for back-porting but the minimal patch on SF
looks good for a bugfix release.
parent d79de70a
...@@ -31,20 +31,27 @@ ...@@ -31,20 +31,27 @@
/*** Global GC state ***/ /*** Global GC state ***/
struct gc_generation {
PyGC_Head head;
int threshold; /* collection threshold */
int count; /* count of allocations or collections of younger
generations */
};
#define NUM_GENERATIONS 3
#define GEN_HEAD(n) (&generations[n].head)
/* linked lists of container objects */ /* linked lists of container objects */
PyGC_Head _PyGC_generation0 = {{&_PyGC_generation0, &_PyGC_generation0, 0}}; static struct gc_generation generations[NUM_GENERATIONS] = {
static PyGC_Head generation1 = {{&generation1, &generation1, 0}}; /* PyGC_Head, threshold, count */
static PyGC_Head generation2 = {{&generation2, &generation2, 0}}; {{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0},
static int generation = 0; /* current generation being collected */ {{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0},
{{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0},
};
/* collection frequencies, XXX tune these */ PyGC_Head *_PyGC_generation0 = GEN_HEAD(0);
static int enabled = 1; /* automatic collection enabled? */
static int threshold0 = 700; /* net new containers before collection */
static int threshold1 = 10; /* generation0 collections before collecting 1 */
static int threshold2 = 10; /* generation1 collections before collecting 2 */
/* net new objects allocated since last collection */ static int enabled = 1; /* automatic collection enabled? */
static int allocated;
/* true if we are currently running the collector */ /* true if we are currently running the collector */
static int collecting; static int collecting;
...@@ -81,6 +88,12 @@ gc_list_init(PyGC_Head *list) ...@@ -81,6 +88,12 @@ gc_list_init(PyGC_Head *list)
list->gc.gc_next = list; list->gc.gc_next = list;
} }
static int
gc_list_is_empty(PyGC_Head *list)
{
return (list->gc.gc_next == list);
}
static void static void
gc_list_append(PyGC_Head *node, PyGC_Head *list) gc_list_append(PyGC_Head *node, PyGC_Head *list)
{ {
...@@ -101,8 +114,7 @@ gc_list_remove(PyGC_Head *node) ...@@ -101,8 +114,7 @@ gc_list_remove(PyGC_Head *node)
static void static void
gc_list_move(PyGC_Head *from, PyGC_Head *to) gc_list_move(PyGC_Head *from, PyGC_Head *to)
{ {
if (from->gc.gc_next == from) { if (gc_list_is_empty(from)) {
/* empty from list */
gc_list_init(to); gc_list_init(to);
} }
else { else {
...@@ -119,7 +131,7 @@ static void ...@@ -119,7 +131,7 @@ static void
gc_list_merge(PyGC_Head *from, PyGC_Head *to) gc_list_merge(PyGC_Head *from, PyGC_Head *to)
{ {
PyGC_Head *tail; PyGC_Head *tail;
if (from->gc.gc_next != from) { if (!gc_list_is_empty(from)) {
tail = to->gc.gc_prev; tail = to->gc.gc_prev;
tail->gc.gc_next = from->gc.gc_next; tail->gc.gc_next = from->gc.gc_next;
tail->gc.gc_next->gc.gc_prev = tail; tail->gc.gc_next->gc.gc_prev = tail;
...@@ -330,7 +342,7 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old) ...@@ -330,7 +342,7 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
{ {
inquiry clear; inquiry clear;
while (unreachable->gc.gc_next != unreachable) { while (!gc_list_is_empty(unreachable)) {
PyGC_Head *gc = unreachable->gc.gc_next; PyGC_Head *gc = unreachable->gc.gc_next;
PyObject *op = FROM_GC(gc); PyObject *op = FROM_GC(gc);
if (debug & DEBUG_SAVEALL) { if (debug & DEBUG_SAVEALL) {
...@@ -354,23 +366,45 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old) ...@@ -354,23 +366,45 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
/* This is the main function. Read this to understand how the /* This is the main function. Read this to understand how the
* collection process works. */ * collection process works. */
static long static long
collect(PyGC_Head *young, PyGC_Head *old) collect(int generation)
{ {
int i;
long n = 0; long n = 0;
long m = 0; long m = 0;
PyGC_Head *young; /* the generation we are examining */
PyGC_Head *old; /* next older generation */
PyGC_Head reachable; PyGC_Head reachable;
PyGC_Head unreachable; PyGC_Head unreachable;
PyGC_Head finalizers; PyGC_Head finalizers;
PyGC_Head *gc; PyGC_Head *gc;
if (debug & DEBUG_STATS) { if (debug & DEBUG_STATS) {
PySys_WriteStderr( PySys_WriteStderr("gc: collecting generation %d...\n",
"gc: collecting generation %d...\n" generation);
"gc: objects in each generation: %ld %ld %ld\n", PySys_WriteStderr("gc: objects in each generation:");
generation, for (i = 0; i < NUM_GENERATIONS; i++) {
gc_list_size(&_PyGC_generation0), PySys_WriteStderr(" %ld", gc_list_size(GEN_HEAD(i)));
gc_list_size(&generation1), }
gc_list_size(&generation2)); PySys_WriteStderr("\n");
}
/* update collection and allocation counters */
if (generation+1 < NUM_GENERATIONS)
generations[generation+1].count += 1;
for (i = 0; i <= generation; i++)
generations[generation].count = 0;
/* merge younger generations with one we are currently collecting */
for (i = 0; i < generation; i++) {
gc_list_merge(GEN_HEAD(i), GEN_HEAD(generation));
}
/* handy references */
young = GEN_HEAD(generation);
if (generation < NUM_GENERATIONS-1) {
old = GEN_HEAD(generation+1);
} else {
old = GEN_HEAD(NUM_GENERATIONS-1);
} }
/* Using ob_refcnt and gc_refs, calculate which objects in the /* Using ob_refcnt and gc_refs, calculate which objects in the
...@@ -449,41 +483,22 @@ collect(PyGC_Head *young, PyGC_Head *old) ...@@ -449,41 +483,22 @@ collect(PyGC_Head *young, PyGC_Head *old)
PyErr_WriteUnraisable(gc_str); PyErr_WriteUnraisable(gc_str);
Py_FatalError("unexpected exception during garbage collection"); Py_FatalError("unexpected exception during garbage collection");
} }
allocated = 0;
return n+m; return n+m;
} }
static long static long
collect_generations(void) collect_generations(void)
{ {
static long collections0 = 0; int i;
static long collections1 = 0;
long n = 0; long n = 0;
/* Find the oldest generation (higest numbered) where the count
if (collections1 > threshold2) { * exceeds the threshold. Objects in the that generation and
generation = 2; * generations younger than it will be collected. */
gc_list_merge(&_PyGC_generation0, &generation2); for (i = NUM_GENERATIONS-1; i >= 0; i--) {
gc_list_merge(&generation1, &generation2); if (generations[i].count > generations[i].threshold) {
if (generation2.gc.gc_next != &generation2) { n = collect(i);
n = collect(&generation2, &generation2); break;
}
collections1 = 0;
}
else if (collections0 > threshold1) {
generation = 1;
collections1++;
gc_list_merge(&_PyGC_generation0, &generation1);
if (generation1.gc.gc_next != &generation1) {
n = collect(&generation1, &generation2);
}
collections0 = 0;
}
else {
generation = 0;
collections0++;
if (_PyGC_generation0.gc.gc_next != &_PyGC_generation0) {
n = collect(&_PyGC_generation0, &generation1);
} }
} }
return n; return n;
...@@ -562,10 +577,7 @@ gc_collect(PyObject *self, PyObject *args) ...@@ -562,10 +577,7 @@ gc_collect(PyObject *self, PyObject *args)
} }
else { else {
collecting = 1; collecting = 1;
generation = 2; n = collect(NUM_GENERATIONS - 1);
gc_list_merge(&_PyGC_generation0, &generation2);
gc_list_merge(&generation1, &generation2);
n = collect(&generation2, &generation2);
collecting = 0; collecting = 0;
} }
...@@ -624,9 +636,16 @@ static char gc_set_thresh__doc__[] = ...@@ -624,9 +636,16 @@ static char gc_set_thresh__doc__[] =
static PyObject * static PyObject *
gc_set_thresh(PyObject *self, PyObject *args) gc_set_thresh(PyObject *self, PyObject *args)
{ {
if (!PyArg_ParseTuple(args, "i|ii:set_threshold", &threshold0, int i;
&threshold1, &threshold2)) if (!PyArg_ParseTuple(args, "i|ii:set_threshold",
&generations[0].threshold,
&generations[1].threshold,
&generations[2].threshold))
return NULL; return NULL;
for (i = 2; i < NUM_GENERATIONS; i++) {
/* generations higher than 2 get the same threshold */
generations[i].threshold = generations[2].threshold;
}
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
...@@ -644,7 +663,10 @@ gc_get_thresh(PyObject *self, PyObject *args) ...@@ -644,7 +663,10 @@ gc_get_thresh(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, ":get_threshold")) /* no args */ if (!PyArg_ParseTuple(args, ":get_threshold")) /* no args */
return NULL; return NULL;
return Py_BuildValue("(iii)", threshold0, threshold1, threshold2); return Py_BuildValue("(iii)",
generations[0].threshold,
generations[1].threshold,
generations[2].threshold);
} }
static int static int
...@@ -683,13 +705,14 @@ Return the list of objects that directly refer to any of objs."; ...@@ -683,13 +705,14 @@ Return the list of objects that directly refer to any of objs.";
static PyObject * static PyObject *
gc_get_referrers(PyObject *self, PyObject *args) gc_get_referrers(PyObject *self, PyObject *args)
{ {
int i;
PyObject *result = PyList_New(0); PyObject *result = PyList_New(0);
if (!(gc_referrers_for(args, &_PyGC_generation0, result) && for (i = 0; i < NUM_GENERATIONS; i++) {
gc_referrers_for(args, &generation1, result) && if (!(gc_referrers_for(args, GEN_HEAD(i), result))) {
gc_referrers_for(args, &generation2, result))) {
Py_DECREF(result); Py_DECREF(result);
return NULL; return NULL;
} }
}
return result; return result;
} }
...@@ -719,6 +742,7 @@ append_objects(PyObject *py_list, PyGC_Head *gc_list) ...@@ -719,6 +742,7 @@ append_objects(PyObject *py_list, PyGC_Head *gc_list)
static PyObject * static PyObject *
gc_get_objects(PyObject *self, PyObject *args) gc_get_objects(PyObject *self, PyObject *args)
{ {
int i;
PyObject* result; PyObject* result;
if (!PyArg_ParseTuple(args, ":get_objects")) /* check no args */ if (!PyArg_ParseTuple(args, ":get_objects")) /* check no args */
...@@ -727,12 +751,12 @@ gc_get_objects(PyObject *self, PyObject *args) ...@@ -727,12 +751,12 @@ gc_get_objects(PyObject *self, PyObject *args)
if (result == NULL) { if (result == NULL) {
return NULL; return NULL;
} }
if (append_objects(result, &_PyGC_generation0) || for (i = 0; i < NUM_GENERATIONS; i++) {
append_objects(result, &generation1) || if (append_objects(result, GEN_HEAD(i))) {
append_objects(result, &generation2)) {
Py_DECREF(result); Py_DECREF(result);
return NULL; return NULL;
} }
}
return result; return result;
} }
...@@ -854,10 +878,10 @@ _PyObject_GC_Malloc(size_t basicsize) ...@@ -854,10 +878,10 @@ _PyObject_GC_Malloc(size_t basicsize)
if (g == NULL) if (g == NULL)
return (PyObject *)PyErr_NoMemory(); return (PyObject *)PyErr_NoMemory();
g->gc.gc_next = NULL; g->gc.gc_next = NULL;
allocated++; generations[0].count++; /* number of allocated GC objects */
if (allocated > threshold0 && if (generations[0].count > generations[0].threshold &&
enabled && enabled &&
threshold0 && generations[0].threshold &&
!collecting && !collecting &&
!PyErr_Occurred()) { !PyErr_Occurred()) {
collecting = 1; collecting = 1;
...@@ -919,8 +943,8 @@ PyObject_GC_Del(void *op) ...@@ -919,8 +943,8 @@ PyObject_GC_Del(void *op)
PyGC_Head *g = AS_GC(op); PyGC_Head *g = AS_GC(op);
if (g->gc.gc_next != NULL) if (g->gc.gc_next != NULL)
gc_list_remove(g); gc_list_remove(g);
if (allocated > 0) { if (generations[0].count > 0) {
allocated--; generations[0].count--;
} }
PyObject_FREE(g); PyObject_FREE(g);
#else #else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment