Skip to content

Commit c91d71e

Browse files
author
Kristján Valur Jónsson
committed
Issue #14435: Remove special block allocation code from floatobject.c
PyFloatObjects are now allocated using PyObject_MALLOC like all other internal types, but maintain a limited freelist of objects at hand for performance. This will result in more consistent memory usage by Python.
1 parent 3d7de87 commit c91d71e

1 file changed

Lines changed: 27 additions & 131 deletions

File tree

Objects/floatobject.c

Lines changed: 27 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -16,53 +16,16 @@
1616

1717

1818
/* Special free list
19-
20-
Since some Python programs can spend much of their time allocating
21-
and deallocating floats, these operations should be very fast.
22-
Therefore we use a dedicated allocation scheme with a much lower
23-
overhead (in space and time) than straight malloc(): a simple
24-
dedicated free list, filled when necessary with memory from malloc().
25-
26-
block_list is a singly-linked list of all PyFloatBlocks ever allocated,
27-
linked via their next members. PyFloatBlocks are never returned to the
28-
system before shutdown (PyFloat_Fini).
29-
3019
free_list is a singly-linked list of available PyFloatObjects, linked
3120
via abuse of their ob_type members.
3221
*/
3322

34-
#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
35-
#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
36-
#define N_FLOATOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyFloatObject))
37-
38-
struct _floatblock {
39-
struct _floatblock *next;
40-
PyFloatObject objects[N_FLOATOBJECTS];
41-
};
42-
43-
typedef struct _floatblock PyFloatBlock;
44-
45-
static PyFloatBlock *block_list = NULL;
23+
#ifndef PyFloat_MAXFREELIST
24+
#define PyFloat_MAXFREELIST 100
25+
#endif
26+
static int numfree = 0;
4627
static PyFloatObject *free_list = NULL;
4728

48-
static PyFloatObject *
49-
fill_free_list(void)
50-
{
51-
PyFloatObject *p, *q;
52-
/* XXX Float blocks escape the object heap. Use PyObject_MALLOC ??? */
53-
p = (PyFloatObject *) PyMem_MALLOC(sizeof(PyFloatBlock));
54-
if (p == NULL)
55-
return (PyFloatObject *) PyErr_NoMemory();
56-
((PyFloatBlock *)p)->next = block_list;
57-
block_list = (PyFloatBlock *)p;
58-
p = &((PyFloatBlock *)p)->objects[0];
59-
q = p + N_FLOATOBJECTS;
60-
while (--q > p)
61-
Py_TYPE(q) = (struct _typeobject *)(q-1);
62-
Py_TYPE(q) = NULL;
63-
return p + N_FLOATOBJECTS - 1;
64-
}
65-
6629
double
6730
PyFloat_GetMax(void)
6831
{
@@ -151,14 +114,16 @@ PyFloat_GetInfo(void)
151114
PyObject *
152115
PyFloat_FromDouble(double fval)
153116
{
154-
register PyFloatObject *op;
155-
if (free_list == NULL) {
156-
if ((free_list = fill_free_list()) == NULL)
157-
return NULL;
117+
register PyFloatObject *op = free_list;
118+
if (op != NULL) {
119+
free_list = (PyFloatObject *) Py_TYPE(op);
120+
numfree--;
121+
} else {
122+
op = (PyFloatObject*) PyObject_MALLOC(sizeof(PyFloatObject));
123+
if (!op)
124+
return PyErr_NoMemory();
158125
}
159126
/* Inline PyObject_New */
160-
op = free_list;
161-
free_list = (PyFloatObject *)Py_TYPE(op);
162127
PyObject_INIT(op, &PyFloat_Type);
163128
op->ob_fval = fval;
164129
return (PyObject *) op;
@@ -217,6 +182,11 @@ static void
217182
float_dealloc(PyFloatObject *op)
218183
{
219184
if (PyFloat_CheckExact(op)) {
185+
if (numfree >= PyFloat_MAXFREELIST) {
186+
PyObject_FREE(op);
187+
return;
188+
}
189+
numfree++;
220190
Py_TYPE(op) = (struct _typeobject *)free_list;
221191
free_list = op;
222192
}
@@ -1932,96 +1902,22 @@ _PyFloat_Init(void)
19321902
int
19331903
PyFloat_ClearFreeList(void)
19341904
{
1935-
PyFloatObject *p;
1936-
PyFloatBlock *list, *next;
1937-
int i;
1938-
int u; /* remaining unfreed floats per block */
1939-
int freelist_size = 0;
1940-
1941-
list = block_list;
1942-
block_list = NULL;
1943-
free_list = NULL;
1944-
while (list != NULL) {
1945-
u = 0;
1946-
for (i = 0, p = &list->objects[0];
1947-
i < N_FLOATOBJECTS;
1948-
i++, p++) {
1949-
if (PyFloat_CheckExact(p) && Py_REFCNT(p) != 0)
1950-
u++;
1951-
}
1952-
next = list->next;
1953-
if (u) {
1954-
list->next = block_list;
1955-
block_list = list;
1956-
for (i = 0, p = &list->objects[0];
1957-
i < N_FLOATOBJECTS;
1958-
i++, p++) {
1959-
if (!PyFloat_CheckExact(p) ||
1960-
Py_REFCNT(p) == 0) {
1961-
Py_TYPE(p) = (struct _typeobject *)
1962-
free_list;
1963-
free_list = p;
1964-
}
1965-
}
1966-
}
1967-
else {
1968-
PyMem_FREE(list);
1969-
}
1970-
freelist_size += u;
1971-
list = next;
1905+
PyFloatObject *f = free_list, *next;
1906+
int i = numfree;
1907+
while (f) {
1908+
next = (PyFloatObject*) Py_TYPE(f);
1909+
PyObject_FREE(f);
1910+
f = next;
19721911
}
1973-
return freelist_size;
1912+
free_list = NULL;
1913+
numfree = 0;
1914+
return i;
19741915
}
19751916

19761917
void
19771918
PyFloat_Fini(void)
19781919
{
1979-
PyFloatObject *p;
1980-
PyFloatBlock *list;
1981-
int i;
1982-
int u; /* total unfreed floats per block */
1983-
1984-
u = PyFloat_ClearFreeList();
1985-
1986-
if (!Py_VerboseFlag)
1987-
return;
1988-
fprintf(stderr, "# cleanup floats");
1989-
if (!u) {
1990-
fprintf(stderr, "\n");
1991-
}
1992-
else {
1993-
fprintf(stderr,
1994-
": %d unfreed float%s\n",
1995-
u, u == 1 ? "" : "s");
1996-
}
1997-
if (Py_VerboseFlag > 1) {
1998-
list = block_list;
1999-
while (list != NULL) {
2000-
for (i = 0, p = &list->objects[0];
2001-
i < N_FLOATOBJECTS;
2002-
i++, p++) {
2003-
if (PyFloat_CheckExact(p) &&
2004-
Py_REFCNT(p) != 0) {
2005-
char *buf = PyOS_double_to_string(
2006-
PyFloat_AS_DOUBLE(p), 'r',
2007-
0, 0, NULL);
2008-
if (buf) {
2009-
/* XXX(twouters) cast
2010-
refcount to long
2011-
until %zd is
2012-
universally
2013-
available
2014-
*/
2015-
fprintf(stderr,
2016-
"# <float at %p, refcnt=%ld, val=%s>\n",
2017-
p, (long)Py_REFCNT(p), buf);
2018-
PyMem_Free(buf);
2019-
}
2020-
}
2021-
}
2022-
list = list->next;
2023-
}
2024-
}
1920+
(void)PyFloat_ClearFreeList();
20251921
}
20261922

20271923
/*----------------------------------------------------------------------------

0 commit comments

Comments
 (0)