Issue #4445: save 3 bytes (on average, on a typical machine) per

string allocation.
This commit is contained in:
Mark Dickinson 2008-12-05 21:55:28 +00:00
parent 205e719fac
commit 826f3fefe5
3 changed files with 28 additions and 15 deletions

View File

@ -631,8 +631,8 @@ class SizeofTest(unittest.TestCase):
# slice # slice
check(slice(1), size(h + '3P')) check(slice(1), size(h + '3P'))
# str # str
check('', size(vh + 'lic')) check('', struct.calcsize(vh + 'li') + 1)
check('abc', size(vh + 'lic') + 3*self.c) check('abc', struct.calcsize(vh + 'li') + 1 + 3*self.c)
# super # super
check(super(int), size(h + '3P')) check(super(int), size(h + '3P'))
# tuple # tuple

View File

@ -12,6 +12,11 @@ What's New in Python 2.7 alpha 1
Core and Builtins 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 - Issue #3996: On Windows, the PyOS_CheckStack function would cause the
interpreter to abort ("Fatal Python error: Could not reset the stack!") interpreter to abort ("Fatal Python error: Could not reset the stack!")
instead of throwing a MemoryError. instead of throwing a MemoryError.

View File

@ -4,6 +4,7 @@
#include "Python.h" #include "Python.h"
#include <ctype.h> #include <ctype.h>
#include <stddef.h>
#ifdef COUNT_ALLOCS #ifdef COUNT_ALLOCS
int null_strings, one_strings; int null_strings, one_strings;
@ -22,6 +23,14 @@ static PyStringObject *nullstring;
*/ */
static PyObject *interned; 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 For both PyString_FromString() and PyString_FromStringAndSize(), the
parameter `size' denotes number of characters to allocate, not counting any 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; 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"); PyErr_SetString(PyExc_OverflowError, "string is too large");
return NULL; return NULL;
} }
/* Inline PyObject_NewVar */ /* Inline PyObject_NewVar */
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
if (op == NULL) if (op == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();
PyObject_INIT_VAR(op, &PyString_Type, size); PyObject_INIT_VAR(op, &PyString_Type, size);
@ -114,7 +123,7 @@ PyString_FromString(const char *str)
assert(str != NULL); assert(str != NULL);
size = strlen(str); size = strlen(str);
if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"string is too long for a Python string"); "string is too long for a Python string");
return NULL; return NULL;
@ -135,7 +144,7 @@ PyString_FromString(const char *str)
} }
/* Inline PyObject_NewVar */ /* Inline PyObject_NewVar */
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
if (op == NULL) if (op == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();
PyObject_INIT_VAR(op, &PyString_Type, size); 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"); "strings are too large to concat");
return NULL; return NULL;
} }
/* Inline PyObject_NewVar */ /* Inline PyObject_NewVar */
if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"strings are too large to concat"); "strings are too large to concat");
return NULL; return NULL;
} }
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
if (op == NULL) if (op == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();
PyObject_INIT_VAR(op, &PyString_Type, size); PyObject_INIT_VAR(op, &PyString_Type, size);
@ -1036,13 +1045,12 @@ string_repeat(register PyStringObject *a, register Py_ssize_t n)
return (PyObject *)a; return (PyObject *)a;
} }
nbytes = (size_t)size; nbytes = (size_t)size;
if (nbytes + sizeof(PyStringObject) <= nbytes) { if (nbytes + PyStringObject_SIZE <= nbytes) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"repeated string is too long"); "repeated string is too long");
return NULL; return NULL;
} }
op = (PyStringObject *) op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + nbytes);
PyObject_MALLOC(sizeof(PyStringObject) + nbytes);
if (op == NULL) if (op == NULL)
return PyErr_NoMemory(); return PyErr_NoMemory();
PyObject_INIT_VAR(op, &PyString_Type, size); PyObject_INIT_VAR(op, &PyString_Type, size);
@ -3940,7 +3948,7 @@ static PyObject *
string_sizeof(PyStringObject *v) string_sizeof(PyStringObject *v)
{ {
Py_ssize_t res; 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); 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 = { PyTypeObject PyString_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) PyVarObject_HEAD_INIT(&PyType_Type, 0)
"str", "str",
sizeof(PyStringObject), PyStringObject_SIZE,
sizeof(char), sizeof(char),
string_dealloc, /* tp_dealloc */ string_dealloc, /* tp_dealloc */
(printfunc)string_print, /* tp_print */ (printfunc)string_print, /* tp_print */
@ -4275,7 +4283,7 @@ _PyString_Resize(PyObject **pv, Py_ssize_t newsize)
_Py_DEC_REFTOTAL; _Py_DEC_REFTOTAL;
_Py_ForgetReference(v); _Py_ForgetReference(v);
*pv = (PyObject *) *pv = (PyObject *)
PyObject_REALLOC((char *)v, sizeof(PyStringObject) + newsize); PyObject_REALLOC((char *)v, PyStringObject_SIZE + newsize);
if (*pv == NULL) { if (*pv == NULL) {
PyObject_Del(v); PyObject_Del(v);
PyErr_NoMemory(); PyErr_NoMemory();