Commit 7ea2c6f9 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Inline things into _PyUnicode_New

Inlining the allocation + object initialization saves
a decent amount of overhead, since most of the properties
will be fixed.  For example, the size of the main allocation is
fixed, so we can directly allocate it from the correct SmallArena
bucket.  We can also skip all the indirect function calls.
parent a60a1b9a
......@@ -101,7 +101,7 @@ static PyUnicodeObject *free_list = NULL;
static int numfree = 0;
/* The empty Unicode object is shared to improve performance. */
static PyUnicodeObject *unicode_empty = NULL;
PyUnicodeObject *unicode_empty = NULL;
#define _Py_RETURN_UNICODE_EMPTY() \
do { \
......@@ -317,76 +317,7 @@ int unicode_resize(register PyUnicodeObject *unicode,
*/
static
PyUnicodeObject *_PyUnicode_New(Py_ssize_t length)
{
register PyUnicodeObject *unicode;
/* Optimization for empty strings */
if (length == 0 && unicode_empty != NULL) {
Py_INCREF(unicode_empty);
return unicode_empty;
}
/* Ensure we won't overflow the size. */
if (length > ((PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) - 1)) {
return (PyUnicodeObject *)PyErr_NoMemory();
}
/* Unicode freelist & memory allocation */
if (free_list) {
unicode = free_list;
free_list = *(PyUnicodeObject **)unicode;
numfree--;
if (unicode->str) {
/* Keep-Alive optimization: we only upsize the buffer,
never downsize it. */
if ((unicode->length < length) &&
unicode_resize(unicode, length) < 0) {
PyObject_DEL(unicode->str);
unicode->str = NULL;
}
}
else {
size_t new_size = sizeof(Py_UNICODE) * ((size_t)length + 1);
unicode->str = (Py_UNICODE*) PyObject_MALLOC(new_size);
}
PyObject_INIT(unicode, &PyUnicode_Type);
}
else {
size_t new_size;
unicode = PyObject_New(PyUnicodeObject, &PyUnicode_Type);
if (unicode == NULL)
return NULL;
new_size = sizeof(Py_UNICODE) * ((size_t)length + 1);
unicode->str = (Py_UNICODE*) PyObject_MALLOC(new_size);
}
if (!unicode->str) {
PyErr_NoMemory();
goto onError;
}
/* Initialize the first element to guard against cases where
* the caller fails before initializing str -- unicode_resize()
* reads str[0], and the Keep-Alive optimization can keep memory
* allocated for str alive across a call to unicode_dealloc(unicode).
* We don't want unicode_resize to read uninitialized memory in
* that case.
*/
unicode->str[0] = 0;
unicode->str[length] = 0;
unicode->length = length;
unicode->hash = -1;
unicode->defenc = NULL;
return unicode;
onError:
/* XXX UNREF/NEWREF interface should be more symmetrical */
_Py_DEC_REFTOTAL;
_Py_ForgetReference((PyObject *)unicode);
PyObject_Del(unicode);
return NULL;
}
extern PyUnicodeObject *_PyUnicode_New(Py_ssize_t length);
static
void unicode_dealloc(register PyUnicodeObject *unicode)
......
import re
def f():
r = re.compile(" ")
u = "a b c d"
for i in xrange(2000000):
r.finditer(u)
f()
def f():
u = "a b c d"
u2 = u" "
for i in xrange(4000000):
u.split(u2)
f()
......@@ -2850,6 +2850,63 @@ out:
return result;
}
extern "C" PyUnicodeObject* unicode_empty;
extern "C" PyUnicodeObject* _PyUnicode_New(Py_ssize_t length) noexcept {
PyUnicodeObject* unicode;
/* Optimization for empty strings */
if (length == 0 && unicode_empty != NULL) {
Py_INCREF(unicode_empty);
return unicode_empty;
}
/* Ensure we won't overflow the size. */
if (length > ((PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) - 1)) {
return (PyUnicodeObject*)PyErr_NoMemory();
}
// Do a bunch of inlining + constant folding of this line of CPython's:
// unicode = PyObject_New(PyUnicodeObject, &PyUnicode_Type);
assert(PyUnicode_Type.tp_basicsize == sizeof(PyUnicodeObject)); // use the compile-time constant
unicode = (PyUnicodeObject*)gc_alloc(sizeof(PyUnicodeObject), gc::GCKind::CONSERVATIVE_PYTHON);
if (unicode == NULL)
return (PyUnicodeObject*)PyErr_NoMemory();
// Inline PyObject_INIT:
assert(!PyType_SUPPORTS_WEAKREFS(&PyUnicode_Type));
assert(!PyUnicode_Type.instancesHaveHCAttrs());
assert(!PyUnicode_Type.instancesHaveDictAttrs());
unicode->ob_type = (struct _typeobject*)&PyUnicode_Type;
size_t new_size = sizeof(Py_UNICODE) * ((size_t)length + 1);
unicode->str = (Py_UNICODE*)PyMem_MALLOC(new_size); // why is this faster than gc_compat_malloc or gc_alloc??
if (!unicode->str) {
PyErr_NoMemory();
goto onError;
}
/* Initialize the first element to guard against cases where
* the caller fails before initializing str -- unicode_resize()
* reads str[0], and the Keep-Alive optimization can keep memory
* allocated for str alive across a call to unicode_dealloc(unicode).
* We don't want unicode_resize to read uninitialized memory in
* that case.
*/
unicode->str[0] = 0;
unicode->str[length] = 0;
unicode->length = length;
unicode->hash = -1;
unicode->defenc = NULL;
return unicode;
onError:
/* XXX UNREF/NEWREF interface should be more symmetrical */
_Py_DEC_REFTOTAL;
_Py_ForgetReference((PyObject*)unicode);
PyObject_Del(unicode);
return NULL;
}
bool TRACK_ALLOCATIONS = false;
void setupRuntime() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment