more cache fixups
This commit is contained in:
parent
359ce3208e
commit
edb28af67c
@ -54,6 +54,16 @@ typedef gboolean (*VipsOperationBuildFn)( VipsObject * );
|
|||||||
typedef struct _VipsOperation {
|
typedef struct _VipsOperation {
|
||||||
VipsObject parent_instance;
|
VipsObject parent_instance;
|
||||||
|
|
||||||
|
/* When we added this oepration to cache .. used to find LRU for
|
||||||
|
* flush.
|
||||||
|
*/
|
||||||
|
int time;
|
||||||
|
|
||||||
|
/* Keep the hash here.
|
||||||
|
*/
|
||||||
|
guint hash;
|
||||||
|
gboolean found_hash;
|
||||||
|
|
||||||
} VipsOperation;
|
} VipsOperation;
|
||||||
|
|
||||||
typedef struct _VipsOperationClass {
|
typedef struct _VipsOperationClass {
|
||||||
|
@ -36,13 +36,11 @@
|
|||||||
|
|
||||||
listen for invalidate
|
listen for invalidate
|
||||||
|
|
||||||
keep operations alive? at the moment
|
can we estimate the resource needs of operations and drop very
|
||||||
|
expensive ones first?
|
||||||
|
|
||||||
vips_min( im, &d, NULL );
|
get vips_malloc()/_free() to track current usage, check that
|
||||||
|
as well as hash table size when looking for cache overflow
|
||||||
will never get cached since it produces no persistent output ... we'd
|
|
||||||
need to keep a ref to the operation in the cache and only drop after a
|
|
||||||
few seconds or if we have more than 10,000 cached operations
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -71,11 +69,17 @@
|
|||||||
#include <dmalloc.h>
|
#include <dmalloc.h>
|
||||||
#endif /*WITH_DMALLOC*/
|
#endif /*WITH_DMALLOC*/
|
||||||
|
|
||||||
/* Hide the hash in the object under this name.
|
/* Max cache size.
|
||||||
*/
|
*/
|
||||||
#define VIPS_HASH_NAME "vips-hash"
|
static int vips_cache_max = 10000;
|
||||||
|
|
||||||
static GHashTable *vips_object_cache = NULL;
|
/* Hold a ref to all "recent" operations.
|
||||||
|
*/
|
||||||
|
static GHashTable *vips_cache_table = NULL;
|
||||||
|
|
||||||
|
/* A 'time' counter: increment on all cache ops. Use this to detect LRU.
|
||||||
|
*/
|
||||||
|
static int vips_cache_time = 0;
|
||||||
|
|
||||||
/* generic is the general type of the value. For example, the value could be
|
/* generic is the general type of the value. For example, the value could be
|
||||||
* held in a GParamSpec allowing OBJECT, but the value could be of type
|
* held in a GParamSpec allowing OBJECT, but the value could be of type
|
||||||
@ -312,30 +316,29 @@ vips_object_hash_arg( VipsObject *object,
|
|||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find a hash from the input arguments to a vipsobject.
|
/* Find a hash from the input arguments to a VipsOperstion.
|
||||||
*/
|
*/
|
||||||
static unsigned int
|
static unsigned int
|
||||||
vips_object_hash( VipsObject *object )
|
vips_operation_hash( VipsOperation *operation )
|
||||||
{
|
{
|
||||||
unsigned int hash;
|
if( !operation->found_hash ) {
|
||||||
|
guint hash;
|
||||||
|
|
||||||
hash = GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( object ),
|
/* Include the operation type in the hash.
|
||||||
VIPS_HASH_NAME ) );
|
*/
|
||||||
|
hash = (guint) G_OBJECT_TYPE( operation );
|
||||||
if( !hash ) {
|
(void) vips_argument_map( VIPS_OBJECT( operation ),
|
||||||
hash = 0;
|
|
||||||
(void) vips_argument_map( object,
|
|
||||||
vips_object_hash_arg, &hash, NULL );
|
vips_object_hash_arg, &hash, NULL );
|
||||||
|
|
||||||
/* Make sure we can't have a zero hash value.
|
/* Make sure we can't have a zero hash value.
|
||||||
*/
|
*/
|
||||||
hash |= 1;
|
hash |= 1;
|
||||||
|
|
||||||
g_object_set_data( G_OBJECT( object ),
|
operation->hash = hash;
|
||||||
VIPS_HASH_NAME, GUINT_TO_POINTER( hash ) );
|
operation->found_hash = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return( hash );
|
return( operation->hash );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
@ -375,53 +378,17 @@ vips_object_equal_arg( VipsObject *object,
|
|||||||
/* Are two objects equal, ie. have the same inputs.
|
/* Are two objects equal, ie. have the same inputs.
|
||||||
*/
|
*/
|
||||||
static gboolean
|
static gboolean
|
||||||
vips_object_equal( VipsObject *a, VipsObject *b )
|
vips_operation_equal( VipsOperation *a, VipsOperation *b )
|
||||||
{
|
{
|
||||||
if( G_OBJECT_TYPE( a ) == G_OBJECT_TYPE( b ) &&
|
if( G_OBJECT_TYPE( a ) == G_OBJECT_TYPE( b ) &&
|
||||||
vips_object_hash( a ) == vips_object_hash( b ) &&
|
vips_operation_hash( a ) == vips_operation_hash( b ) &&
|
||||||
!vips_argument_map( a, vips_object_equal_arg, b, NULL ) )
|
!vips_argument_map( VIPS_OBJECT( a ),
|
||||||
|
vips_object_equal_arg, b, NULL ) )
|
||||||
return( TRUE );
|
return( TRUE );
|
||||||
|
|
||||||
return( FALSE );
|
return( FALSE );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
|
||||||
vips_object_validate_arg( VipsObject *object,
|
|
||||||
GParamSpec *pspec,
|
|
||||||
VipsArgumentClass *argument_class,
|
|
||||||
VipsArgumentInstance *argument_instance,
|
|
||||||
void *a, void *b )
|
|
||||||
{
|
|
||||||
if( (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
|
|
||||||
(argument_class->flags & VIPS_ARGUMENT_OUTPUT) &&
|
|
||||||
argument_instance->assigned ) {
|
|
||||||
GType type = G_PARAM_SPEC_VALUE_TYPE( pspec );
|
|
||||||
const char *name = g_param_spec_get_name( pspec );
|
|
||||||
GValue value = { 0, };
|
|
||||||
gboolean null;
|
|
||||||
|
|
||||||
g_value_init( &value, type );
|
|
||||||
g_object_get_property( G_OBJECT( object ), name, &value );
|
|
||||||
null = vips_value_is_null( pspec, &value );
|
|
||||||
g_value_unset( &value );
|
|
||||||
|
|
||||||
if( null )
|
|
||||||
return( object );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( NULL );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that an object is still valid. It may have lost some of its
|
|
||||||
* output objects but still be alive.
|
|
||||||
*/
|
|
||||||
static gboolean
|
|
||||||
vips_object_validate( VipsObject *object )
|
|
||||||
{
|
|
||||||
return( !vips_argument_map( object,
|
|
||||||
vips_object_validate_arg, NULL, NULL ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
vips_object_ref_arg( VipsObject *object,
|
vips_object_ref_arg( VipsObject *object,
|
||||||
GParamSpec *pspec,
|
GParamSpec *pspec,
|
||||||
@ -454,59 +421,49 @@ vips_object_ref_outputs( VipsObject *object )
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vips_cache_remove( void *data, GObject *object )
|
vips_operation_touch( VipsOperation *operation )
|
||||||
{
|
{
|
||||||
VIPS_DEBUG_MSG( "vips_cache_remove: removing %p\n", object );
|
vips_cache_time += 1;
|
||||||
|
operation->time = vips_cache_time;
|
||||||
/* Can this get triggered more than once for an object? Unclear.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if( !g_hash_table_remove( vips_object_cache, object ) )
|
|
||||||
g_assert( 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look up an object in the cache. If we get a hit, unref the new one, ref the
|
/* Look up an object in the cache. If we get a hit, unref the new one, ref the
|
||||||
* old one and return that.
|
* old one and return that.
|
||||||
*
|
*
|
||||||
* If we miss, build, add this object and weakref so we
|
* If we miss, build and add this object.
|
||||||
* will drop it when it's unreffed.
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vips_object_build_cache( VipsObject **object )
|
vips_operation_build_cache( VipsOperation **operation )
|
||||||
{
|
{
|
||||||
VipsObject *hit;
|
VipsOperation *hit;
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_object_build_cache: %p\n", *object );
|
VIPS_DEBUG_MSG( "vips_operation_build_cache: %p\n", *object );
|
||||||
|
|
||||||
if( !vips_object_cache )
|
if( !vips_cache_table )
|
||||||
vips_object_cache = g_hash_table_new(
|
vips_cache_table = g_hash_table_new(
|
||||||
(GHashFunc) vips_object_hash,
|
(GHashFunc) vips_operation_hash,
|
||||||
(GEqualFunc) vips_object_equal );
|
(GEqualFunc) vips_operation_equal );
|
||||||
|
|
||||||
if( (hit = g_hash_table_lookup( vips_object_cache, *object )) ) {
|
if( (hit = g_hash_table_lookup( vips_cache_table, *operation )) ) {
|
||||||
VIPS_DEBUG_MSG( "\thit %p\n", hit );
|
VIPS_DEBUG_MSG( "\thit %p\n", hit );
|
||||||
if( vips_object_validate( hit ) ) {
|
|
||||||
g_object_unref( *object );
|
|
||||||
g_object_ref( hit );
|
|
||||||
vips_object_ref_outputs( hit );
|
|
||||||
*object = hit;
|
|
||||||
|
|
||||||
return( 0 );
|
g_object_unref( *operation );
|
||||||
}
|
g_object_ref( hit );
|
||||||
|
vips_object_ref_outputs( VIPS_OBJECT( hit ) );
|
||||||
VIPS_DEBUG_MSG( "\tvalidation failed, dropping\n" );
|
vips_operation_touch( hit );
|
||||||
g_hash_table_remove( vips_object_cache, hit );
|
*operation = hit;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
VIPS_DEBUG_MSG( "\tmiss, build and add\n" );
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "\tmiss, building\n" );
|
if( vips_object_build( VIPS_OBJECT( *operation ) ) )
|
||||||
|
return( -1 );
|
||||||
|
g_object_ref( *operation );
|
||||||
|
vips_object_ref_outputs( VIPS_OBJECT( *operation ) );
|
||||||
|
vips_operation_touch( *operation );
|
||||||
|
|
||||||
if( vips_object_build( *object ) )
|
g_hash_table_insert( vips_cache_table, *operation, *operation );
|
||||||
return( -1 );
|
}
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "\tadding\n" );
|
|
||||||
|
|
||||||
g_hash_table_insert( vips_object_cache, *object, *object );
|
|
||||||
g_object_weak_ref( G_OBJECT( *object ), vips_cache_remove, NULL );
|
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user