]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #4445: save 3 bytes (on average, on a typical machine) per
authorMark Dickinson <dickinsm@gmail.com>
Fri, 5 Dec 2008 21:55:28 +0000 (21:55 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Fri, 5 Dec 2008 21:55:28 +0000 (21:55 +0000)
string allocation.

Lib/test/test_sys.py
Misc/NEWS
Objects/stringobject.c

index 35467e46f62905e75404b65f2f652fa97d5f47c6..8e10ec6a4bb5627338dc39ee45f6c9403395f6cd 100644 (file)
@@ -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
index 5967deab202991ba18afe8b57e403f6a9985e60c..1a70e3183b66efb3109dcbe6cb71dd032fd619e2 100644 (file)
--- 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.
index 7f30f6663db2a82cc69fe94677a70468cf03b034..2c365fb8791c73bfb03b7190d55a25f207d1b522 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "Python.h"
 #include <ctype.h>
+#include <stddef.h>
 
 #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();