Skip to content

Commit 83ebcf5

Browse files
committed
Avoid layers of indirection allocating and freeing strs and ints
1 parent c520bf9 commit 83ebcf5

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

Include/cpython/object.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,7 @@ PyAPI_FUNC(int) PyType_Unwatch(int watcher_id, PyObject *type);
506506
* assigned, or 0 if a new tag could not be assigned.
507507
*/
508508
PyAPI_FUNC(int) PyUnstable_Type_AssignVersionTag(PyTypeObject *type);
509+
510+
/* Streamlined allocate and free variants */
511+
void *_PyObject_MallocFast(size_t size);
512+
void _PyObject_FreeFast(void *ptr);

Objects/longobject.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ _PyLong_New(Py_ssize_t size)
154154
sizeof() instead of the offsetof, but this risks being
155155
incorrect in the presence of padding between the header
156156
and the digits. */
157-
result = PyObject_Malloc(offsetof(PyLongObject, long_value.ob_digit) +
157+
result = _PyObject_MallocFast(offsetof(PyLongObject, long_value.ob_digit) +
158158
ndigits*sizeof(digit));
159159
if (!result) {
160160
PyErr_NoMemory();
@@ -206,7 +206,7 @@ _PyLong_FromMedium(sdigit x)
206206
assert(!IS_SMALL_INT(x));
207207
assert(is_medium_int(x));
208208
/* We could use a freelist here */
209-
PyLongObject *v = PyObject_Malloc(sizeof(PyLongObject));
209+
PyLongObject *v = _PyObject_MallocFast(sizeof(PyLongObject));
210210
if (v == NULL) {
211211
PyErr_NoMemory();
212212
return NULL;
@@ -3553,7 +3553,13 @@ long_dealloc(PyObject *self)
35533553
}
35543554
}
35553555
}
3556-
Py_TYPE(self)->tp_free(self);
3556+
if (PyLong_CheckExact(self)) {
3557+
assert(Py_TYPE(self)->tp_free == PyObject_Free);
3558+
_PyObject_FreeFast(self);
3559+
}
3560+
else {
3561+
Py_TYPE(self)->tp_free(self);
3562+
}
35573563
}
35583564

35593565
static Py_hash_t

Objects/obmalloc.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3446,4 +3446,47 @@ _PyObject_DebugMallocStats(FILE *out)
34463446
}
34473447
}
34483448

3449+
void *
3450+
_PyObject_MallocFast(size_t size)
3451+
{
3452+
assert(size != 0);
3453+
OBJECT_STAT_INC_COND(allocations512, size < 512);
3454+
OBJECT_STAT_INC_COND(allocations4k, size >= 512 && size < 4094);
3455+
OBJECT_STAT_INC_COND(allocations_big, size >= 4094);
3456+
OBJECT_STAT_INC(allocations);
3457+
if (_PyObject.malloc == _PyObject_Malloc) {
3458+
OMState *state = get_state();
3459+
void* ptr = pymalloc_alloc(state, NULL, size);
3460+
if (LIKELY(ptr != NULL)) {
3461+
return ptr;
3462+
}
3463+
ptr = PyMem_RawMalloc(size);
3464+
if (ptr != NULL) {
3465+
raw_allocated_blocks++;
3466+
}
3467+
return ptr;
3468+
}
3469+
return _PyObject.malloc(_PyObject.ctx, size);
3470+
}
3471+
3472+
void
3473+
_PyObject_FreeFast(void *ptr)
3474+
{
3475+
/* PyObject_Free(NULL) has no effect */
3476+
assert(ptr != NULL);
3477+
OBJECT_STAT_INC(frees);
3478+
if (_PyObject.free == _PyObject_Free) {
3479+
OMState *state = get_state();
3480+
if (UNLIKELY(!pymalloc_free(state, NULL, ptr))) {
3481+
/* pymalloc didn't allocate this address */
3482+
PyMem_RawFree(ptr);
3483+
raw_allocated_blocks--;
3484+
}
3485+
}
3486+
else {
3487+
_PyObject.free(_PyObject.ctx, ptr);
3488+
}
3489+
}
3490+
3491+
34493492
#endif /* #ifdef WITH_PYMALLOC */

Objects/unicodeobject.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,7 @@ PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar)
12351235
* PyObject_New() so we are able to allocate space for the object and
12361236
* it's data buffer.
12371237
*/
1238-
obj = (PyObject *) PyObject_Malloc(struct_size + (size + 1) * char_size);
1238+
obj = (PyObject *) _PyObject_MallocFast(struct_size + (size + 1) * char_size);
12391239
if (obj == NULL) {
12401240
return PyErr_NoMemory();
12411241
}
@@ -1596,7 +1596,13 @@ unicode_dealloc(PyObject *unicode)
15961596
PyMem_Free(_PyUnicode_DATA_ANY(unicode));
15971597
}
15981598

1599-
Py_TYPE(unicode)->tp_free(unicode);
1599+
if (PyUnicode_CheckExact(unicode)) {
1600+
assert(Py_TYPE(unicode)->tp_free == PyObject_Free);
1601+
_PyObject_FreeFast(unicode);
1602+
}
1603+
else {
1604+
Py_TYPE(unicode)->tp_free(unicode);
1605+
}
16001606
}
16011607

16021608
#ifdef Py_DEBUG

0 commit comments

Comments
 (0)