Author: Antonio Cuni <[email protected]>
Branch: cpyext-avoid-roundtrip
Changeset: r92686:e5c7b7f85187
Date: 2017-10-09 18:26 +0200
http://bitbucket.org/pypy/pypy/changeset/e5c7b7f85187/
Log: (antocuni, arigo): implement PyInt_FromLong in C and use freelists
for allocation, copying the code from CPython; plus, some smaller
changes here and there which are needed to implement this.
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -628,7 +628,7 @@
'_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc',
'Py_IncRef', 'Py_DecRef', 'PyObject_Free', 'PyObject_GC_Del',
'PyType_GenericAlloc',
'_PyObject_New', '_PyObject_NewVar', '_PyObject_GC_New',
- 'PyObject_Init', 'PyObject_InitVar',
+ 'PyObject_Init', 'PyObject_InitVar', 'PyInt_FromLong',
]
TYPES = {}
FORWARD_DECLS = []
@@ -1120,9 +1120,17 @@
[PyTypeObjectPtr, Py_ssize_t], PyObject,
compilation_info=eci,
_nowrapper=True)
+ state.C.PyInt_FromLong = rffi.llexternal(
+ mangle_name(prefix, 'PyInt_FromLong'),
+ [rffi.LONG], PyObject,
+ compilation_info=eci,
+ _nowrapper=True)
_, state.C.set_marker = rffi.CExternVariable(
Py_ssize_t, '_pypy_rawrefcount_w_marker_deallocating',
eci, _nowrapper=True, c_type='Py_ssize_t')
+ state.C._PyPy_int_dealloc = rffi.llexternal(
+ '_PyPy_int_dealloc', [PyObject], lltype.Void,
+ compilation_info=eci, _nowrapper=True)
state.C._PyPy_subtype_dealloc = rffi.llexternal(
'_PyPy_subtype_dealloc', [PyObject], lltype.Void,
compilation_info=eci, _nowrapper=True)
@@ -1445,6 +1453,7 @@
source_dir / "pymem.c",
source_dir / "object.c",
source_dir / "typeobject.c",
+ source_dir / "intobject.c",
]
def build_eci(code, use_micronumpy=False, translating=False):
diff --git a/pypy/module/cpyext/include/intobject.h
b/pypy/module/cpyext/include/intobject.h
--- a/pypy/module/cpyext/include/intobject.h
+++ b/pypy/module/cpyext/include/intobject.h
@@ -16,6 +16,9 @@
PyType_FastSubclass((op)->ob_type, Py_TPFLAGS_INT_SUBCLASS)
#define PyInt_CheckExact(op) ((op)->ob_type == &PyInt_Type)
+PyAPI_FUNC(PyObject *) PyInt_FromLong(long);
+PyAPI_FUNC(void) _PyPy_int_dealloc(PyObject *);
+
#ifdef __cplusplus
}
#endif
diff --git a/pypy/module/cpyext/intobject.py b/pypy/module/cpyext/intobject.py
--- a/pypy/module/cpyext/intobject.py
+++ b/pypy/module/cpyext/intobject.py
@@ -5,7 +5,8 @@
cpython_api, cpython_struct, build_type_checkers_flags, bootstrap_function,
PyObject, PyObjectFields, CONST_STRING, CANNOT_FAIL, Py_ssize_t)
from pypy.module.cpyext.pyobject import (
- make_typedescr, track_reference, from_ref)
+ make_typedescr, track_reference, from_ref, BaseCpyTypedescr)
+from pypy.module.cpyext.state import State
from rpython.rlib.rarithmetic import r_uint, intmask, LONG_TEST, r_ulonglong
from pypy.objspace.std.intobject import W_IntObject
import sys
@@ -19,11 +20,26 @@
@bootstrap_function
def init_intobject(space):
"Type description of PyIntObject"
+ state = space.fromcache(State)
make_typedescr(space.w_int.layout.typedef,
basestruct=PyIntObject.TO,
attach=int_attach,
+ alloc=int_alloc,
+ dealloc=state.C._PyPy_int_dealloc,
realize=int_realize)
+def int_alloc(typedescr, space, w_type, itemcount):
+ state = space.fromcache(State)
+ if w_type is space.w_int:
+ # in theory here we just want to allocate, without initializing the
+ # value. However, it's just easier to call PyInt_FromLong with a dummy
+ # value; make sure it's big enough to avoid the smallint optimization
+ # (if it will ever be enabled)
+ return state.C.PyInt_FromLong(0xDEADBEEF)
+ else:
+ return BaseCpyTypedescr.allocate(typedescr, space, w_type, itemcount)
+
+# CCC kill this?
def int_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyIntObject with the given int object. The
@@ -32,6 +48,7 @@
py_int = rffi.cast(PyIntObject, py_obj)
py_int.c_ob_ival = space.int_w(w_obj)
+# CCC kill this?
def int_realize(space, obj):
intval = rffi.cast(lltype.Signed, rffi.cast(PyIntObject, obj).c_ob_ival)
w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
@@ -48,12 +65,6 @@
as defined in the system header files)."""
return sys.maxint
-@cpython_api([lltype.Signed], PyObject)
-def PyInt_FromLong(space, ival):
- """Create a new integer object with a value of ival.
-
- """
- return space.newint(ival)
@cpython_api([PyObject], lltype.Signed, error=-1)
def PyInt_AsLong(space, w_obj):
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -106,7 +106,7 @@
if tp_alloc:
def allocate(self, space, w_type, itemcount=0, immortal=False):
- return tp_alloc(space, w_type, itemcount)
+ return tp_alloc(self, space, w_type, itemcount)
if hasattr(tp_dealloc, 'api_func'):
def get_dealloc(self, space):
diff --git a/pypy/module/cpyext/src/intobject.c
b/pypy/module/cpyext/src/intobject.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/src/intobject.c
@@ -0,0 +1,108 @@
+
+/* Integer object implementation -- copied&adapted from CPython */
+
+#include "Python.h"
+
+/* Integers are quite normal objects, to make object handling uniform.
+ (Using odd pointers to represent integers would save much space
+ but require extra checks for this special case throughout the code.)
+ Since a typical Python program spends much of its time allocating
+ and deallocating integers, these operations should be very fast.
+ Therefore we use a dedicated allocation scheme with a much lower
+ overhead (in space and time) than straight malloc(): a simple
+ dedicated free list, filled when necessary with memory from malloc().
+
+ block_list is a singly-linked list of all PyIntBlocks ever allocated,
+ linked via their next members. PyIntBlocks are never returned to the
+ system before shutdown (PyInt_Fini).
+
+ free_list is a singly-linked list of available PyIntObjects, linked
+ via abuse of their ob_type members.
+*/
+
+#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
+#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
+#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
+
+struct _intblock {
+ struct _intblock *next;
+ PyIntObject objects[N_INTOBJECTS];
+};
+
+typedef struct _intblock PyIntBlock;
+
+static PyIntBlock *block_list = NULL;
+static PyIntObject *free_list = NULL;
+
+static PyIntObject *
+fill_free_list(void)
+{
+ PyIntObject *p, *q;
+ /* Python's object allocator isn't appropriate for large blocks. */
+ p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
+ if (p == NULL)
+ return (PyIntObject *) PyErr_NoMemory();
+ ((PyIntBlock *)p)->next = block_list;
+ block_list = (PyIntBlock *)p;
+ /* Link the int objects together, from rear to front, then return
+ the address of the last int object in the block. */
+ p = &((PyIntBlock *)p)->objects[0];
+ q = p + N_INTOBJECTS;
+ while (--q > p)
+ Py_TYPE(q) = (struct _typeobject *)(q-1);
+ Py_TYPE(q) = NULL;
+ return p + N_INTOBJECTS - 1;
+}
+
+#ifndef NSMALLPOSINTS
+#define NSMALLPOSINTS 257
+#endif
+#ifndef NSMALLNEGINTS
+#define NSMALLNEGINTS 5
+#endif
+#if NSMALLNEGINTS + NSMALLPOSINTS > 0
+/* References to small integers are saved in this array so that they
+ can be shared.
+ The integers that are saved are those in the range
+ -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
+*/
+static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
+#endif
+
+PyObject *
+PyInt_FromLong(long ival)
+{
+ register PyIntObject *v;
+ /*
+#if NSMALLNEGINTS + NSMALLPOSINTS > 0
+ if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
+ v = small_ints[ival + NSMALLNEGINTS];
+ Py_INCREF(v);
+ return (PyObject *) v;
+ }
+#endif
+ */
+ if (free_list == NULL) {
+ if ((free_list = fill_free_list()) == NULL)
+ return NULL;
+ }
+ /* Inline PyObject_New */
+ v = free_list;
+ free_list = (PyIntObject *)Py_TYPE(v);
+ (void)PyObject_INIT(v, &PyInt_Type);
+ v->ob_ival = ival;
+ return (PyObject *) v;
+}
+
+/* this is CPython's int_dealloc */
+void
+_PyPy_int_dealloc(PyObject *obj)
+{
+ PyIntObject *v = (PyIntObject *)obj;
+ if (PyInt_CheckExact(v)) {
+ Py_TYPE(v) = (struct _typeobject *)free_list;
+ free_list = v;
+ }
+ else
+ Py_TYPE(v)->tp_free((PyObject *)v);
+}
diff --git a/pypy/module/cpyext/test/test_intobject.py
b/pypy/module/cpyext/test/test_intobject.py
--- a/pypy/module/cpyext/test/test_intobject.py
+++ b/pypy/module/cpyext/test/test_intobject.py
@@ -1,13 +1,17 @@
from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.module.cpyext.intobject import (
- PyInt_Check, PyInt_AsLong, PyInt_AS_LONG, PyInt_FromLong,
+ PyInt_Check, PyInt_AsLong, PyInt_AS_LONG,
PyInt_AsUnsignedLong, PyInt_AsUnsignedLongMask,
PyInt_AsUnsignedLongLongMask)
+from pypy.module.cpyext.pyobject import (decref, make_ref,
+ get_w_obj_and_decref)
+from pypy.module.cpyext.state import State
import sys
class TestIntObject(BaseApiTest):
def test_intobject(self, space):
+ state = space.fromcache(State)
assert PyInt_Check(space, space.wrap(3))
assert PyInt_Check(space, space.w_True)
assert not PyInt_Check(space, space.wrap((1, 2, 3)))
@@ -16,7 +20,8 @@
y = PyInt_AS_LONG(space, space.wrap(i))
assert x == i
assert y == i
- w_x = PyInt_FromLong(space, x + 1)
+ py_x = state.C.PyInt_FromLong(x + 1)
+ w_x = get_w_obj_and_decref(space, py_x)
assert space.type(w_x) is space.w_int
assert space.eq_w(w_x, space.wrap(i + 1))
@@ -40,6 +45,14 @@
assert (PyInt_AsUnsignedLongLongMask(space, space.wrap(10 ** 30))
== 10 ** 30 % (2 ** 64))
+ def test_freelist_direct(self, space):
+ state = space.fromcache(State)
+ p_x = state.C.PyInt_FromLong(12345678)
+ decref(space, p_x)
+ p_y = state.C.PyInt_FromLong(87654321)
+ assert p_x == p_y
+ decref(space, p_y)
+
def test_coerce(self, space):
w_obj = space.appexec([], """():
class Coerce(object):
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -658,7 +658,7 @@
# CCC port it to C
-def type_alloc(space, w_metatype, itemsize=0):
+def type_alloc(typedescr, space, w_metatype, itemsize=0):
metatype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_metatype))
# Don't increase refcount for non-heaptypes
if metatype:
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit