[pypy-commit] pypy default: Implement _PyTraceMalloc_Track() and define a macro that can be

arigo pypy.commits at gmail.com
Sun Mar 12 04:11:57 EDT 2017


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r90637:f0290a07535c
Date: 2017-03-12 09:11 +0100
http://bitbucket.org/pypy/pypy/changeset/f0290a07535c/

Log:	Implement _PyTraceMalloc_Track() and define a macro that can be
	tested for in CPython C extensions. See pymem.h for the exact way to
	say that...

diff --git a/pypy/module/cpyext/include/pymem.h b/pypy/module/cpyext/include/pymem.h
--- a/pypy/module/cpyext/include/pymem.h
+++ b/pypy/module/cpyext/include/pymem.h
@@ -51,6 +51,25 @@
 #define PyMem_Del               PyMem_Free
 #define PyMem_DEL               PyMem_FREE
 
+
+/* From CPython 3.6, with a different goal.  _PyTraceMalloc_Track()
+ * is equivalent to __pypy__.add_memory_pressure(size); it works with
+ * or without the GIL.  _PyTraceMalloc_Untrack() is an empty stub.
+ * You can check if these functions are available by using:
+ *
+ *    #if defined(PYPY_TRACEMALLOC) || \
+ *         (PY_VERSION_HEX >= 0x03060000 && !defined(Py_LIMITED_API))
+ */
+#define PYPY_TRACEMALLOC        1
+
+typedef unsigned int _PyTraceMalloc_domain_t;
+
+PyAPI_FUNC(int) _PyTraceMalloc_Track(_PyTraceMalloc_domain_t domain,
+                                     uintptr_t ptr, size_t size);
+PyAPI_FUNC(int) _PyTraceMalloc_Untrack(_PyTraceMalloc_domain_t domain,
+                                       uintptr_t ptr);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -475,3 +475,8 @@
     with rffi.scoped_nonmovingbuffer(data) as buf:
         fwrite(buf, 1, count, fp)
     return 0
+
+ at cpython_api([lltype.Signed], lltype.Void)
+def _PyPyGC_AddMemoryPressure(space, report):
+    from rpython.rlib import rgc
+    rgc.add_memory_pressure(report)
diff --git a/pypy/module/cpyext/src/pymem.c b/pypy/module/cpyext/src/pymem.c
--- a/pypy/module/cpyext/src/pymem.c
+++ b/pypy/module/cpyext/src/pymem.c
@@ -4,3 +4,46 @@
 {
     return malloc((n) ? (n) : 1);
 }
+
+int _PyTraceMalloc_Track(_PyTraceMalloc_domain_t domain,
+                         uintptr_t ptr, size_t size)
+{
+    /* to avoid acquiring/releasing the GIL too often, only do it
+       if the total reported size exceeds 64KB. */
+    static volatile long unreported_size = 0;
+    long prev, next, report;
+
+    size += sizeof(long);
+    /* ^^^ to account for some alignment.  Important, otherwise we'd
+     * collect sizes of, say, 1-bytes mallocs in 1-bytes increment */
+
+ retry:
+    report = 0;
+    prev = unreported_size;
+    next = prev + size;
+    if (next >= 65536) {
+        report = next;
+        next = 0;
+    }
+    if (prev != next) {
+#ifdef _WIN32
+        if (InterlockedCompareExchange(&unreported_size, next, prev) != prev)
+            goto retry;
+#else
+        if (!__sync_bool_compare_and_swap(&unreported_size, prev, next))
+            goto retry;
+#endif
+    }
+
+    if (report) {
+        PyGILState_STATE state = PyGILState_Ensure();
+        _PyPyGC_AddMemoryPressure(report);
+        PyGILState_Release(state);
+    }
+}
+
+int _PyTraceMalloc_Untrack(_PyTraceMalloc_domain_t domain,
+                           uintptr_t ptr)
+{
+    /* nothing */
+}
diff --git a/pypy/module/cpyext/test/test_object.py b/pypy/module/cpyext/test/test_object.py
--- a/pypy/module/cpyext/test/test_object.py
+++ b/pypy/module/cpyext/test/test_object.py
@@ -214,6 +214,9 @@
 
 class AppTestObject(AppTestCpythonExtensionBase):
     def setup_class(cls):
+        from rpython.rlib import rgc
+        from pypy.interpreter import gateway
+
         AppTestCpythonExtensionBase.setup_class.im_func(cls)
         tmpname = str(py.test.ensuretemp('out', dir=0))
         if cls.runappdirect:
@@ -221,6 +224,28 @@
         else:
             cls.w_tmpname = cls.space.wrap(tmpname)
 
+        cls.total_mem = 0
+        def add_memory_pressure(estimate):
+            assert estimate >= 0
+            cls.total_mem += estimate
+        cls.orig_add_memory_pressure = [rgc.add_memory_pressure]
+        rgc.add_memory_pressure = add_memory_pressure
+
+        def _reset_memory_pressure(space):
+            cls.total_mem = 0
+        cls.w_reset_memory_pressure = cls.space.wrap(
+            gateway.interp2app(_reset_memory_pressure))
+
+        def _cur_memory_pressure(space):
+            return space.newint(cls.total_mem)
+        cls.w_cur_memory_pressure = cls.space.wrap(
+            gateway.interp2app(_cur_memory_pressure))
+
+    def teardown_class(cls):
+        from rpython.rlib import rgc
+        if hasattr(cls, 'orig_add_memory_pressure'):
+            [rgc.add_memory_pressure] = cls.orig_add_memory_pressure
+
     def test_object_malloc(self):
         module = self.import_extension('foo', [
             ("malloctest", "METH_NOARGS",
@@ -323,6 +348,30 @@
         a = module.empty_format('hello')
         assert isinstance(a, unicode)
 
+    def test_add_memory_pressure(self):
+        module = self.import_extension('foo', [
+            ("foo", "METH_O",
+            """
+                _PyTraceMalloc_Track(0, 0, PyInt_AsLong(args) - sizeof(long));
+                Py_INCREF(Py_None);
+                return Py_None;
+            """)])
+        self.reset_memory_pressure()
+        module.foo(42)
+        assert self.cur_memory_pressure() == 0
+        module.foo(65000 - 42)
+        assert self.cur_memory_pressure() == 0
+        module.foo(536)
+        assert self.cur_memory_pressure() == 65536
+        module.foo(40000)
+        assert self.cur_memory_pressure() == 65536
+        module.foo(40000)
+        assert self.cur_memory_pressure() == 65536 + 80000
+        module.foo(35000)
+        assert self.cur_memory_pressure() == 65536 + 80000
+        module.foo(35000)
+        assert self.cur_memory_pressure() == 65536 + 80000 + 70000
+
 class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase):
     """
     PyBuffer_FillInfo populates the fields of a Py_buffer from its arguments.


More information about the pypy-commit mailing list