]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-126703: Add PyCFunction freelist (GH-128692)
authorPieter Eendebak <pieter.eendebak@gmail.com>
Sat, 5 Apr 2025 15:51:05 +0000 (17:51 +0200)
committerGitHub <noreply@github.com>
Sat, 5 Apr 2025 15:51:05 +0000 (23:51 +0800)
Include/internal/pycore_freelist_state.h
Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-22-12-03.gh-issue-126703.6rAxZ7.rst [new file with mode: 0644]
Objects/methodobject.c
Objects/object.c

index 7c252f5b570c13b17e33ce0ee9ddcb27eec42e3a..54415b22fd41efe82fdacff1bded8d717756da23 100644 (file)
@@ -24,6 +24,8 @@ extern "C" {
 #  define Py_futureiters_MAXFREELIST 255
 #  define Py_object_stack_chunks_MAXFREELIST 4
 #  define Py_unicode_writers_MAXFREELIST 1
+#  define Py_pycfunctionobject_MAXFREELIST 16
+#  define Py_pycmethodobject_MAXFREELIST 16
 #  define Py_pymethodobjects_MAXFREELIST 20
 
 // A generic freelist of either PyObjects or other data structures.
@@ -53,6 +55,8 @@ struct _Py_freelists {
     struct _Py_freelist futureiters;
     struct _Py_freelist object_stack_chunks;
     struct _Py_freelist unicode_writers;
+    struct _Py_freelist pycfunctionobject;
+    struct _Py_freelist pycmethodobject;
     struct _Py_freelist pymethodobjects;
 };
 
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-22-12-03.gh-issue-126703.6rAxZ7.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-22-12-03.gh-issue-126703.6rAxZ7.rst
new file mode 100644 (file)
index 0000000..6dc0413
--- /dev/null
@@ -0,0 +1 @@
+Improve performance of builtin methods by using a freelist.
index ecec0f7205a11d12938223c3ae1c5f01b37682f9..1f459dea44192cdc0eab5dde83ca93e15920ba10 100644 (file)
@@ -4,6 +4,7 @@
 #include "Python.h"
 #include "pycore_call.h"          // _Py_CheckFunctionResult()
 #include "pycore_ceval.h"         // _Py_EnterRecursiveCallTstate()
+#include "pycore_freelist.h"
 #include "pycore_object.h"
 #include "pycore_pyerrors.h"
 #include "pycore_pystate.h"       // _PyThreadState_GET()
@@ -85,9 +86,12 @@ PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *c
                             "flag but no class");
             return NULL;
         }
-        PyCMethodObject *om = PyObject_GC_New(PyCMethodObject, &PyCMethod_Type);
+        PyCMethodObject *om = _Py_FREELIST_POP(PyCMethodObject, pycmethodobject);
         if (om == NULL) {
-            return NULL;
+            om = PyObject_GC_New(PyCMethodObject, &PyCMethod_Type);
+            if (om == NULL) {
+                return NULL;
+            }
         }
         om->mm_class = (PyTypeObject*)Py_NewRef(cls);
         op = (PyCFunctionObject *)om;
@@ -98,9 +102,12 @@ PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *c
                             "but no METH_METHOD flag");
             return NULL;
         }
-        op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);
+        op = _Py_FREELIST_POP(PyCFunctionObject, pycfunctionobject);
         if (op == NULL) {
-            return NULL;
+            op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);
+            if (op == NULL) {
+                return NULL;
+            }
         }
     }
 
@@ -171,7 +178,14 @@ meth_dealloc(PyObject *self)
     Py_XDECREF(PyCFunction_GET_CLASS(m));
     Py_XDECREF(m->m_self);
     Py_XDECREF(m->m_module);
-    PyObject_GC_Del(m);
+    if (m->m_ml->ml_flags & METH_METHOD) {
+        assert(Py_IS_TYPE(self, &PyCMethod_Type));
+        _Py_FREELIST_FREE(pycmethodobject, m, PyObject_GC_Del);
+    }
+    else {
+        assert(Py_IS_TYPE(self, &PyCFunction_Type));
+        _Py_FREELIST_FREE(pycfunctionobject, m, PyObject_GC_Del);
+    }
     Py_TRASHCAN_END;
 }
 
index 457ff17b980e75aefa55a416bf9735d243e8b5b8..42ac3a1c2baa7bf22c0aaa9477e3b07c86357c8b 100644 (file)
@@ -942,6 +942,8 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization)
     }
     clear_freelist(&freelists->unicode_writers, is_finalization, PyMem_Free);
     clear_freelist(&freelists->ints, is_finalization, free_object);
+    clear_freelist(&freelists->pycfunctionobject, is_finalization, PyObject_GC_Del);
+    clear_freelist(&freelists->pycmethodobject, is_finalization, PyObject_GC_Del);
     clear_freelist(&freelists->pymethodobjects, is_finalization, free_object);
 }