diff --git a/Objects/floatobject.c b/Objects/floatobject.c --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -29,11 +29,18 @@ free_list is a singly-linked list of available PyFloatObjects, linked via abuse of their ob_type members. + + A range of common integral floats arount 0 is interned. Such floats + are surprisingly common in practice, particularly when working + with tabled data. */ #define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */ #define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */ #define N_FLOATOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyFloatObject)) +#define INTERN_LOW 0 /* lower end of range of floats to intern */ +#define INTERN_HIGH 2 /* upper end of range of floats to intern */ +#define N_INTERN (INTERN_HIGH - INTERN_LOW) struct _floatblock { struct _floatblock *next; @@ -44,6 +51,7 @@ static PyFloatBlock *block_list = NULL; static PyFloatObject *free_list = NULL; +static PyObject *f_intern[N_INTERN > 0 ? N_INTERN : 1] = {0}; static PyFloatObject * fill_free_list(void) @@ -148,6 +156,57 @@ return floatinfo; } +#if N_INTERN > 0 +Py_LOCAL_INLINE(int) +float_is_positive_zero(double fval) +{ +#ifdef MS_WINDOWS + return _fpclass(fval) == _FPCLASS_PZ; +#else +#if defined HAVE_LONG_LONG + /* perform a bitwise compare. positive zero is all bits 0 */ + if (sizeof(PY_LONG_LONG) == sizeof(double)) + return *(PY_LONG_LONG*)&fval == 0; +#endif + /* we can't tell, so assume all zeros are negative, disabling + * internment for them + */ + return 0; +#endif +} +#endif + +Py_LOCAL_INLINE(int) +float_can_intern(double fval, int *ival) +{ +#if INTERN_LOW == 0 && INTERN_HIGH == 2 + /* fast special case */ + if (fval == 0.0) { + if (float_is_positive_zero(fval)) { + *ival = 0; + return 1; + } else + return 0; + } + else if (fval == 1.0) { + *ival = 1; + return 1; + } + else + return 0; +#else + int i; + if (fval < (double)INTERN_LOW || fval >= (double)INTERN_HIGH) + return 0; + i = (int)fval; + if ((float)i == fval) { + *ival = i; + return 1; + } + return 0; +#endif +} + PyObject * PyFloat_FromDouble(double fval) { @@ -155,7 +214,28 @@ if (free_list == NULL) { if ((free_list = fill_free_list()) == NULL) return NULL; + if (!f_intern[0]) { + /* pre-intern floats */ + int i; + for(i = 0; i 0 + { + /* check integral value internment */ + int ival; + if (float_can_intern(fval, &ival)) { + ival -= INTERN_LOW; + if (f_intern[ival]) { + Py_INCREF(f_intern[ival]); + return f_intern[ival]; + } + } + } +#endif + /* Inline PyObject_New */ op = free_list; free_list = (PyFloatObject *)Py_TYPE(op); @@ -1938,6 +2018,11 @@ int u; /* remaining unfreed floats per block */ int freelist_size = 0; + for (i=0; i