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

Reply via email to