[postgis-commits] svn - r3056 - trunk/lwgeom
postgis-commits at postgis.refractions.net
postgis-commits at postgis.refractions.net
Thu Oct 2 16:53:37 PDT 2008
Author: pramsey
Date: 2008-10-02 16:53:37 -0700 (Thu, 02 Oct 2008)
New Revision: 3056
Modified:
trunk/lwgeom/lwgeom_geos_c.c
trunk/lwgeom/lwgeom_transform.c
Log:
Rework prepared geometry handling to look more like the implementation of
PJ caching in transform.
Modified: trunk/lwgeom/lwgeom_geos_c.c
===================================================================
--- trunk/lwgeom/lwgeom_geos_c.c 2008-10-02 18:52:10 UTC (rev 3055)
+++ trunk/lwgeom/lwgeom_geos_c.c 2008-10-02 23:53:37 UTC (rev 3056)
@@ -4,7 +4,9 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "fmgr.h"
+#include "miscadmin.h"
+
#include "lwgeom_pg.h"
#include "liblwgeom.h"
#include "profile.h"
@@ -3511,12 +3513,17 @@
** of geometry in a special MemoryContext for re-used by future function
** invocations.
**
+** By creating a memory context to hold the GEOS PreparedGeometry and Geometry
+** and making it a child of the fmgr memory context, we can get the memory held
+** by the GEOS objects released when the memory context delete callback is
+** invoked by the parent context.
+**
*********************************************************************************/
-Datum containsPrepared(PG_FUNCTION_ARGS);
-Datum containsProperlyPrepared(PG_FUNCTION_ARGS);
-Datum coversPrepared(PG_FUNCTION_ARGS);
-Datum intersectsPrepared(PG_FUNCTION_ARGS);
+#include "utils/memutils.h"
+#include "executor/spi.h"
+#include "access/hash.h"
+#include "utils/hsearch.h"
/*
** GEOS prepared geometry is only available from GEOS 3.1 onwards
@@ -3526,92 +3533,357 @@
#warning COMPILING PREPARED GEOMETRY
#endif
-#ifdef PREPARED_GEOM
+/* Prepared geometry function prototypes */
+Datum containsPrepared(PG_FUNCTION_ARGS);
+Datum containsProperlyPrepared(PG_FUNCTION_ARGS);
+Datum coversPrepared(PG_FUNCTION_ARGS);
+Datum intersectsPrepared(PG_FUNCTION_ARGS);
+/*
+** Cache structure. Keys are unique for a unique geometry, usually the row
+** primary key is passed in. This avoid the need to do a memcmp to test
+** for geometry equality at each function invocation. The argnum gives
+** the number of function arguments we are caching. Intersects requires that
+** both arguments be checked for cacheability, while Contains only requires
+** that the containing argument be checked. Both the Geometry and the
+** PreparedGeometry have to be cached, because the PreparedGeometry
+** contains a reference to the geometry.
+*/
typedef struct
{
int32 key1;
int32 key2;
int32 argnum;
- GEOSPreparedGeometry *prepared_geom;
- GEOSGeometry *geom;
+ const GEOSPreparedGeometry* prepared_geom;
+ const GEOSGeometry* geom;
+ MemoryContext context;
+} PrepGeomCache;
+
+/*
+** Backend prepared hash table
+**
+** The memory context call-backs use a MemoryContext as the parameter
+** so we need to map that over to actual references to GEOS objects to
+** delete.
+**
+** This hash table stores a key/value pair of MemoryContext/Geom* objects.
+*/
+static HTAB* PrepGeomHash = NULL;
+
+#define PREPARED_BACKEND_HASH_SIZE 32
+
+typedef struct
+{
+ MemoryContext context;
+ const GEOSPreparedGeometry* prepared_geom;
+ const GEOSGeometry* geom;
+} PrepGeomHashEntry;
+
+/* Utility function to pull or prepare the current cache */
+PrepGeomCache *GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *serialized_geom1, PG_LWGEOM *serialized_geom2, int32 key1, int32 key2);
+
+/* Memory context hash table function prototypes */
+uint32 mcxt_ptr_hasha(const void *key, Size keysize);
+static HTAB* CreatePrepGeomHash(void);
+static void AddPrepGeomHashEntry(PrepGeomHashEntry pghe);
+static PrepGeomHashEntry *GetPrepGeomHashEntry(MemoryContext mcxt);
+static void DeletePrepGeomHashEntry(MemoryContext mcxt);
+
+
+/* Memory context cache function prototypes */
+static void PreparedCacheInit(MemoryContext context);
+static void PreparedCacheReset(MemoryContext context);
+static void PreparedCacheDelete(MemoryContext context);
+static bool PreparedCacheIsEmpty(MemoryContext context);
+static void PreparedCacheStats(MemoryContext context, int level);
+#ifdef MEMORY_CONTEXT_CHECKING
+static void PreparedCacheCheck(MemoryContext context);
+#endif
+
+/* Memory context definition must match the current version of PostgreSQL */
+static MemoryContextMethods PreparedCacheContextMethods = {
+ NULL,
+ NULL,
+ NULL,
+ PreparedCacheInit,
+ PreparedCacheReset,
+ PreparedCacheDelete,
+ NULL,
+ PreparedCacheIsEmpty,
+ PreparedCacheStats
+#ifdef MEMORY_CONTEXT_CHECKING
+ , PreparedCacheCheck
+#endif
+};
+
+static void
+PreparedCacheInit(MemoryContext context)
+{
+ /*
+ * Do nothing as the cache is initialised when the transform()
+ * function is first called
+ */
}
-PREPARED_GEOM_CACHE;
+static void
+PreparedCacheDelete(MemoryContext context)
+{
+ PrepGeomHashEntry* pghe;
+ /* Lookup the hash entry pointer in the global hash table so we can free it */
+ pghe = GetPrepGeomHashEntry(context);
+
+ if (!pghe)
+ elog(ERROR, "PreparedCacheDelete: Trying to delete non-existant hash entry object with MemoryContext key (%p)", (void *)context);
+
+ LWDEBUGF(3, "deleting geom object (%p) and prepared geom object (%p) with MemoryContext key (%p)", pghe.geom, pghe.prepared_geom, context);
+
+ /* Free them */
+ if( pghe->prepared_geom )
+ GEOSPreparedGeom_destroy( pghe->prepared_geom );
+ if( pghe->geom )
+ GEOSGeom_destroy( pghe->geom );
+
+ /* Remove the hash entry as it is no longer needed */
+ DeletePrepGeomHashEntry(context);
+}
+
+static void
+PreparedCacheReset(MemoryContext context)
+{
+ /*
+ * Do nothing, but we must supply a function since this call is mandatory according to tgl
+ * (see postgis-devel archives July 2007)
+ */
+}
+
+static bool
+PreparedCacheIsEmpty(MemoryContext context)
+{
+ /*
+ * Always return false since this call is mandatory according to tgl
+ * (see postgis-devel archives July 2007)
+ */
+ return FALSE;
+}
+
+static void
+PreparedCacheStats(MemoryContext context, int level)
+{
+ /*
+ * Simple stats display function - we must supply a function since this call is mandatory according to tgl
+ * (see postgis-devel archives July 2007)
+ */
+
+ fprintf(stderr, "%s: Prepared context\n", context->name);
+}
+
+#ifdef MEMORY_CONTEXT_CHECKING
+static void
+PreparedCacheCheck(MemoryContext context)
+{
+ /*
+ * Do nothing - stub required for when PostgreSQL is compiled
+ * with MEMORY_CONTEXT_CHECKING defined
+ */
+}
+#endif
+
+/* TODO: put this in common are for both transform and prepared
+** mcxt_ptr_hash
+** Build a key from a pointer and a size value.
+*/
+uint32
+mcxt_ptr_hasha(const void *key, Size keysize)
+{
+ uint32 hashval;
+
+ hashval = DatumGetUInt32(hash_any(key, keysize));
+
+ return hashval;
+}
+
+static HTAB*
+CreatePrepGeomHash(void)
+{
+ HASHCTL ctl;
+
+ ctl.keysize = sizeof(MemoryContext);
+ ctl.entrysize = sizeof(PrepGeomHashEntry);
+ ctl.hash = mcxt_ptr_hasha;
+
+ return hash_create("PostGIS Prepared Geometry Backend MemoryContext Hash", PREPARED_BACKEND_HASH_SIZE, &ctl, (HASH_ELEM | HASH_FUNCTION));
+}
+
+static void
+AddPrepGeomHashEntry(PrepGeomHashEntry pghe)
+{
+ bool found;
+ void **key;
+ PrepGeomHashEntry *he;
+
+ /* The hash key is the MemoryContext pointer */
+ key = (void *)&(pghe.context);
+
+ he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_ENTER, &found);
+ if (!found)
+ {
+ /* Insert the entry into the new hash element */
+ he->context = pghe.context;
+ he->geom = pghe.geom;
+ he->prepared_geom = pghe.prepared_geom;
+ }
+ else
+ {
+ elog(ERROR, "AddPrepGeomHashEntry: This memory context is already in use! (%p)", (void *)pghe.context);
+ }
+}
+
+static PrepGeomHashEntry*
+GetPrepGeomHashEntry(MemoryContext mcxt)
+{
+ void **key;
+ PrepGeomHashEntry *he;
+
+ /* The hash key is the MemoryContext pointer */
+ key = (void *)&mcxt;
+
+ /* Return the projection object from the hash */
+ he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_FIND, NULL);
+
+ return he;
+}
+
+
+static void
+DeletePrepGeomHashEntry(MemoryContext mcxt)
+{
+ void **key;
+ PrepGeomHashEntry *he;
+
+ /* The hash key is the MemoryContext pointer */
+ key = (void *)&mcxt;
+
+ /* Delete the projection object from the hash */
+ he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_REMOVE, NULL);
+
+ he->prepared_geom = NULL;
+ he->geom = NULL;
+
+ if (!he)
+ elog(ERROR, "DeletePrepGeomHashEntry: There was an error removing the geometry object from this MemoryContext (%p)", (void *)mcxt);
+}
+
/*
- * get cached prepared geometry for geom1
- * only geom1 is potentially prepared as only
- * the first arg of the prepared predicates CAN be prepared
- * briefly, the following does:
- *
- * get cache
- * if cache not exist
- * create cache
- * key into cache
- *
- * else if key matches cached key
- * if cached prepared not exist
- * geom1 prepared into cache
- *
- * else
- * key into cache
- * clear prepared cache
- */
-PREPARED_GEOM_CACHE * get_prepared_geometry_cache(
- PREPARED_GEOM_CACHE *cache,
- PG_LWGEOM *serialized_geom1, PG_LWGEOM *serialized_geom2,
- int32 key1, int32 key2 )
+** GetPrepGeomCache
+**
+** Pull the current prepared geometry from the cache or make
+** one if there is not one available. Only prepare geometry
+** if we are seeing a key for the second time. That way rapidly
+** cycling keys don't cause too much preparing.
+**
+*/
+#ifdef PREPARED_GEOM
+PrepGeomCache*
+GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *serialized_geom1, PG_LWGEOM *serialized_geom2, int32 key1, int32 key2)
{
+ PrepGeomCache* cache = fcinfo->flinfo->fn_extra;
- if ( !cache )
+ if (!PrepGeomHash)
+ PrepGeomHash = CreatePrepGeomHash();
+
+ if ( cache == NULL)
{
- cache = lwalloc( sizeof(PREPARED_GEOM_CACHE) );
+ PrepGeomHashEntry pghe;
+ MemoryContext old_context;
+
+ old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+ cache = palloc(sizeof(PrepGeomCache));
+ MemoryContextSwitchTo(old_context);
+
cache->prepared_geom = 0;
cache->geom = 0;
cache->argnum = 0;
cache->key1 = 0;
cache->key2 = 0;
- LWDEBUGF(3, "get_prepared_geometry_cache: creating cache: %x", cache);
+ cache->context = MemoryContextCreate(T_AllocSetContext, 8192,
+ &PreparedCacheContextMethods,
+ fcinfo->flinfo->fn_mcxt,
+ "PostGIS Prepared Geometry Context");
+
+ LWDEBUGF(3, "GetPrepGeomCache: creating cache: %x", cache);
+
+ pghe.context = cache->context;
+ pghe.geom = 0;
+ pghe.prepared_geom = 0;
+ AddPrepGeomHashEntry( pghe );
+
+ fcinfo->flinfo->fn_extra = cache;
+
+ LWDEBUGF(3, "GetPrepGeomCache: adding context to hash: %x", cache);
}
else if ( cache->key1 == key1 )
{
if ( !cache->prepared_geom )
{
+ PrepGeomHashEntry* pghe;
+
GEOSGeom g = POSTGIS2GEOS( serialized_geom1 );
cache->geom = g;
cache->prepared_geom = GEOSPrepare( g );
cache->argnum = 1;
- LWDEBUG(3, "get_prepared_geometry_cache: preparing obj in argument 1");
+ LWDEBUG(3, "GetPrepGeomCache: preparing obj in argument 1");
+
+ pghe = GetPrepGeomHashEntry(cache->context);
+ pghe->geom = cache->geom;
+ pghe->prepared_geom = cache->prepared_geom;
+ LWDEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 1");
+
}
else
{
- LWDEBUG(3, "get_prepared_geometry_cache: prepared obj 1 in cache");
+ LWDEBUG(3, "GetPrepGeomCache: prepared obj 1 in cache");
}
}
else if ( key2 && cache->key2 == key2 )
{
if ( !cache->prepared_geom )
{
+ PrepGeomHashEntry* pghe;
+
GEOSGeom g = POSTGIS2GEOS( serialized_geom2 );
cache->geom = g;
cache->prepared_geom = GEOSPrepare( g );
cache->argnum = 2;
- LWDEBUG(3, "get_prepared_geometry_cache: preparing obj in argument 2");
+ LWDEBUG(3, "GetPrepGeomCache: preparing obj in argument 2");
+
+ pghe = GetPrepGeomHashEntry(cache->context);
+ pghe->geom = cache->geom;
+ pghe->prepared_geom = cache->prepared_geom;
+ LWDEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 2");
+
}
else
{
- LWDEBUG(3, "get_prepared_geometry_cache: prepared obj 2 in cache");
+ LWDEBUG(3, "GetPrepGeomCache: prepared obj 2 in cache");
}
}
else if ( cache->prepared_geom )
{
- LWDEBUG(3, "get_prepared_geometry_cache: obj NOT in cache");
+ PrepGeomHashEntry* pghe;
+
+ pghe = GetPrepGeomHashEntry(cache->context);
+ pghe->geom = 0;
+ pghe->prepared_geom = 0;
+
+ LWDEBUG(3, "GetPrepGeomCache: obj NOT in cache");
GEOSPreparedGeom_destroy( cache->prepared_geom );
GEOSGeom_destroy( cache->geom );
+
cache->prepared_geom = 0;
cache->geom = 0;
cache->argnum = 0;
+
}
cache->key1 = key1;
@@ -3619,7 +3891,6 @@
return cache;
}
-
#endif /* PREPARED_GEOM */
@@ -3628,16 +3899,14 @@
{
#ifndef PREPARED_GEOM
elog(ERROR,"Not implemented in this version!");
- PG_RETURN_NULL(); /* never get here */
+ PG_RETURN_NULL();
#else
- PG_LWGEOM * geom1;
- PG_LWGEOM * geom2;
- GEOSPreparedGeometry * pg;
- bool result;
- BOX2DFLOAT4 box1, box2;
- PREPARED_GEOM_CACHE * prep_cache;
- MemoryContext old_context;
- int32 key1;
+ PG_LWGEOM * geom1;
+ PG_LWGEOM * geom2;
+ bool result;
+ BOX2DFLOAT4 box1, box2;
+ PrepGeomCache * prep_cache;
+ int32 key1;
geom1 = (PG_LWGEOM *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
geom2 = (PG_LWGEOM *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
@@ -3659,19 +3928,15 @@
PG_RETURN_BOOL(FALSE);
}
- old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
- prep_cache = fcinfo->flinfo->fn_extra;
- prep_cache = get_prepared_geometry_cache( prep_cache, geom1, 0, key1, 0 );
- fcinfo->flinfo->fn_extra = prep_cache;
- MemoryContextSwitchTo(old_context);
+ prep_cache = GetPrepGeomCache( fcinfo, geom1, 0, key1, 0 );
+
initGEOS(lwnotice, lwnotice);
if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
{
GEOSGeom g = POSTGIS2GEOS(geom2);
- pg = prep_cache->prepared_geom;
- result = GEOSPreparedContains( pg, g);
+ result = GEOSPreparedContains( prep_cache->prepared_geom, g);
GEOSGeom_destroy(g);
}
else
@@ -3705,11 +3970,9 @@
#else
PG_LWGEOM * geom1;
PG_LWGEOM * geom2;
- GEOSPreparedGeometry * pg;
bool result;
BOX2DFLOAT4 box1, box2;
- PREPARED_GEOM_CACHE * prep_cache;
- MemoryContext old_context;
+ PrepGeomCache * prep_cache;
int32 key1;
geom1 = (PG_LWGEOM *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
@@ -3732,19 +3995,14 @@
PG_RETURN_BOOL(FALSE);
}
- old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
- prep_cache = fcinfo->flinfo->fn_extra;
- prep_cache = get_prepared_geometry_cache( prep_cache, geom1, 0, key1, 0 );
- fcinfo->flinfo->fn_extra = prep_cache;
- MemoryContextSwitchTo(old_context);
+ prep_cache = GetPrepGeomCache( fcinfo, geom1, 0, key1, 0 );
initGEOS(lwnotice, lwnotice);
if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
{
GEOSGeom g = POSTGIS2GEOS(geom2);
- pg = prep_cache->prepared_geom;
- result = GEOSPreparedContainsProperly( pg, g);
+ result = GEOSPreparedContainsProperly( prep_cache->prepared_geom, g);
GEOSGeom_destroy(g);
}
else
@@ -3778,11 +4036,9 @@
#else
PG_LWGEOM * geom1;
PG_LWGEOM * geom2;
- GEOSPreparedGeometry * pg;
bool result;
BOX2DFLOAT4 box1, box2;
- PREPARED_GEOM_CACHE * prep_cache;
- MemoryContext old_context;
+ PrepGeomCache * prep_cache;
int32 key1;
geom1 = (PG_LWGEOM *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
@@ -3805,19 +4061,14 @@
PG_RETURN_BOOL(FALSE);
}
- old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
- prep_cache = fcinfo->flinfo->fn_extra;
- prep_cache = get_prepared_geometry_cache( prep_cache, geom1, 0, key1, 0 );
- fcinfo->flinfo->fn_extra = prep_cache;
- MemoryContextSwitchTo(old_context);
+ prep_cache = GetPrepGeomCache( fcinfo, geom1, 0, key1, 0 );
initGEOS(lwnotice, lwnotice);
if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
{
GEOSGeom g = POSTGIS2GEOS(geom2);
- pg = prep_cache->prepared_geom;
- result = GEOSPreparedCovers( pg, g);
+ result = GEOSPreparedCovers( prep_cache->prepared_geom, g);
GEOSGeom_destroy(g);
}
else
@@ -3852,11 +4103,9 @@
#else
PG_LWGEOM * geom1;
PG_LWGEOM * geom2;
- GEOSPreparedGeometry * pg;
bool result;
BOX2DFLOAT4 box1, box2;
- PREPARED_GEOM_CACHE * prep_cache;
- MemoryContext old_context;
+ PrepGeomCache * prep_cache;
int32 key1, key2;
geom1 = (PG_LWGEOM *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
@@ -3880,11 +4129,7 @@
PG_RETURN_BOOL(FALSE);
}
- old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
- prep_cache = fcinfo->flinfo->fn_extra;
- prep_cache = get_prepared_geometry_cache( prep_cache, geom1, geom2, key1, key2 );
- fcinfo->flinfo->fn_extra = prep_cache;
- MemoryContextSwitchTo(old_context);
+ prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2, key1, key2 );
initGEOS(lwnotice, lwnotice);
@@ -3893,15 +4138,13 @@
if ( prep_cache->argnum == 1 )
{
GEOSGeom g = POSTGIS2GEOS(geom2);
- pg = prep_cache->prepared_geom;
- result = GEOSPreparedCovers( pg, g);
+ result = GEOSPreparedCovers( prep_cache->prepared_geom, g);
GEOSGeom_destroy(g);
}
else
{
GEOSGeom g = POSTGIS2GEOS(geom1);
- pg = prep_cache->prepared_geom;
- result = GEOSPreparedCovers( pg, g);
+ result = GEOSPreparedCovers( prep_cache->prepared_geom, g);
GEOSGeom_destroy(g);
}
}
Modified: trunk/lwgeom/lwgeom_transform.c
===================================================================
--- trunk/lwgeom/lwgeom_transform.c 2008-10-02 18:52:10 UTC (rev 3055)
+++ trunk/lwgeom/lwgeom_transform.c 2008-10-02 23:53:37 UTC (rev 3056)
@@ -116,7 +116,7 @@
static void PROJ4SRSCacheDelete(MemoryContext context);
static void PROJ4SRSCacheReset(MemoryContext context);
static bool PROJ4SRSCacheIsEmpty(MemoryContext context);
-static void PROJ4SRSCacheStats(MemoryContext context);
+static void PROJ4SRSCacheStats(MemoryContext context, int level);
#ifdef MEMORY_CONTEXT_CHECKING
static void PROJ4SRSCacheCheck(MemoryContext context);
#endif
@@ -188,7 +188,7 @@
}
static void
-PROJ4SRSCacheStats(MemoryContext context)
+PROJ4SRSCacheStats(MemoryContext context, int level)
{
/*
* Simple stats display function - we must supply a function since this call is mandatory according to tgl
More information about the postgis-commits
mailing list