reusing work, though memuse seems high?
This commit is contained in:
John Cupitt 2013-12-13 16:51:01 +00:00
parent 82df44f9d6
commit bfcd50acf2
5 changed files with 124 additions and 57 deletions

View File

@ -27,6 +27,7 @@
- added vipsprofile, visualises --vips-profile output - added vipsprofile, visualises --vips-profile output
- auto-vectorization-friendly inner loops - auto-vectorization-friendly inner loops
- added vips::init() and vips::shutdown() to C++ API - added vips::init() and vips::shutdown() to C++ API
- reuse pixel buffers on sharing
20/11/13 started 7.36.5 20/11/13 started 7.36.5
- better cache sizing in unbuffered sequential mode - better cache sizing in unbuffered sequential mode

View File

@ -127,6 +127,7 @@ int vips_mapfilerw( VipsImage * );
int vips_remapfilerw( VipsImage * ); int vips_remapfilerw( VipsImage * );
void vips__buffer_init( void ); void vips__buffer_init( void );
void vips__buffer_shutdown( void );
void vips__copy_4byte( int swap, unsigned char *to, unsigned char *from ); void vips__copy_4byte( int swap, unsigned char *to, unsigned char *from );
void vips__copy_2byte( gboolean swap, unsigned char *to, unsigned char *from ); void vips__copy_2byte( gboolean swap, unsigned char *to, unsigned char *from );

View File

@ -87,6 +87,8 @@ void vips_window_print( VipsWindow *window );
typedef struct { typedef struct {
GHashTable *hash; /* Hash to VipsBufferCacheList* */ GHashTable *hash; /* Hash to VipsBufferCacheList* */
GThread *thread; /* Just for sanity checking */ GThread *thread; /* Just for sanity checking */
GSList *reserve; /* VipsBuffer kept in reserve */
int n_reserve; /* Number in reserve */
} VipsBufferCache; } VipsBufferCache;
/* Per-image buffer cache. Hash to this from VipsBufferCache. /* Per-image buffer cache. Hash to this from VipsBufferCache.

View File

@ -75,17 +75,44 @@ static int buffer_cache_n = 0;
static GPrivate *thread_buffer_cache_key = NULL; static GPrivate *thread_buffer_cache_key = NULL;
static void
vips_buffer_free( VipsBuffer *buffer )
{
vips_tracked_free( buffer->buf );
buffer->bsize = 0;
g_free( buffer );
#ifdef DEBUG
g_mutex_lock( vips__global_lock );
g_assert( g_slist_find( vips__buffers_all, buffer ) );
vips__buffers_all = g_slist_remove( vips__buffers_all, buffer );
printf( "%d buffers in vips\n", g_slist_length( vips__buffers_all ) );
g_mutex_unlock( vips__global_lock );
#endif /*DEBUG*/
}
static void static void
buffer_cache_free( VipsBufferCache *cache ) buffer_cache_free( VipsBufferCache *cache )
{ {
GSList *p;
#ifdef DEBUG_CREATE #ifdef DEBUG_CREATE
buffer_cache_n -= 1; buffer_cache_n -= 1;
printf( "buffer_cache_free: freeing cache %p on thread %p\n", printf( "vips__buffer_cache_free: freeing cache %p on thread %p\n",
cache, g_thread_self() ); cache, g_thread_self() );
printf( "\t(%d cachees left)\n", buffer_cache_n ); printf( "\t(%d caches left)\n", buffer_cache_n );
#endif /*DEBUG_CREATE*/ #endif /*DEBUG_CREATE*/
for( p = cache->reserve; p; p = p->next ) {
VipsBuffer *buffer = (VipsBuffer *) p->data;
vips_buffer_free( buffer );
}
VIPS_FREEF( g_slist_free, cache->reserve );
VIPS_FREEF( g_hash_table_destroy, cache->hash ); VIPS_FREEF( g_hash_table_destroy, cache->hash );
VIPS_FREE( cache ); VIPS_FREE( cache );
} }
@ -104,7 +131,7 @@ buffer_cache_list_free( VipsBufferCacheList *cache_list )
buffer->done = FALSE; buffer->done = FALSE;
} }
g_slist_free( cache_list->buffers ); VIPS_FREEF( g_slist_free, cache_list->buffers );
g_free( cache_list ); g_free( cache_list );
} }
@ -122,7 +149,7 @@ buffer_cache_list_new( VipsBufferCache *cache, VipsImage *im )
#ifdef DEBUG_CREATE #ifdef DEBUG_CREATE
printf( "buffer_cache_list_new: new cache %p for thread %p\n", printf( "buffer_cache_list_new: new cache %p for thread %p\n",
cache, g_thread_self() ); cache, g_thread_self() );
printf( "\t(%d cachees now)\n", buffer_cache_n ); printf( "\t(%d caches now)\n", buffer_cache_n );
#endif /*DEBUG_CREATE*/ #endif /*DEBUG_CREATE*/
return( cache_list ); return( cache_list );
@ -137,13 +164,15 @@ buffer_cache_new( void )
cache->hash = g_hash_table_new_full( g_direct_hash, g_direct_equal, cache->hash = g_hash_table_new_full( g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) buffer_cache_list_free ); NULL, (GDestroyNotify) buffer_cache_list_free );
cache->thread = g_thread_self(); cache->thread = g_thread_self();
cache->reserve = NULL;
cache->n_reserve = 0;
#ifdef DEBUG_CREATE #ifdef DEBUG_CREATE
buffer_cache_n += 1; buffer_cache_n += 1;
printf( "buffer_cache_new: new cache %p for thread %p\n", printf( "buffer_cache_new: new cache %p for thread %p\n",
cache, g_thread_self() ); cache, g_thread_self() );
printf( "\t(%d cachees now)\n", buffer_cache_n ); printf( "\t(%d caches now)\n", buffer_cache_n );
#endif /*DEBUG_CREATE*/ #endif /*DEBUG_CREATE*/
return( cache ); return( cache );
@ -253,6 +282,8 @@ vips_buffer_unref( VipsBuffer *buffer )
buffer->ref_count -= 1; buffer->ref_count -= 1;
if( buffer->ref_count == 0 ) { if( buffer->ref_count == 0 ) {
VipsBufferCache *cache = buffer_cache_get();
#ifdef DEBUG #ifdef DEBUG
if( !buffer->done ) if( !buffer->done )
printf( "vips_buffer_unref: buffer was not done\n" ); printf( "vips_buffer_unref: buffer was not done\n" );
@ -260,60 +291,24 @@ vips_buffer_unref( VipsBuffer *buffer )
vips_buffer_undone( buffer ); vips_buffer_undone( buffer );
buffer->im = NULL; /* Place on this thread's reserve list for reuse.
vips_tracked_free( buffer->buf ); */
buffer->bsize = 0; if( cache->n_reserve < 5 ) {
g_free( buffer ); cache->reserve =
g_slist_prepend( cache->reserve, buffer );
cache->n_reserve += 1;
#ifdef DEBUG buffer->done = FALSE;
g_mutex_lock( vips__global_lock ); buffer->cache = NULL;
g_assert( g_slist_find( vips__buffers_all, buffer ) ); buffer->im = NULL;
vips__buffers_all = g_slist_remove( vips__buffers_all, buffer ); buffer->area.width = 0;
printf( "%d buffers in vips\n", buffer->area.height = 0;
g_slist_length( vips__buffers_all ) ); }
g_mutex_unlock( vips__global_lock ); else
#endif /*DEBUG*/ vips_buffer_free( buffer );
} }
} }
/* Make a new buffer.
*/
VipsBuffer *
vips_buffer_new( VipsImage *im, VipsRect *area )
{
VipsBuffer *buffer;
buffer = g_new( VipsBuffer, 1 );
buffer->ref_count = 1;
buffer->im = im;
buffer->area = *area;
buffer->done = FALSE;
buffer->cache = NULL;
buffer->bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL( im ) *
area->width * area->height;
if( !(buffer->buf = vips_tracked_malloc( buffer->bsize )) ) {
vips_buffer_unref( buffer );
return( NULL );
}
#ifdef DEBUG
printf( "** vips_buffer_new: left = %d, top = %d, "
"width = %d, height = %d (%p)\n",
buffer->area.left, buffer->area.top,
buffer->area.width, buffer->area.height,
buffer );
#endif /*DEBUG*/
#ifdef DEBUG
g_mutex_lock( vips__global_lock );
vips__buffers_all = g_slist_prepend( vips__buffers_all, buffer );
printf( "%d buffers in vips\n", g_slist_length( vips__buffers_all ) );
g_mutex_unlock( vips__global_lock );
#endif /*DEBUG*/
return( buffer );
}
static int static int
buffer_move( VipsBuffer *buffer, VipsRect *area ) buffer_move( VipsBuffer *buffer, VipsRect *area )
{ {
@ -329,9 +324,10 @@ buffer_move( VipsBuffer *buffer, VipsRect *area )
new_bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL( im ) * new_bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL( im ) *
area->width * area->height; area->width * area->height;
if( buffer->bsize < new_bsize ) { if( buffer->bsize < new_bsize ||
!buffer->buf ) {
buffer->bsize = new_bsize; buffer->bsize = new_bsize;
vips_tracked_free( buffer->buf ); VIPS_FREEF( vips_tracked_free, buffer->buf );
if( !(buffer->buf = vips_tracked_malloc( buffer->bsize )) ) if( !(buffer->buf = vips_tracked_malloc( buffer->bsize )) )
return( -1 ); return( -1 );
} }
@ -339,6 +335,60 @@ buffer_move( VipsBuffer *buffer, VipsRect *area )
return( 0 ); return( 0 );
} }
/* Make a new buffer.
*/
VipsBuffer *
vips_buffer_new( VipsImage *im, VipsRect *area )
{
VipsBufferCache *cache = buffer_cache_get();
VipsBuffer *buffer;
if( cache->reserve ) {
buffer = (VipsBuffer *) cache->reserve->data;
cache->reserve = g_slist_remove( cache->reserve, buffer );
cache->n_reserve -= 1;
buffer->ref_count = 1;
buffer->im = im;
buffer->done = FALSE;
buffer->cache = NULL;
}
else {
buffer = g_new0( VipsBuffer, 1 );
buffer->ref_count = 1;
buffer->im = im;
buffer->done = FALSE;
buffer->cache = NULL;
buffer->buf = NULL;
buffer->bsize = 0;
#ifdef DEBUG
printf( "** vips_buffer_new: left = %d, top = %d, "
"width = %d, height = %d (%p)\n",
buffer->area.left, buffer->area.top,
buffer->area.width, buffer->area.height,
buffer );
#endif /*DEBUG*/
#ifdef DEBUG
g_mutex_lock( vips__global_lock );
vips__buffers_all =
g_slist_prepend( vips__buffers_all, buffer );
printf( "%d buffers in vips\n",
g_slist_length( vips__buffers_all ) );
g_mutex_unlock( vips__global_lock );
#endif /*DEBUG*/
}
if( buffer_move( buffer, area ) ) {
vips_buffer_free( buffer );
return( NULL );
}
return( buffer );
}
/* Find an existing buffer that encloses area and return a ref. /* Find an existing buffer that encloses area and return a ref.
*/ */
static VipsBuffer * static VipsBuffer *
@ -479,3 +529,14 @@ vips__buffer_init( void )
(GDestroyNotify) buffer_cache_free ); (GDestroyNotify) buffer_cache_free );
#endif #endif
} }
void
vips__buffer_shutdown( void )
{
VipsBufferCache *cache;
if( (cache = g_private_get( thread_buffer_cache_key )) ) {
buffer_cache_free( cache );
g_private_set( thread_buffer_cache_key, NULL );
}
}

View File

@ -398,6 +398,8 @@ vips_shutdown( void )
vips__thread_profile_detach(); vips__thread_profile_detach();
vips__thread_profile_stop(); vips__thread_profile_stop();
vips__buffer_shutdown();
/* In dev releases, always show leaks. But not more than once, it's /* In dev releases, always show leaks. But not more than once, it's
* annoying. * annoying.
*/ */