diff --git a/test/bufspeed/.gitignore b/test/bufspeed/.gitignore new file mode 100644 index 0000000000..25d1705737 --- /dev/null +++ b/test/bufspeed/.gitignore @@ -0,0 +1,10 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs +*.xml +test1 +test2 diff --git a/test/bufspeed/Makefile.am b/test/bufspeed/Makefile.am new file mode 100644 index 0000000000..e86b7a7699 --- /dev/null +++ b/test/bufspeed/Makefile.am @@ -0,0 +1,6 @@ +noinst_PROGRAMS = test1 test2 + +test1_SOURCES = test1.c gstbuffer.c gstmempool.c + +#LIBS += $(GST_LIBS) +CFLAGS += $(GST_CFLAGS) diff --git a/test/bufspeed/README b/test/bufspeed/README new file mode 100644 index 0000000000..8bb6600a9f --- /dev/null +++ b/test/bufspeed/README @@ -0,0 +1,6 @@ +benchmark of 5000000 gst_buffer_new/free on 0.2.1 code +------------------------------------------------------ +gstmemchunk, no lock: real 0m1.309s user 0m1.220s sys 0m0.070s +gmemchunk, no lock: real 0m3.872s user 0m3.740s sys 0m0.090s +gstmemchunk, lock: real 0m5.306s user 0m5.160s sys 0m0.100s +gmemchunk, lock: real 0m8.001s user 0m7.890s sys 0m0.080s diff --git a/test/bufspeed/gstbuffer.c b/test/bufspeed/gstbuffer.c new file mode 100644 index 0000000000..35a92ee90a --- /dev/null +++ b/test/bufspeed/gstbuffer.c @@ -0,0 +1,495 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * + * gstbuffer.c: Buffer operations + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* this file makes too much noise for most debugging sessions */ +#define GST_DEBUG_FORCE_DISABLE +#include "gst/gst_private.h" + +#include "gstbuffer.h" +#include "gstmempool.h" + +GType _gst_buffer_type; + +static GstMemPool *_gst_buffer_pool; + +static void +gst_buffer_alloc_func (GstMemPool *pool, gpointer data) +{ + GstBuffer *buffer = GST_BUFFER (data); + + GST_DATA_TYPE(buffer) = _gst_buffer_type; + buffer->lock = g_mutex_new (); +} + +static void +gst_buffer_free_func (GstMemPool *pool, gpointer data) +{ + GstBuffer *buffer = GST_BUFFER (data); + + g_mutex_free (buffer->lock); +} + +void +_gst_buffer_initialize (void) +{ + int buffersize = sizeof(GstBuffer); + static const GTypeInfo buffer_info = { + 0, // sizeof(class), + NULL, + NULL, + NULL, + NULL, + NULL, + 0, // sizeof(object), + 0, + NULL, + }; + + // round up to the nearest 32 bytes for cache-line and other efficiencies + buffersize = (((buffersize-1) / 32) + 1) * 32; + + _gst_buffer_pool = gst_mem_pool_new ("GstBuffer", buffersize, + buffersize * 32, G_ALLOC_AND_FREE, gst_buffer_alloc_func, gst_buffer_free_func); + + _gst_buffer_type = g_type_register_static (G_TYPE_INT, "GstBuffer", &buffer_info, 0); +} + +/** + * gst_buffer_new: + * + * Create a new buffer. + * + * Returns: new buffer + */ +GstBuffer* +gst_buffer_new (void) +{ + GstBuffer *buffer; + + buffer = gst_mem_pool_alloc (_gst_buffer_pool); + + GST_INFO (GST_CAT_BUFFER,"creating new buffer %p",buffer); + +#ifdef HAVE_ATOMIC_H + atomic_set (&buffer->refcount, 1); +#else + buffer->refcount = 1; +#endif + buffer->offset = -1; + buffer->flags = 0; + buffer->data = NULL; + buffer->size = 0; + buffer->maxsize = 0; + buffer->timestamp = 0; + buffer->parent = NULL; + buffer->pool = NULL; + buffer->pool_private = NULL; + buffer->free = NULL; + buffer->copy = NULL; + + return buffer; +} + +/** + * gst_buffer_new_from_pool: + * @pool: the buffer pool to use + * + * Create a new buffer using the specified bufferpool. + * + * Returns: new buffer + */ +GstBuffer* +gst_buffer_new_from_pool (GstBufferPool *pool, guint32 offset, guint32 size) +{ + GstBuffer *buffer; + + g_return_val_if_fail (pool != NULL, NULL); + g_return_val_if_fail (pool->buffer_new != NULL, NULL); + + buffer = pool->buffer_new (pool, offset, size, pool->user_data); + buffer->pool = pool; + buffer->free = pool->buffer_free; + buffer->copy = pool->buffer_copy; + + GST_INFO (GST_CAT_BUFFER,"creating new buffer %p from pool %p (size %x, offset %x)", + buffer, pool, size, offset); + + return buffer; +} + +/** + * gst_buffer_create_sub: + * @parent: parent buffer + * @offset: offset into parent buffer + * @size: size of new subbuffer + * + * Creates a sub-buffer from the parent at a given offset. + * + * Returns: new buffer + */ +GstBuffer* +gst_buffer_create_sub (GstBuffer *parent, + guint32 offset, + guint32 size) +{ + GstBuffer *buffer; + + g_return_val_if_fail (parent != NULL, NULL); + g_return_val_if_fail (GST_BUFFER_REFCOUNT(parent) > 0, NULL); + g_return_val_if_fail (size > 0, NULL); + g_return_val_if_fail ((offset+size) <= parent->size, NULL); + + buffer = gst_mem_pool_alloc (_gst_buffer_pool); + GST_DATA_TYPE(buffer) = _gst_buffer_type; + + GST_INFO (GST_CAT_BUFFER,"creating new subbuffer %p from parent %p (size %u, offset %u)", + buffer, parent, size, offset); + +#ifdef HAVE_ATOMIC_H + atomic_set (&buffer->refcount, 1); +#else + buffer->refcount = 1; +#endif + + // copy flags and type from parent, for lack of better + buffer->flags = parent->flags; + + // set the data pointer, size, offset, and maxsize + buffer->data = parent->data + offset; + buffer->size = size; + buffer->maxsize = parent->size - offset; + + // deal with bogus/unknown offsets + if (parent->offset != -1) + buffer->offset = parent->offset + offset; + else + buffer->offset = -1; + + // again, for lack of better, copy parent's timestamp + buffer->timestamp = parent->timestamp; + buffer->maxage = parent->maxage; + + // if the parent buffer is a subbuffer itself, use its parent, a real buffer + if (parent->parent != NULL) + parent = parent->parent; + + // set parentage and reference the parent + buffer->parent = parent; + gst_buffer_ref (parent); + + buffer->pool = NULL; + + return buffer; +} + + +// FIXME FIXME: how does this overlap with the newly-added gst_buffer_span() ??? +/** + * gst_buffer_append: + * @buffer: a buffer + * @append: the buffer to append + * + * Creates a new buffer by appending the data of append to the + * existing data of buffer. + * + * Returns: new buffer + */ +GstBuffer* +gst_buffer_append (GstBuffer *buffer, + GstBuffer *append) +{ + guint size; + GstBuffer *newbuf; + + g_return_val_if_fail (buffer != NULL, NULL); + g_return_val_if_fail (append != NULL, NULL); + g_return_val_if_fail (buffer->pool == NULL, NULL); + g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL); + g_return_val_if_fail (GST_BUFFER_REFCOUNT(append) > 0, NULL); + + GST_INFO (GST_CAT_BUFFER,"appending buffers %p and %p",buffer,append); + + GST_BUFFER_LOCK (buffer); + // the buffer is not used by anyone else + if (GST_BUFFER_REFCOUNT (buffer) == 1 && buffer->parent == NULL + && !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE)) { + // save the old size + size = buffer->size; + buffer->size += append->size; + buffer->data = g_realloc (buffer->data, buffer->size); + memcpy(buffer->data + size, append->data, append->size); + GST_BUFFER_UNLOCK (buffer); + } + // the buffer is used, create a new one + else { + newbuf = gst_buffer_new (); + newbuf->size = buffer->size+append->size; + newbuf->data = g_malloc (newbuf->size); + memcpy (newbuf->data, buffer->data, buffer->size); + memcpy (newbuf->data+buffer->size, append->data, append->size); + GST_BUFFER_UNLOCK (buffer); + gst_buffer_unref (buffer); + buffer = newbuf; + } + return buffer; +} + +/** + * gst_buffer_destroy: + * @buffer: the GstBuffer to destroy + * + * destroy the buffer + */ +void +gst_buffer_destroy (GstBuffer *buffer) +{ + + g_return_if_fail (buffer != NULL); + + GST_INFO (GST_CAT_BUFFER, "freeing %sbuffer %p", + (buffer->parent?"sub":""), + buffer); + + // free the data only if there is some, DONTFREE isn't set, and not sub + if (GST_BUFFER_DATA (buffer) && + !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE) && + (buffer->parent == NULL)) { + // if there's a free function, use it + if (buffer->free != NULL) { + (buffer->free)(buffer); + } else { + g_free (GST_BUFFER_DATA (buffer)); + } + } + + // unreference the parent if there is one + if (buffer->parent != NULL) + gst_buffer_unref (buffer->parent); + + // remove it entirely from memory + gst_mem_pool_free (_gst_buffer_pool,buffer); +} + +/** + * gst_buffer_ref: + * @buffer: the GstBuffer to reference + * + * Increment the refcount of this buffer. + */ +void +gst_buffer_ref (GstBuffer *buffer) +{ + g_return_if_fail (buffer != NULL); + g_return_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0); + + GST_INFO (GST_CAT_BUFFER, "ref buffer %p\n", buffer); + +#ifdef HAVE_ATOMIC_H + atomic_inc (&(buffer->refcount)); +#else + GST_BUFFER_LOCK (buffer); + buffer->refcount++; + GST_BUFFER_UNLOCK (buffer); +#endif +} + +/** + * gst_buffer_unref: + * @buffer: the GstBuffer to unref + * + * Decrement the refcount of this buffer. If the refcount is + * zero, the buffer will be destroyed. + */ +void +gst_buffer_unref (GstBuffer *buffer) +{ + gint zero; + + g_return_if_fail (buffer != NULL); + g_return_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0); + + GST_INFO (GST_CAT_BUFFER, "unref buffer %p\n", buffer); + +#ifdef HAVE_ATOMIC_H + zero = atomic_dec_and_test (&(buffer->refcount)); +#else + GST_BUFFER_LOCK (buffer); + buffer->refcount--; + zero = (buffer->refcount == 0); + GST_BUFFER_UNLOCK (buffer); +#endif + + /* if we ended up with the refcount at zero, destroy the buffer */ + if (zero) { + gst_buffer_destroy (buffer); + } +} + +/** + * gst_buffer_copy: + * @buffer: the orignal GstBuffer to make a copy of + * + * Make a full copy of the give buffer, data and all. + * + * Returns: new buffer + */ +GstBuffer * +gst_buffer_copy (GstBuffer *buffer) +{ + GstBuffer *newbuf; + + g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL); + + // if a copy function exists, use it, else copy the bytes + if (buffer->copy != NULL) { + newbuf = (buffer->copy)(buffer); + } else { + // allocate a new buffer + newbuf = gst_buffer_new(); + + // copy the absolute size + newbuf->size = buffer->size; + // allocate space for the copy + newbuf->data = (guchar *)g_malloc (buffer->size); + // copy the data straight across + memcpy(newbuf->data,buffer->data,buffer->size); + // the new maxsize is the same as the size, since we just malloc'd it + newbuf->maxsize = newbuf->size; + } + newbuf->offset = buffer->offset; + newbuf->timestamp = buffer->timestamp; + newbuf->maxage = buffer->maxage; + + // since we just created a new buffer, so we have no ties to old stuff + newbuf->parent = NULL; + newbuf->pool = NULL; + + return newbuf; +} + +/* + * gst_buffer_is_span_fast + * @buf1: first source buffer + * @buf2: second source buffer + * + * Determines whether a gst_buffer_span is free, or requires a memcpy. + * + * Returns: TRUE if the buffers are contiguous, FALSE if a copy would be required. + */ +gboolean +gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2) +{ + g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, FALSE); + g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, FALSE); + + return (buf1->parent && buf2->parent && + (buf1->parent == buf2->parent) && + ((buf1->data + buf1->size) == buf2->data)); +} + + +/** + * gst_buffer_span: + * @buf1: first source buffer to merge + * @offset: offset in first buffer to start new buffer + * @buf2: second source buffer to merge + * @len: length of new buffer + * + * Create a new buffer that consists of part of buf1 and buf2. + * Logically, buf1 and buf2 are concatenated into a single larger + * buffer, and a new buffer is created at the given offset inside + * this space, with a given length. + * + * If the two source buffers are children of the same larger buffer, + * and are contiguous, the new buffer will be a child of the shared + * parent, and thus no copying is necessary. + * + * Returns: new buffer that spans the two source buffers + */ +// FIXME need to think about CoW and such... +GstBuffer * +gst_buffer_span (GstBuffer *buf1, guint32 offset, GstBuffer *buf2, guint32 len) +{ + GstBuffer *newbuf; + + g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, NULL); + g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, NULL); + + // make sure buf1 has a lower address than buf2 + if (buf1->data > buf2->data) { + GstBuffer *tmp = buf1; + g_print ("swapping buffers\n"); + buf1 = buf2; + buf2 = tmp; + } + + // if the two buffers have the same parent and are adjacent + if (gst_buffer_is_span_fast(buf1,buf2)) { + // we simply create a subbuffer of the common parent + newbuf = gst_buffer_create_sub (buf1->parent, buf1->data - (buf1->parent->data) + offset, len); + } + else { + g_print ("slow path taken in buffer_span\n"); + // otherwise we simply have to brute-force copy the buffers + newbuf = gst_buffer_new (); + + // put in new size + newbuf->size = len; + // allocate space for the copy + newbuf->data = (guchar *)g_malloc(len); + // copy the first buffer's data across + memcpy(newbuf->data, buf1->data + offset, buf1->size - offset); + // copy the second buffer's data across + memcpy(newbuf->data + (buf1->size - offset), buf2->data, len - (buf1->size - offset)); + + if (newbuf->offset != -1) + newbuf->offset = buf1->offset + offset; + newbuf->timestamp = buf1->timestamp; + if (buf2->maxage > buf1->maxage) newbuf->maxage = buf2->maxage; + else newbuf->maxage = buf1->maxage; + + } + + return newbuf; +} + + +/** + * gst_buffer_merge: + * @buf1: first source buffer to merge + * @buf2: second source buffer to merge + * + * Create a new buffer that is the concatenation of the two source + * buffers. The original source buffers will not be modified or + * unref'd. + * + * Internally is nothing more than a specialized gst_buffer_span, + * so the same optimizations can occur. + * + * Returns: new buffer that's the concatenation of the source buffers + */ +GstBuffer * +gst_buffer_merge (GstBuffer *buf1, GstBuffer *buf2) +{ + // we're just a specific case of the more general gst_buffer_span() + return gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size); +} diff --git a/test/bufspeed/gstbuffer.h b/test/bufspeed/gstbuffer.h new file mode 100644 index 0000000000..d3a5a875b4 --- /dev/null +++ b/test/bufspeed/gstbuffer.h @@ -0,0 +1,174 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * + * gstbuffer.h: Header for GstBuffer object + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_BUFFER_H__ +#define __GST_BUFFER_H__ + +// +// Define this to add file:line info to each GstBuffer showing +// the location in the source code where the buffer was created. +// +// #define GST_BUFFER_WHERE +// +// Then in gdb, you can `call gst_buffer_print_live()' to get a list +// of allocated GstBuffers and also the file:line where they were +// allocated. +// + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ATOMIC_H +#include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern GType _gst_buffer_type; + +#define GST_TYPE_BUFFER (_gst_buffer_type) +#define GST_BUFFER(buf) ((GstBuffer *)(buf)) +#define GST_IS_BUFFER(buf) (GST_DATA_TYPE(buf) == GST_TYPE_BUFFER) + +#define GST_BUFFER_FLAGS(buf) (GST_BUFFER(buf)->flags) +#define GST_BUFFER_FLAG_IS_SET(buf,flag) (GST_BUFFER_FLAGS(buf) & (1<<(flag))) +#define GST_BUFFER_FLAG_SET(buf,flag) G_STMT_START{ (GST_BUFFER_FLAGS(buf) |= (1<<(flag))); }G_STMT_END +#define GST_BUFFER_FLAG_UNSET(buf,flag) G_STMT_START{ (GST_BUFFER_FLAGS(buf) &= ~(1<<(flag))); }G_STMT_END + + +#define GST_BUFFER_DATA(buf) (GST_BUFFER(buf)->data) +#define GST_BUFFER_SIZE(buf) (GST_BUFFER(buf)->size) +#define GST_BUFFER_OFFSET(buf) (GST_BUFFER(buf)->offset) +#define GST_BUFFER_MAXSIZE(buf) (GST_BUFFER(buf)->maxsize) +#define GST_BUFFER_TIMESTAMP(buf) (GST_BUFFER(buf)->timestamp) +#define GST_BUFFER_MAXAGE(buf) (GST_BUFFER(buf)->maxage) +#define GST_BUFFER_BUFFERPOOL(buf) (GST_BUFFER(buf)->pool) +#define GST_BUFFER_PARENT(buf) (GST_BUFFER(buf)->parent) +#define GST_BUFFER_POOL_PRIVATE(buf) (GST_BUFFER(buf)->pool_private) +#define GST_BUFFER_COPY_FUNC(buf) (GST_BUFFER(buf)->copy) +#define GST_BUFFER_FREE_FUNC(buf) (GST_BUFFER(buf)->free) + + +#define GST_BUFFER_LOCK(buf) (g_mutex_lock(GST_BUFFER(buf)->lock)) +#define GST_BUFFER_TRYLOCK(buf) (g_mutex_trylock(GST_BUFFER(buf)->lock)) +#define GST_BUFFER_UNLOCK(buf) (g_mutex_unlock(GST_BUFFER(buf)->lock)) + + +typedef enum { + GST_BUFFER_READONLY, + GST_BUFFER_ORIGINAL, + GST_BUFFER_DONTFREE, + + GST_BUFFER_FLUSH, + GST_BUFFER_EOS, + GST_BUFFER_DISCONTINUOUS, +} GstBufferFlags; + + +typedef struct _GstBuffer GstBuffer; + + +typedef void (*GstBufferFreeFunc) (GstBuffer *buf); +typedef GstBuffer *(*GstBufferCopyFunc) (GstBuffer *srcbuf); + + +#include + +struct _GstBuffer { + GstData data_type; + + /* locking */ + GMutex *lock; + + /* refcounting */ +#ifdef HAVE_ATOMIC_H + atomic_t refcount; +#define GST_BUFFER_REFCOUNT(buf) (atomic_read(&(GST_BUFFER((buf))->refcount))) +#else + int refcount; +#define GST_BUFFER_REFCOUNT(buf) (GST_BUFFER(buf)->refcount) +#endif + + /* flags */ + guint16 flags; + + /* pointer to data, its size, and offset in original source if known */ + guchar *data; + guint32 size; + guint32 maxsize; + guint32 offset; + + /* timestamp */ + gint64 timestamp; + gint64 maxage; + + /* subbuffer support, who's my parent? */ + GstBuffer *parent; + + /* this is a pointer to the buffer pool (if any) */ + GstBufferPool *pool; + gpointer pool_private; + + /* utility function pointers */ + GstBufferFreeFunc free; // free the data associated with the buffer + GstBufferCopyFunc copy; // copy the data from one buffer to another +}; + +/* initialisation */ +void _gst_buffer_initialize (void); +/* creating a new buffer from scratch */ +GstBuffer* gst_buffer_new (void); +GstBuffer* gst_buffer_new_from_pool (GstBufferPool *pool, guint32 offset, guint32 size); + +/* creating a subbuffer */ +GstBuffer* gst_buffer_create_sub (GstBuffer *parent, guint32 offset, guint32 size); + +/* refcounting */ +void gst_buffer_ref (GstBuffer *buffer); +void gst_buffer_unref (GstBuffer *buffer); + +/* destroying the buffer */ +void gst_buffer_destroy (GstBuffer *buffer); + +/* copy buffer */ +GstBuffer* gst_buffer_copy (GstBuffer *buffer); + +/* merge, span, or append two buffers, intelligently */ +GstBuffer* gst_buffer_merge (GstBuffer *buf1, GstBuffer *buf2); +GstBuffer* gst_buffer_span (GstBuffer *buf1,guint32 offset,GstBuffer *buf2,guint32 len); +GstBuffer* gst_buffer_append (GstBuffer *buf, GstBuffer *buf2); + +gboolean gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_BUFFER_H__ */ diff --git a/test/bufspeed/gstmempool.c b/test/bufspeed/gstmempool.c new file mode 100644 index 0000000000..c9f956e6f3 --- /dev/null +++ b/test/bufspeed/gstmempool.c @@ -0,0 +1,191 @@ +#include "gstmempool.h" + +#ifdef __SMP__ +#define POOL_LOCK "lock ; " +#else +#define POOL_LOCK "" +#endif + +#define GST_MEM_POOL_AREA(pool) (((GstMemPoolElement*)(pool))->area) +#define GST_MEM_POOL_DATA(pool) ((gpointer)(((GstMemPoolElement*)(pool)) + 1)) +#define GST_MEM_POOL_LINK(mem) ((GstMemPoolElement*)((guint8*)(mem) - sizeof (GstMemPoolElement))) + +#define USE_ASM + +/******************************************************* + * area size + * +-----------------------------------------+ + * pool size + * +------------+ + * + * !next!data... !next!data.... !next!data... + * ! ^ ! ^ ! + * +-------------+ +------------+ +---> NULL + * + */ +static gboolean +populate (GstMemPool *mem_pool) +{ + guint8 *area; + gint i; + + if (mem_pool->cleanup) + return FALSE; + + area = (guint8 *) g_malloc (mem_pool->area_size); + + for (i=0; i < mem_pool->area_size; i += mem_pool->pool_size) { + guint8 *areap = area + i; + + GST_MEM_POOL_AREA (areap) = (GstMemPoolElement *)area; + + if (mem_pool->alloc_func) { + mem_pool->alloc_func (mem_pool, GST_MEM_POOL_DATA (areap)); + } + + gst_mem_pool_free (mem_pool, GST_MEM_POOL_DATA (areap)); + } + + return TRUE; +} + + +GstMemPool* +gst_mem_pool_new (gchar* name, gint atom_size, gulong area_size, gint type, + GstMemPoolAllocFunc alloc_func, + GstMemPoolFreeFunc free_func) +{ + GstMemPool *mem_pool; + + g_return_val_if_fail (atom_size > 0, NULL); + g_return_val_if_fail (area_size >= atom_size, NULL); + + mem_pool = g_malloc (sizeof (GstMemPool)); + + mem_pool->pool_size = atom_size + sizeof (GstMemPoolElement); + area_size = (area_size/atom_size) * mem_pool->pool_size; + + mem_pool->name = g_strdup (name); + mem_pool->free = NULL; + mem_pool->cnt = 0; + mem_pool->atom_size = atom_size; + mem_pool->area_size = area_size; + mem_pool->cleanup = FALSE; + mem_pool->alloc_func = alloc_func; + mem_pool->free_func = free_func; + mem_pool->chunk_lock = g_mutex_new (); + + populate (mem_pool); + + return mem_pool; +} + +static gboolean +free_area (gpointer key, gpointer value, gpointer user_data) +{ + g_print ("free %p\n", key); + g_free (key); + + return TRUE; +} + +void +gst_mem_pool_destroy (GstMemPool *mem_pool) +{ + GHashTable *elements = g_hash_table_new (NULL, NULL); + gpointer data; + + mem_pool->cleanup = TRUE; + + data = gst_mem_pool_alloc (mem_pool); + while (data) { + GstMemPoolElement *elem = GST_MEM_POOL_LINK (data); + + g_hash_table_insert (elements, GST_MEM_POOL_AREA (elem), NULL); + + data = gst_mem_pool_alloc (mem_pool); + } + g_hash_table_foreach_remove (elements, free_area, NULL); + + g_hash_table_destroy (elements); + g_free (mem_pool->name); + g_free (mem_pool); +} + +gpointer +gst_mem_pool_alloc (GstMemPool *mem_pool) +{ + GstMemPoolElement *pool = NULL; + + g_return_val_if_fail (mem_pool != NULL, NULL); + +again: +#ifdef USE_ASM + __asm__ __volatile__ (" testl %%eax, %%eax \n\t" + " jz 20f \n" + "10: \t" + " movl (%%eax), %%ebx \n\t" + " movl %%edx, %%ecx \n\t" + " incl %%ecx \n\t" + POOL_LOCK "cmpxchg8b %1 \n\t" + " jz 20f \n\t" + " testl %%eax, %%eax \n\t" + " jnz 10b \n" + "20:\t" + :"=a" (pool) + :"m" (*mem_pool), "a" (mem_pool->free), "d" (mem_pool->cnt) + :"ecx", "ebx"); +#else + g_mutex_lock (mem_pool->chunk_lock); + if (mem_pool->free) { + pool = mem_pool->free; + mem_pool->free = pool->link; + } + g_mutex_unlock (mem_pool->chunk_lock); +#endif + + if (!pool) { + //g_print ("extending\n"); + if (populate (mem_pool)) + goto again; + else + return NULL; + } + return GST_MEM_POOL_DATA (pool); +} + +gpointer +gst_mem_pool_alloc0 (GstMemPool *mem_pool) +{ + gpointer mem = gst_mem_pool_alloc (mem_pool); + + if (mem) + memset (mem, 0, mem_pool->atom_size); + + return mem; +} + +void +gst_mem_pool_free (GstMemPool *mem_pool, gpointer mem) +{ + GstMemPoolElement *pool; + + g_return_if_fail (mem_pool != NULL); + g_return_if_fail (mem != NULL); + + pool = GST_MEM_POOL_LINK (mem); + +#ifdef USE_ASM + __asm__ __volatile__ ( "1: \t" + " movl %2, (%1) \n" + POOL_LOCK "cmpxchg %1, %0 \n\t" + " jnz 1b \n\t" + : + :"m" (*mem_pool), "r" (pool), "a" (mem_pool->free)); +#else + g_mutex_lock (mem_pool->chunk_lock); + pool->link = mem_pool->free; + mem_pool->free = pool; + g_mutex_unlock (mem_pool->chunk_lock); +#endif +} diff --git a/test/bufspeed/gstmempool.h b/test/bufspeed/gstmempool.h new file mode 100644 index 0000000000..307b768892 --- /dev/null +++ b/test/bufspeed/gstmempool.h @@ -0,0 +1,43 @@ +#include + +typedef struct _GstMemPool GstMemPool; +typedef struct _GstMemPoolElement GstMemPoolElement; + +typedef void (*GstMemPoolAllocFunc) (GstMemPool *pool, gpointer data); +typedef void (*GstMemPoolFreeFunc) (GstMemPool *pool, gpointer data); + +struct _GstMemPoolElement +{ + GstMemPoolElement *link; /* next cell in the lifo */ + GstMemPoolElement *area; +}; + +struct _GstMemPool +{ + volatile GstMemPoolElement *free; /* the first free element */ + volatile gulong cnt; /* used to avoid ABA problem */ + + gchar *name; + gulong area_size; + gulong pool_size; + gulong atom_size; + gboolean cleanup; + GstMemPoolAllocFunc alloc_func; + GstMemPoolFreeFunc free_func; + GMutex *chunk_lock; +}; + + +GstMemPool* gst_mem_pool_new (gchar *name, + gint atom_size, + gulong area_size, + gint type, + GstMemPoolAllocFunc alloc_func, + GstMemPoolFreeFunc free_func); + +void gst_mem_pool_destroy (GstMemPool *mem_pool); + +gpointer gst_mem_pool_alloc (GstMemPool *mem_pool); +gpointer gst_mem_pool_alloc0 (GstMemPool *mem_pool); +void gst_mem_pool_free (GstMemPool *mem_pool, + gpointer mem); diff --git a/test/bufspeed/test1.c b/test/bufspeed/test1.c new file mode 100644 index 0000000000..951af46067 --- /dev/null +++ b/test/bufspeed/test1.c @@ -0,0 +1,19 @@ +#include "gstbuffer.h" + +int +main (int argc, char *argv[]) +{ + GstBuffer *buf; + guint i; + + g_thread_init (NULL); + gtk_init (&argc, &argv); + _gst_buffer_initialize (); + + for (i=0; i<5000000; i++) { + buf = gst_buffer_new (); + gst_buffer_unref (buf); + } + + return 0; +} diff --git a/test/bufspeed/test2.c b/test/bufspeed/test2.c new file mode 100644 index 0000000000..616ad1f554 --- /dev/null +++ b/test/bufspeed/test2.c @@ -0,0 +1,19 @@ +#include + +int +main (int argc, char *argv[]) +{ + GstBuffer *buf; + guint i; + + g_thread_init (NULL); + gtk_init (&argc, &argv); + _gst_buffer_initialize (); + + for (i=0; i<5000000; i++) { + buf = gst_buffer_new (); + gst_buffer_unref (buf); + } + + return 0; +}