I thought I’d mention a cool little patch we did to Python some years back.
We work with database tables a lot. Game configuration data is essentially rows in a vast database. And those rows contain a lot of floats. At some point I recognized that common float values were not being reused. In particular, id(0.0) != id(0.0). I was a bit surprized by this, since I figured, some floats must be more common than others. Certainly, 0.0 is a bit special.
I mentioned this on python-dev some years back but with somewhat underwhelming results. A summary of the discussion can be found here.
Anyway, I thought I’d mention this to people doing a lot of floating point. We saved a huge amount of memory on our servers just caching integral floating point values between -10 and +10, including both the negative and positive 0.0. These values are very frequent, for example as multipliers in tables, and so on.
Here’s some of the code:
[C]
PyObject *
PyFloat_FromDouble(double fval)
{
register PyFloatObject *op;
int ival;
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
/* CCP addition, cache common values */
if (!f_reuse[0]) {
int i;
for(i = 0; i<21; i++)
f_reuse[i] = PyFloat_FromDouble((double)(i-10));
}
}
/* CCP addition, check for recycling */
ival = (int)fval;
if ((double)ival == fval && ival>=-10 && ival <= 10) {
#ifdef MS_WINDOWS
/* ignore the negative zero */
if (ival || _fpclass(fval) != _FPCLASS_NZ) {
#else
/* can't differentiate between positive and negative zeroes, ignore both */
if (ival) {
#endif
ival+=10;
if (f_reuse[ival]) {
Py_INCREF(f_reuse[ival]);
return f_reuse[ival];
}
}
}
/* Inline PyObject_New */
op = free_list;
free_list = (PyFloatObject *)Py_TYPE(op);
PyObject_INIT(op, &PyFloat_Type);
op->ob_fval = fval;
return (PyObject *) op;
}
[/C]
(Please excuse the lame syntax highlighter with its & and < thingies 🙂