Commit fd83f41a authored by Marius Wachtler's avatar Marius Wachtler

add tuple free list

this uses a slightly modified version of cpythons free list.
I removed the special handling for the empty tuple and fixed some C/C++ type mismatches.
parent 6042419c
......@@ -27,6 +27,11 @@
namespace pyston {
#if PyTuple_MAXSAVESIZE > 0
BoxedTuple* BoxedTuple::free_list[PyTuple_MAXSAVESIZE];
int BoxedTuple::numfree[PyTuple_MAXSAVESIZE];
#endif
extern "C" Box* createTuple(int64_t nelts, Box** elts) {
return BoxedTuple::create(nelts, elts);
}
......@@ -725,7 +730,22 @@ extern "C" void _PyTuple_MaybeUntrack(PyObject* op) noexcept {
}
extern "C" int PyTuple_ClearFreeList() noexcept {
return 0; // number of entries cleared
int freelist_size = 0;
#if PyTuple_MAXSAVESIZE > 0
int i;
for (i = 1; i < PyTuple_MAXSAVESIZE; i++) {
PyTupleObject* p = (PyTupleObject*)BoxedTuple::free_list[i];
freelist_size += BoxedTuple::numfree[i];
BoxedTuple::free_list[i] = NULL;
BoxedTuple::numfree[i] = 0;
while (p) {
PyTupleObject* q = p;
p = (PyTupleObject*)(p->ob_item[0]);
PyObject_GC_Del(q);
}
}
#endif
return freelist_size;
}
void setupTuple() {
......
......@@ -3780,7 +3780,7 @@ void HiddenClass::dump() noexcept {
}
}
static void tupledealloc(PyTupleObject* op) noexcept {
void BoxedTuple::dealloc(PyTupleObject* op) noexcept {
Py_ssize_t i;
Py_ssize_t len = Py_SIZE(op);
PyObject_GC_UnTrack(op);
......@@ -3789,10 +3789,11 @@ static void tupledealloc(PyTupleObject* op) noexcept {
while (--i >= 0)
Py_XDECREF(op->ob_item[i]);
#if PyTuple_MAXSAVESIZE > 0
if (len < PyTuple_MAXSAVESIZE && numfree[len] < PyTuple_MAXFREELIST && Py_TYPE(op) == &PyTuple_Type) {
if (likely(len < PyTuple_MAXSAVESIZE && BoxedTuple::numfree[len] < PyTuple_MAXFREELIST
&& ((BoxedTuple*)op)->cls == tuple_cls)) {
op->ob_item[0] = (PyObject*)free_list[len];
numfree[len]++;
free_list[len] = op;
free_list[len] = (BoxedTuple*)op;
goto done; /* return */
}
#endif
......@@ -4247,7 +4248,7 @@ void setupRuntime() {
// Not sure why CPython defines sizeof(PyTupleObject) to include one element,
// but we copy that, which means we have to subtract that extra pointer to get the tp_basicsize:
tuple_cls = new (0) BoxedClass(object_cls, 0, 0, sizeof(BoxedTuple) - sizeof(Box*), false, "tuple", true,
(destructor)tupledealloc, NULL, true, (traverseproc)tupletraverse, NOCLEAR);
(destructor)BoxedTuple::dealloc, NULL, true, (traverseproc)tupletraverse, NOCLEAR);
tuple_cls->tp_flags |= Py_TPFLAGS_TUPLE_SUBCLASS;
tuple_cls->tp_itemsize = sizeof(Box*);
......
......@@ -695,7 +695,16 @@ static_assert(offsetof(BoxedList, elts) == offsetof(PyListObject, ob_item), "");
static_assert(offsetof(GCdArray, elts) == 0, "");
static_assert(offsetof(BoxedList, capacity) == offsetof(PyListObject, allocated), "");
#define PyTuple_MAXSAVESIZE 20 /* Largest tuple to save on free list */
#define PyTuple_MAXFREELIST 2000 /* Maximum number of tuples of each size to save */
extern "C" int PyTuple_ClearFreeList() noexcept;
class BoxedTuple : public BoxVar {
private:
#if PyTuple_MAXSAVESIZE > 0
static BoxedTuple* free_list[PyTuple_MAXSAVESIZE];
static int numfree[PyTuple_MAXSAVESIZE];
#endif
public:
static BoxedTuple* create(int64_t size) {
if (size == 0) {
......@@ -834,7 +843,7 @@ public:
return BoxVar::operator new(size, cls, nitems);
}
void* operator new(size_t size, size_t nitems) __attribute__((visibility("default"))) {
void* operator new(size_t /*size*/, size_t nitems) __attribute__((visibility("default"))) {
ALLOC_STATS_VAR(tuple_cls)
assert(tuple_cls->tp_alloc == PyType_GenericAlloc);
......@@ -843,11 +852,31 @@ public:
assert(tuple_cls->is_pyston_class);
assert(tuple_cls->attrs_offset == 0);
BoxVar* rtn = static_cast<BoxVar*>(PyObject_GC_NewVar(BoxedTuple, &PyTuple_Type, nitems));
assert(rtn);
_PyObject_GC_TRACK(rtn);
return rtn;
BoxedTuple* op = NULL;
#if PyTuple_MAXSAVESIZE > 0
if (likely(nitems < PyTuple_MAXSAVESIZE && (op = free_list[nitems]) != NULL)) {
free_list[nitems] = (BoxedTuple*)op->elts[0];
numfree[nitems]--;
/* Inline PyObject_InitVar */
#ifdef Py_TRACE_REFS
Py_SIZE(op) = nitems;
Py_TYPE(op) = &PyTuple_Type;
#endif
_Py_NewReference((PyObject*)op);
} else
#endif
{
Py_ssize_t nbytes = nitems * sizeof(PyObject*);
/* Check for overflow */
if (unlikely(nbytes / sizeof(PyObject*) != (size_t)nitems
|| (nbytes > PY_SSIZE_T_MAX - sizeof(PyTupleObject) - sizeof(PyObject*)))) {
return PyErr_NoMemory();
}
op = PyObject_GC_NewVar(BoxedTuple, &PyTuple_Type, nitems);
}
_PyObject_GC_TRACK(op);
return (PyObject*)op;
}
private:
......@@ -871,6 +900,9 @@ public:
Box* elts[0];
Box* _elts[1];
};
static void dealloc(PyTupleObject* op) noexcept;
friend int PyTuple_ClearFreeList() noexcept;
};
static_assert(sizeof(BoxedTuple) == sizeof(PyTupleObject), "");
static_assert(offsetof(BoxedTuple, ob_size) == offsetof(PyTupleObject, ob_size), "");
......
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