[pygobject] invoke state: add a free memory cache for PyGIInvokeArgState



commit e1921b7224ca1e909d9fe5483a09414742d0baf4
Author: Christoph Reiter <creiter src gnome org>
Date:   Sat Sep 26 21:29:54 2015 +0200

    invoke state: add a free memory cache for PyGIInvokeArgState
    
    Keep one free allocation per argument count around
    to reduce g_slice_alloc/free usage.
    
    Reduces CPU time for simple functions by 10% and 5% for closures.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=750658

 gi/pygi-closure.c |    6 +---
 gi/pygi-invoke.c  |   61 +++++++++++++++++++++++++++++++++++++++++++++++-----
 gi/pygi-invoke.h  |    4 +++
 3 files changed, 61 insertions(+), 10 deletions(-)
---
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index f54d886..6a68e2b 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -317,9 +317,7 @@ _invoke_state_init_from_cache (PyGIInvokeState *state,
     state->args = NULL;
     state->error = NULL;
 
-    state->args = g_slice_alloc0 (state->n_args * sizeof (PyGIInvokeArgState));
-    if (state->args == NULL && state->n_args != 0) {
-        PyErr_NoMemory();
+    if (!_pygi_invoke_arg_state_init (state)) {
         return FALSE;
     }
 
@@ -332,7 +330,7 @@ _invoke_state_init_from_cache (PyGIInvokeState *state,
 static void
 _invoke_state_clear (PyGIInvokeState *state)
 {
-    g_slice_free1 (state->n_args * sizeof (PyGIInvokeArgState), state->args);
+    _pygi_invoke_arg_state_free (state);
     Py_XDECREF (state->py_in_args);
 }
 
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
index af4d7c0..0290fc9 100644
--- a/gi/pygi-invoke.c
+++ b/gi/pygi-invoke.c
@@ -216,6 +216,59 @@ _py_args_combine_and_check_length (PyGICallableCache *cache,
     return combined_py_args;
 }
 
+/* To reduce calls to g_slice_*() we (1) allocate all the memory depended on
+ * the argument count in one go and (2) keep one version per argument count
+ * around for faster reuse.
+ */
+
+#define PyGI_INVOKE_ARG_STATE_SIZE(n)   (n * (sizeof (PyGIInvokeArgState) + sizeof (GIArgument *)))
+#define PyGI_INVOKE_ARG_STATE_N_MAX     10
+static gpointer free_arg_state[PyGI_INVOKE_ARG_STATE_N_MAX];
+
+/**
+ * _pygi_invoke_arg_state_init:
+ * Sets PyGIInvokeState.args and PyGIInvokeState.ffi_args.
+ * On error returns FALSE and sets an exception.
+ */
+gboolean
+_pygi_invoke_arg_state_init (PyGIInvokeState *state) {
+
+    gpointer mem;
+
+    if (state->n_args < PyGI_INVOKE_ARG_STATE_N_MAX && (mem = free_arg_state[state->n_args]) != NULL) {
+        free_arg_state[state->n_args] = NULL;
+        memset (mem, 0, PyGI_INVOKE_ARG_STATE_SIZE (state->n_args));
+    } else {
+        mem = g_slice_alloc0 (PyGI_INVOKE_ARG_STATE_SIZE (state->n_args));
+    }
+
+    if (mem == NULL && state->n_args != 0) {
+        PyErr_NoMemory();
+        return FALSE;
+    }
+
+    if (mem != NULL) {
+        state->args = mem;
+        state->ffi_args = (gpointer)((gchar *)mem + state->n_args * sizeof (PyGIInvokeArgState));
+    }
+
+    return TRUE;
+}
+
+/**
+ * _pygi_invoke_arg_state_free:
+ * Frees PyGIInvokeState.args and PyGIInvokeState.ffi_args
+ */
+void
+_pygi_invoke_arg_state_free(PyGIInvokeState *state) {
+    if (state->n_args < PyGI_INVOKE_ARG_STATE_N_MAX && free_arg_state[state->n_args] == NULL) {
+        free_arg_state[state->n_args] = state->args;
+        return;
+    }
+
+    g_slice_free1 (PyGI_INVOKE_ARG_STATE_SIZE (state->n_args), state->args);
+}
+
 static gboolean
 _invoke_state_init_from_cache (PyGIInvokeState *state,
                                PyGIFunctionCache *function_cache,
@@ -245,14 +298,10 @@ _invoke_state_init_from_cache (PyGIInvokeState *state,
     }
     state->n_py_in_args = PyTuple_Size (state->py_in_args);
 
-    state->args = g_slice_alloc0 (state->n_args * (sizeof (PyGIInvokeArgState) + sizeof (GIArgument *)));
-    if (state->args == NULL && state->n_args != 0) {
-        PyErr_NoMemory();
+    if (!_pygi_invoke_arg_state_init (state)) {
         return FALSE;
     }
 
-    state->ffi_args = (gpointer)((char*)state->args + state->n_args * sizeof (PyGIInvokeArgState));
-
     state->error = NULL;
 
     if (cache->throws) {
@@ -268,7 +317,7 @@ _invoke_state_init_from_cache (PyGIInvokeState *state,
 static void
 _invoke_state_clear (PyGIInvokeState *state, PyGIFunctionCache *function_cache)
 {
-    g_slice_free1 (state->n_args * (sizeof (PyGIInvokeArgState) + sizeof (GIArgument *)), state->args);
+    _pygi_invoke_arg_state_free (state);
     Py_XDECREF (state->py_in_args);
 }
 
diff --git a/gi/pygi-invoke.h b/gi/pygi-invoke.h
index b49ffa7..dfed2e0 100644
--- a/gi/pygi-invoke.h
+++ b/gi/pygi-invoke.h
@@ -38,6 +38,10 @@ PyObject *pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
 PyObject *_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args,
                                         PyObject *kwargs);
 
+gboolean _pygi_invoke_arg_state_init (PyGIInvokeState *state);
+
+void _pygi_invoke_arg_state_free     (PyGIInvokeState *state);
+
 G_END_DECLS
 
 #endif /* __PYGI_INVOKE_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]