From 826f3fefe5d86a5f479a256c535d7f31ce3f88ee Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Fri, 5 Dec 2008 21:55:28 +0000 Subject: [PATCH] Issue #4445: save 3 bytes (on average, on a typical machine) per string allocation. --- Lib/test/test_sys.py | 4 ++-- Misc/NEWS | 5 +++++ Objects/stringobject.c | 34 +++++++++++++++++++++------------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 35467e46f62..8e10ec6a4bb 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -631,8 +631,8 @@ class SizeofTest(unittest.TestCase): # slice check(slice(1), size(h + '3P')) # str - check('', size(vh + 'lic')) - check('abc', size(vh + 'lic') + 3*self.c) + check('', struct.calcsize(vh + 'li') + 1) + check('abc', struct.calcsize(vh + 'li') + 1 + 3*self.c) # super check(super(int), size(h + '3P')) # tuple diff --git a/Misc/NEWS b/Misc/NEWS index 5967deab202..1a70e3183b6 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,11 @@ What's New in Python 2.7 alpha 1 Core and Builtins ----------------- +- Issue #4445: Replace "sizeof(PyStringObject)" with + "offsetof(PyStringObject, ob_sval) + 1" when allocating memory for + str instances. On a typical machine this saves 3 bytes of memory + (on average) per string allocation. + - Issue #3996: On Windows, the PyOS_CheckStack function would cause the interpreter to abort ("Fatal Python error: Could not reset the stack!") instead of throwing a MemoryError. diff --git a/Objects/stringobject.c b/Objects/stringobject.c index 7f30f6663db..2c365fb8791 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -4,6 +4,7 @@ #include "Python.h" #include +#include #ifdef COUNT_ALLOCS int null_strings, one_strings; @@ -22,6 +23,14 @@ static PyStringObject *nullstring; */ static PyObject *interned; +/* PyStringObject_SIZE gives the basic size of a string; any memory allocation + for a string of length n should request PyStringObject_SIZE + n bytes. + + Using PyStringObject_SIZE instead of sizeof(PyStringObject) saves + 3 bytes per string allocation on a typical system. +*/ +#define PyStringObject_SIZE (offsetof(PyStringObject, ob_sval) + 1) + /* For both PyString_FromString() and PyString_FromStringAndSize(), the parameter `size' denotes number of characters to allocate, not counting any @@ -74,13 +83,13 @@ PyString_FromStringAndSize(const char *str, Py_ssize_t size) return (PyObject *)op; } - if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { + if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) { PyErr_SetString(PyExc_OverflowError, "string is too large"); return NULL; } /* Inline PyObject_NewVar */ - op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); + op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); @@ -114,7 +123,7 @@ PyString_FromString(const char *str) assert(str != NULL); size = strlen(str); - if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { + if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) { PyErr_SetString(PyExc_OverflowError, "string is too long for a Python string"); return NULL; @@ -135,7 +144,7 @@ PyString_FromString(const char *str) } /* Inline PyObject_NewVar */ - op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); + op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); @@ -992,14 +1001,14 @@ string_concat(register PyStringObject *a, register PyObject *bb) "strings are too large to concat"); return NULL; } - + /* Inline PyObject_NewVar */ - if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { + if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) { PyErr_SetString(PyExc_OverflowError, "strings are too large to concat"); return NULL; } - op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); + op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); @@ -1036,13 +1045,12 @@ string_repeat(register PyStringObject *a, register Py_ssize_t n) return (PyObject *)a; } nbytes = (size_t)size; - if (nbytes + sizeof(PyStringObject) <= nbytes) { + if (nbytes + PyStringObject_SIZE <= nbytes) { PyErr_SetString(PyExc_OverflowError, "repeated string is too long"); return NULL; } - op = (PyStringObject *) - PyObject_MALLOC(sizeof(PyStringObject) + nbytes); + op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + nbytes); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); @@ -3940,7 +3948,7 @@ static PyObject * string_sizeof(PyStringObject *v) { Py_ssize_t res; - res = sizeof(PyStringObject) + v->ob_size * v->ob_type->tp_itemsize; + res = PyStringObject_SIZE + v->ob_size * v->ob_type->tp_itemsize; return PyInt_FromSsize_t(res); } @@ -4179,7 +4187,7 @@ If the argument is a string, the return value is the same object."); PyTypeObject PyString_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "str", - sizeof(PyStringObject), + PyStringObject_SIZE, sizeof(char), string_dealloc, /* tp_dealloc */ (printfunc)string_print, /* tp_print */ @@ -4275,7 +4283,7 @@ _PyString_Resize(PyObject **pv, Py_ssize_t newsize) _Py_DEC_REFTOTAL; _Py_ForgetReference(v); *pv = (PyObject *) - PyObject_REALLOC((char *)v, sizeof(PyStringObject) + newsize); + PyObject_REALLOC((char *)v, PyStringObject_SIZE + newsize); if (*pv == NULL) { PyObject_Del(v); PyErr_NoMemory();