[PATCH] Introduce fast object pool for image objects (mallocectomy).

Jonathan Morton jmorton at sd070.hel.movial.fi
Mon Jun 15 06:19:51 PDT 2009


NB: This falls back to plain malloc() calls on platforms not explicitly supported.
This patch only explicitly supports recent ARM platforms.
Support for PowerPC and x86-family machines would be beneficial.
---
 pixman/pixman-image.c   |   16 +++-
 pixman/pixman-ptrpool.h |  278 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 292 insertions(+), 2 deletions(-)
 create mode 100644 pixman/pixman-ptrpool.h

diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index e95cff2..9ce0451 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -30,6 +30,7 @@
 #include <assert.h>
 
 #include "pixman-private.h"
+#include "pixman-ptrpool.h"
 
 #define Alpha(x) ((x) >> 24)
 
@@ -88,10 +89,21 @@ _pixman_image_get_scanline_64_generic (pixman_image_t * pict, int x, int y, int
     free(mask8);
 }
 
+static pixman_ptrpool_t* imagePool = NULL;
+
 pixman_image_t *
 _pixman_image_allocate (void)
 {
-    pixman_image_t *image = malloc (sizeof (pixman_image_t));
+    pixman_image_t *image = NULL;
+
+	if(!imagePool) {
+		imagePool = malloc(sizeof(*imagePool));
+		if(!imagePool)
+			return NULL;
+		pixman_ptrpool_init(imagePool, sizeof(*image));
+	}
+
+	image = pixman_ptrpool_fetch(imagePool);
 
     if (image)
     {
@@ -225,7 +237,7 @@ pixman_image_unref (pixman_image_t *image)
 	if (image->type == BITS && image->bits.free_me)
 	    free (image->bits.free_me);
 
-	free (image);
+	pixman_ptrpool_release(imagePool, image);
 
 	return TRUE;
     }
diff --git a/pixman/pixman-ptrpool.h b/pixman/pixman-ptrpool.h
new file mode 100644
index 0000000..a18d722
--- /dev/null
+++ b/pixman/pixman-ptrpool.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright © 2009 Movial Creative Technologies Oy
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of SuSE not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  SuSE makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ *
+ * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Jonathan Morton <jonathan.morton at movial.com>
+ */
+
+#ifndef PIXMAN_PTRPOOL_H
+#define PIXMAN_PTRPOOL_H 1
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+/*
+ * Fast, thread-safe object-pool implementation.
+ * Strategy: fast atomic operations are provided for specific CPUs.
+ * Platforms not directly supported just call malloc().
+ *
+ * Supported platform list:  (add as required)
+ * - ARMv6 (GCC & armcc)
+ */
+
+// void* pixman_atomic_fetch_reset(void** place)
+//
+// If *place is non-zero, atomically resets it to zero while returning original value.
+// If *place is zero, returns zero while leaving *place at zero.
+// If contention invalidates the result, return zero.
+// Does not spin or iterate.
+//
+// Possible implementation #1 (x86):
+// tmp = *place; if(cmpxchg(place, tmp, NULL) == success) return tmp;
+// Possible implementation #2 (RISC):
+// tmp = ldrex(place); if(!tmp) return NULL; if(strex(place, NULL) == failure) return NULL; return tmp;
+
+#if USE_ARM_SIMD
+#if USE_GCC_INLINE_ASM
+
+#define PIXMAN_POOL_IMPLEMENTED 1
+
+static inline void* pixman_atomic_fetch_reset(void** place)
+{
+	void* out = NULL;
+	uint32_t tmp1 = 0, tmp2 = 0;
+
+	asm volatile (
+	"	ldrex   %[out], [%[place]]          \n"
+	"	cmp     %[out], #0                  \n"
+	"	strexne %[tmp1], %[tmp2], [%[place]]\n"
+	"	cmp     %[tmp1], #0                 \n"
+	"	movne   %[out], #0                  \n"
+	: [out] "+&r" (out), [tmp1] "+&r" (tmp1)
+	: [place] "r" (place), [tmp2] "r" (tmp2)
+	: "cc"
+	);
+
+	return out;
+}
+
+#elif defined(__ARMCC_VERSION)
+
+#define PIXMAN_POOL_IMPLEMENTED 1
+
+static inline void* pixman_atomic_fetch_reset(void** place)
+{
+	void* out = (void*) __ldrex(place);
+
+	if(out)
+		if(__strex(0, place))
+			return NULL;
+
+	return out;
+}
+
+#endif  // ! USE_GCC_INLINE_ASM
+#endif  // USE_ARM_SIMD
+
+// bool pixman_atomic_store_in_empty(void** place, void* item)
+//
+// If *place is zero, atomically fill it with item and return true.
+// Otherwise return false, leaving *place untouched.
+// If contention interferes, return false and leave *place untouched.
+// Does not spin or iterate.
+//
+// Possible implementation #1 (x86):
+// return cmpxchg(place, NULL, item) == success;
+// Possible implementation #2 (RISC):
+// if(ldrex(place)) return false; return strex(place, item) == success;
+
+#if USE_ARM_SIMD
+#if USE_GCC_INLINE_ASM
+
+static inline int pixman_atomic_store_in_empty(void** place, void* item)
+{
+	int tmp = 0;
+
+	asm volatile (
+	"	ldrex   %[tmp], [%[place]]          \n"
+	"	cmp     %[tmp], #0                  \n"
+	"	movne   %[tmp], #1                  \n"
+	"	strexeq %[tmp], %[item], [%[place]] \n"
+	"	rsb     %[tmp], %[tmp], #1          \n"
+	: [tmp] "=&r" (tmp)
+	: [item] "r" (item), [place] "r" (place)
+	: "cc"
+	);
+
+	return tmp;
+}
+
+#elif defined(__ARMCC_VERSION)
+
+static inline int pixman_atomic_store_in_empty(void** place, void* item)
+{
+	if(__ldrex(place))
+		return 0;
+
+	return !__strex((uint32_t) item, place);
+}
+
+#endif  // ! USE_GCC_INLINE_ASM
+#endif  // USE_ARM_SIMD
+
+
+#ifndef PIXMAN_POOL_IMPLEMENTED
+#define PIXMAN_NO_POOL 1
+#endif
+
+#if PIXMAN_NO_POOL
+
+/*
+ * Don't get stuck on the roof.
+ * These generic versions should be no slower than just calling malloc() directly.
+ */
+
+typedef struct { size_t size; } pixman_ptrpool_t;
+
+static inline void pixman_ptrpool_init(pixman_ptrpool_t* pool, size_t size)
+{
+	pool->size = size;
+}
+
+static inline void pixman_ptrpool_destroy(pixman_ptrpool_t* pool)
+{
+	pool=pool;
+}
+
+static inline void* pixman_ptrpool_fetch(pixman_ptrpool_t* pool)
+{
+	return malloc(pool->size);
+}
+
+static inline void pixman_ptrpool_release(pixman_ptrpool_t* pool, void* ptr)
+{
+	pool=pool;
+	free(ptr);
+}
+
+#else
+
+/*
+ * The "porcelain" part of the fast implementation.
+ * This should be used for all optimised implementations.
+ */
+
+#define PIXMAN_POOL_SIZE (4)
+
+typedef struct
+{
+	void *pool[PIXMAN_POOL_SIZE];
+	size_t size;
+	int top;
+} pixman_ptrpool_t;
+
+/*
+ * The init() and destroy() methods need not be thread-safe.
+ */
+
+static inline void pixman_ptrpool_init(pixman_ptrpool_t* pool, size_t size)
+{
+	memset(pool, 0, sizeof(*pool));
+	pool->size = size;
+}
+
+static inline void pixman_ptrpool_destroy(pixman_ptrpool_t* pool)
+{
+	int i;
+
+	for(i=0; i < PIXMAN_POOL_SIZE; i++)
+		if(pool->pool[i])
+			free(pool->pool[i]);
+
+	memset(pool, 0, sizeof(*pool));
+}
+
+/*
+ * The fetch() and release() methods use the atomic "plumbing" operations
+ * defined above to become thread-safe.
+ */
+
+static inline void* pixman_ptrpool_fetch(pixman_ptrpool_t* pool)
+{
+	int i = pool->top-1;
+	void* p;
+
+	if(i < 0) i = 0;
+
+	// fast path first
+	// ideally the pool is used as a bottom-up stack
+	p = pixman_atomic_fetch_reset(&(pool->pool[i]));
+	if(p) {
+		pool->top = i;
+		return p;
+	}
+
+	// either the pool is empty, or contention has broken the fastpath
+	// search the whole pool instead
+	for(i = PIXMAN_POOL_SIZE-1; i >= 0; i--) {
+		p = pixman_atomic_fetch_reset(&(pool->pool[i]));
+		if(p) {
+			pool->top = i;
+			return p;
+		}
+	}
+
+	// the pool is empty
+	pool->top = 0;
+	p = malloc(pool->size);
+	return p;
+}
+
+static inline void pixman_ptrpool_release(pixman_ptrpool_t* pool, void* ptr)
+{
+	// NB: the 'top' pointer is non-critical and can therefore be accessed with less care
+	int i = pool->top;
+
+	// fast path first
+	// ideally the pool is used as a bottom-up stack
+	if(pixman_atomic_store_in_empty(&(pool->pool[i]), ptr)) {
+		pool->top = i+1;
+		return;
+	}
+
+	// either the pool is full, or contention has broken the fastpath
+	// search for a free space
+	for(i=0; i < PIXMAN_POOL_SIZE; i++) {
+		if(pixman_atomic_store_in_empty(&(pool->pool[i]), ptr)) {
+			pool->top = i+1;
+			return;
+		}
+	}
+
+	// the pool is full
+	pool->top = PIXMAN_POOL_SIZE;
+	free(ptr);
+}
+
+#endif  // !PIXMAN_NO_POOL
+
+#endif  // !PIXMAN_PTRPOOL_H
-- 
1.5.6.3


--=-by6jJzXI0j1mDgcsBTPS--



More information about the cairo mailing list