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 🙂