diff -r e34ed02738bf Doc/c-api/long.rst --- a/Doc/c-api/long.rst Mon May 11 12:11:40 2015 -0400 +++ b/Doc/c-api/long.rst Tue May 12 00:18:53 2015 +0300 @@ -260,3 +260,10 @@ All integers are implemented as "long" i If *pylong* cannot be converted, an :exc:`OverflowError` will be raised. This is only assured to produce a usable :c:type:`void` pointer for values created with :c:func:`PyLong_FromVoidPtr`. + +.. c:function:: int PyLong_ClearFreeList() + + Clear the integer free list. Return the number of items that could not + be freed. + + .. versionadded:: 3.5 diff -r e34ed02738bf Include/longobject.h --- a/Include/longobject.h Mon May 11 12:11:40 2015 -0400 +++ b/Include/longobject.h Tue May 12 00:18:53 2015 +0300 @@ -198,6 +198,13 @@ PyAPI_FUNC(int) _PyLong_FormatAdvancedWr PyAPI_FUNC(unsigned long) PyOS_strtoul(const char *, char **, int); PyAPI_FUNC(long) PyOS_strtol(const char *, char **, int); +/* free list api */ +PyAPI_FUNC(int) PyLong_ClearFreeList(void); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(void) _PyLong_DebugMallocStats(FILE* out); +#endif + #ifdef __cplusplus } #endif diff -r e34ed02738bf Modules/gcmodule.c --- a/Modules/gcmodule.c Mon May 11 12:11:40 2015 -0400 +++ b/Modules/gcmodule.c Tue May 12 00:18:53 2015 +0300 @@ -892,6 +892,7 @@ clear_freelists(void) (void)PyList_ClearFreeList(); (void)PyDict_ClearFreeList(); (void)PySet_ClearFreeList(); + (void)PyLong_ClearFreeList(); } /* This is the main function. Read this to understand how the diff -r e34ed02738bf Objects/longobject.c --- a/Objects/longobject.c Mon May 11 12:11:40 2015 -0400 +++ b/Objects/longobject.c Tue May 12 00:18:53 2015 +0300 @@ -70,6 +70,22 @@ maybe_small_long(PyLongObject *v) #define maybe_small_long(val) (val) #endif +/* Speed optimization to avoid frequent malloc/free of small ints */ +#ifndef PyLong_MAXSAVESIZE +# define PyLong_MAXSAVESIZE (32 / PyLong_SHIFT + 2) /* Largest int to save on free list */ +#endif +#ifndef PyLong_MAXFREELIST +# define PyLong_MAXFREELIST 100 /* Maximum number of ints of each size to save */ +#endif + +#if PyLong_MAXSAVESIZE > 0 +static PyLongObject *free_list[PyLong_MAXSAVESIZE] = {NULL}; +static int numfree[PyLong_MAXSAVESIZE] = {0}; +#endif +#ifdef COUNT_ALLOCS +Py_ssize_t fast_int_allocs; +#endif + /* If a freshly-allocated int is already shared, it must be a small integer, so negating it must go to PyLong_FromLong */ Py_LOCAL_INLINE(void) @@ -183,21 +199,33 @@ PyLongObject * _PyLong_New(Py_ssize_t size) { PyLongObject *result; - /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + - sizeof(digit)*size. Previous incarnations of this code used - sizeof(PyVarObject) instead of the offsetof, but this risks being - incorrect in the presence of padding between the PyVarObject header - and the digits. */ - if (size > (Py_ssize_t)MAX_LONG_DIGITS) { - PyErr_SetString(PyExc_OverflowError, - "too many digits in integer"); - return NULL; - } - result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) + - size*sizeof(digit)); - if (!result) { - PyErr_NoMemory(); - return NULL; +#if PyLong_MAXSAVESIZE > 0 + if (size < PyLong_MAXSAVESIZE && (result = free_list[size]) != NULL) { + free_list[size] = (PyLongObject *)Py_TYPE(result); + numfree[size]--; +#ifdef COUNT_ALLOCS + fast_int_allocs++; +#endif + } + else +#endif + { + /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + + sizeof(digit)*size. Previous incarnations of this code used + sizeof(PyVarObject) instead of the offsetof, but this risks being + incorrect in the presence of padding between the PyVarObject header + and the digits. */ + if (size > (Py_ssize_t)MAX_LONG_DIGITS) { + PyErr_SetString(PyExc_OverflowError, + "too many digits in integer"); + return NULL; + } + result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) + + size*sizeof(digit)); + if (!result) { + PyErr_NoMemory(); + return NULL; + } } return (PyLongObject*)PyObject_INIT_VAR(result, &PyLong_Type, size); } @@ -249,7 +277,7 @@ PyLong_FromLong(long ival) } /* Fast path for single-digit ints */ - if (!(abs_ival >> PyLong_SHIFT)) { + if (abs_ival < (1UL << PyLong_SHIFT)) { v = _PyLong_New(1); if (v) { Py_SIZE(v) = sign; @@ -259,9 +287,9 @@ PyLong_FromLong(long ival) return (PyObject*)v; } -#if PyLong_SHIFT==15 +#if 2*PyLong_SHIFT < 8*SIZEOF_LONG /* 2 digits */ - if (!(abs_ival >> 2*PyLong_SHIFT)) { + if (abs_ival < (1UL << 2*PyLong_SHIFT)) { v = _PyLong_New(2); if (v) { Py_SIZE(v) = 2*sign; @@ -2719,6 +2747,19 @@ PyLong_AsDouble(PyObject *v) static void long_dealloc(PyObject *v) { +#if PyLong_MAXSAVESIZE > 0 + if (PyLong_CheckExact(v)) { + Py_ssize_t size = Py_SIZE(v); + size = Py_ABS(size); + if (size < PyLong_MAXSAVESIZE && + numfree[size] < PyLong_MAXFREELIST) { + Py_TYPE(v) = (PyTypeObject *)free_list[size]; + free_list[size] = (PyLongObject *)v; + numfree[size]++; + return; + } + } +#endif Py_TYPE(v)->tp_free(v); } @@ -5113,15 +5154,56 @@ int void PyLong_Fini(void) { + (void)PyLong_ClearFreeList(); /* Integers are currently statically allocated. Py_DECREF is not needed, but Python must forget about the reference or multiple reinitializations will fail. */ #if NSMALLNEGINTS + NSMALLPOSINTS > 0 - int i; - PyLongObject *v = small_ints; - for (i = 0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++, v++) { - _Py_DEC_REFTOTAL; - _Py_ForgetReference((PyObject*)v); + { + int i; + PyLongObject *v = small_ints; + for (i = 0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++, v++) { + _Py_DEC_REFTOTAL; + _Py_ForgetReference((PyObject*)v); + } } #endif } + +int +PyLong_ClearFreeList(void) +{ + int freelist_size = 0; +#if PyLong_MAXSAVESIZE > 0 + int size; + for (size = 0; size < PyLong_MAXSAVESIZE; size++) { + PyLongObject *f = free_list[size], *next; + freelist_size += numfree[size]; + free_list[size] = NULL; + numfree[size] = 0; + while (f) { + next = (PyLongObject*) Py_TYPE(f); + PyObject_FREE(f); + f = next; + } + } +#endif + return freelist_size; +} + +/* Print summary info about the state of the optimized allocator */ +void +_PyLong_DebugMallocStats(FILE *out) +{ +#if PyLong_MAXSAVESIZE > 0 + int i; + char buf[128]; + for (i = 0; i < PyLong_MAXSAVESIZE; i++) { + PyOS_snprintf(buf, sizeof(buf), + "free %d-sized PyLongObject", i); + _PyDebugAllocatorStats(out, + buf, + numfree[i], _PyObject_VAR_SIZE(&PyLong_Type, i)); + } +#endif +} diff -r e34ed02738bf Objects/object.c --- a/Objects/object.c Mon May 11 12:11:40 2015 -0400 +++ b/Objects/object.c Tue May 12 00:18:53 2015 +0300 @@ -103,7 +103,7 @@ static PyTypeObject *type_list; once the last object is deallocated. */ static int unlist_types_without_objects; extern Py_ssize_t tuple_zero_allocs, fast_tuple_allocs; -extern Py_ssize_t quick_int_allocs, quick_neg_int_allocs; +extern Py_ssize_t quick_int_allocs, quick_neg_int_allocs, fast_int_allocs; extern Py_ssize_t null_strings, one_strings; void dump_counts(FILE* f) @@ -119,9 +119,10 @@ dump_counts(FILE* f) fprintf(f, "fast tuple allocs: %" PY_FORMAT_SIZE_T "d, " "empty: %" PY_FORMAT_SIZE_T "d\n", fast_tuple_allocs, tuple_zero_allocs); - fprintf(f, "fast int allocs: pos: %" PY_FORMAT_SIZE_T "d, " - "neg: %" PY_FORMAT_SIZE_T "d\n", - quick_int_allocs, quick_neg_int_allocs); + fprintf(f, "fast int allocs: %" PY_FORMAT_SIZE_T "d, " + "small pos: %" PY_FORMAT_SIZE_T "d, " + "small neg: %" PY_FORMAT_SIZE_T "d\n", + fast_int_allocs, quick_int_allocs, quick_neg_int_allocs); fprintf(f, "null strings: %" PY_FORMAT_SIZE_T "d, " "1-strings: %" PY_FORMAT_SIZE_T "d\n", null_strings, one_strings); @@ -1838,6 +1839,7 @@ void _PyFloat_DebugMallocStats(out); _PyFrame_DebugMallocStats(out); _PyList_DebugMallocStats(out); + _PyLong_DebugMallocStats(out); _PyMethod_DebugMallocStats(out); _PyTuple_DebugMallocStats(out); }