AS 'MODULE_PATHNAME', 'chash_collision_test'
LANGUAGE C;
+CREATE FUNCTION chash_write_stats_to_log()
+RETURNS void
+AS 'MODULE_PATHNAME', 'chash_write_stats_to_log'
+LANGUAGE C;
+
CREATE FUNCTION dynahash_insert_test()
RETURNS void
AS 'MODULE_PATHNAME', 'dynahash_insert_test'
Datum chash_delete_test(PG_FUNCTION_ARGS);
Datum chash_concurrent_test(PG_FUNCTION_ARGS);
Datum chash_collision_test(PG_FUNCTION_ARGS);
+Datum chash_write_stats_to_log(PG_FUNCTION_ARGS);
Datum dynahash_insert_test(PG_FUNCTION_ARGS);
Datum dynahash_search_test(PG_FUNCTION_ARGS);
Datum dynahash_delete_test(PG_FUNCTION_ARGS);
PG_RETURN_VOID();
}
+Datum
+chash_write_stats_to_log(PG_FUNCTION_ARGS)
+{
+ StringInfoData buf;
+ CHashTableStats stats;
+
+ CHashStatistics(chash, &stats);
+ initStringInfo(&buf);
+
+ if (stats.s_insert_retry)
+ appendStringInfo(&buf, UINT64_FORMAT " inserts retried; ",
+ stats.s_insert_retry);
+ if (stats.s_delete_retry)
+ appendStringInfo(&buf, UINT64_FORMAT " deletes retried; ",
+ stats.s_delete_retry);
+ if (stats.s_cleanup_scan)
+ appendStringInfo(&buf, UINT64_FORMAT " cleanup scans; ",
+ stats.s_delete_retry);
+ if (stats.s_scan_expunge_ok)
+ appendStringInfo(&buf, UINT64_FORMAT " scan expunges ok; ",
+ stats.s_scan_expunge_ok);
+ if (stats.s_scan_expunge_fail)
+ appendStringInfo(&buf, UINT64_FORMAT " scan expunges failed; ",
+ stats.s_scan_expunge_fail);
+ if (stats.s_scan_restart)
+ appendStringInfo(&buf, UINT64_FORMAT " scans restarted; ",
+ stats.s_scan_restart);
+ if (stats.s_allocate_pop_fail)
+ appendStringInfo(&buf, UINT64_FORMAT " allocation pops failed; ",
+ stats.s_allocate_pop_fail);
+ if (stats.s_gc_pop_fail)
+ appendStringInfo(&buf, UINT64_FORMAT " GC pops failed; ",
+ stats.s_gc_pop_fail);
+ if (stats.s_gc_reclaim_retry)
+ appendStringInfo(&buf, UINT64_FORMAT " GC reclaims retried; ",
+ stats.s_gc_reclaim_retry);
+ if (stats.s_garbage_retry)
+ appendStringInfo(&buf, UINT64_FORMAT " garbage enqueues retried; ",
+ stats.s_garbage_retry);
+ if (stats.s_free_retry)
+ appendStringInfo(&buf, UINT64_FORMAT " immediate frees retried; ",
+ stats.s_free_retry);
+
+ if (buf.len > 1)
+ {
+ buf.data[buf.len-2] = '\0';
+ elog(LOG, "chash statistics: %s", buf.data);
+ }
+ else
+ elog(LOG, "chash statistics: nothing to report");
+
+ PG_RETURN_VOID();
+}
+
static bool
dynahash_insert(uint32 key, uint32 val)
{
* unused.
*/
uint32 gc_next; /* next garbage list to reclaim */
- uint64 s_insert_retry; /* insert point concurrently updated */
- uint64 s_delete_retry; /* delete point concurrently updated */
- uint64 s_cleanup_scan; /* expunge-after-delete failed */
- uint64 s_scan_expunge_ok; /* scan did expunge */
- uint64 s_scan_expunge_fail; /* expunge failed */
- uint64 s_scan_restart; /* scan restarted */
- uint64 s_cleanup_expunge_ok; /* cleanup scan did expunge */
- uint64 s_cleanup_expunge_fail; /* cleanup scan expunge failed */
- uint64 s_cleanup_restart; /* cleanup scan restarted */
- uint64 s_allocate_pop_fail; /* freelist pop failed */
- uint64 s_gc_pop_fail; /* garbage list pop failed */
- uint64 s_gc_reclaim_retry; /* failed to return GC'd nodes */
- uint64 s_garbage_retry; /* failed to enqueue garbage */
- uint64 s_free_retry; /* failed to perform immediate free */
+ CHashTableStats stats; /* statistics */
} CHashTableData;
#define CHashTableGetRaw(table, offset) \
if (!__sync_bool_compare_and_swap(scan.pointer_to_target,
scan.target, new))
{
- table->s_insert_retry++;
+ table->stats.s_insert_retry++;
goto retry;
}
}
cleanup_scan = true;
break;
}
- table->s_delete_retry++;
+ table->stats.s_delete_retry++;
}
/*
*/
if (cleanup_scan)
{
- table->s_cleanup_scan++;
+ table->stats.s_cleanup_scan++;
CHashBucketCleanup(table, &table->bucket[bucket], hashcode);
}
return scan.found;
}
+/*
+ * Provide backend-local statistics to caller.
+ */
+void
+CHashStatistics(CHashTable table, CHashTableStats *stats)
+{
+ memcpy(stats, &table->stats, sizeof(CHashTableStats));
+}
+
/*
* Scan one bucket of a concurrent hash table, storing the results in a
* CHashResult object provided by the caller.
* all non-deleted items (and possibly some deleted items)
* that were present at the time we began the scan.
*/
- table->s_scan_expunge_ok++;
+ table->stats.s_scan_expunge_ok++;
CHashAddToGarbage(table, hashcode & table->bucket_mask,
target);
target = CHashPtrUnmark(next);
* have to be pretty unlucky to have it happen even twice in
* a row.
*/
- table->s_scan_expunge_fail++;
+ table->stats.s_scan_expunge_fail++;
target = *pointer_to_target;
if (CHashPtrIsMarked(target))
{
- table->s_scan_restart++;
+ table->stats.s_scan_restart++;
goto retry;
}
}
CHashPtrUnmark(next)))
{
/* We removed the item. */
- table->s_cleanup_expunge_ok++;
+ table->stats.s_cleanup_expunge_ok++;
CHashAddToGarbage(table, hashcode & table->bucket_mask,
target);
target = CHashPtrUnmark(next);
pg_read_barrier_depends();
if (__sync_bool_compare_and_swap(b, new, n->un.gcnext))
return new;
- table->s_allocate_pop_fail++;
+ table->stats.s_allocate_pop_fail++;
}
/* If next garbage list is non-empty, empty it via compare-and-swap. */
if (CHashPtrIsInvalid(garbage))
;
else if (!__sync_bool_compare_and_swap(b, garbage, InvalidCHashPtr))
- ++table->s_gc_pop_fail;
+ ++table->stats.s_gc_pop_fail;
else
{
uint64 chash_bucket;
n->un.gcnext = oldhead;
if (__sync_bool_compare_and_swap(b, oldhead, fhead))
break;
- ++table->s_gc_reclaim_retry;
+ ++table->stats.s_gc_reclaim_retry;
}
}
n->un.gcnext = g;
if (__sync_bool_compare_and_swap(garbage, g, c))
break;
- ++table->s_garbage_retry;
+ ++table->stats.s_garbage_retry;
}
}
n->un.gcnext = f;
if (__sync_bool_compare_and_swap(free, f, c))
break;
- ++table->s_free_retry;
+ ++table->stats.s_free_retry;
}
}
uint16 key_size; /* size of each key */
} CHashDescriptor;
+/* Concurrent hash table statistics. */
+typedef struct
+{
+ uint64 s_insert_retry; /* insert point concurrently updated */
+ uint64 s_delete_retry; /* delete point concurrently updated */
+ uint64 s_cleanup_scan; /* expunge-after-delete failed */
+ uint64 s_scan_expunge_ok; /* scan did expunge */
+ uint64 s_scan_expunge_fail; /* expunge failed */
+ uint64 s_scan_restart; /* scan restarted */
+ uint64 s_cleanup_expunge_ok; /* cleanup scan did expunge */
+ uint64 s_cleanup_expunge_fail; /* cleanup scan expunge failed */
+ uint64 s_cleanup_restart; /* cleanup scan restarted */
+ uint64 s_allocate_pop_fail; /* freelist pop failed */
+ uint64 s_gc_pop_fail; /* garbage list pop failed */
+ uint64 s_gc_reclaim_retry; /* failed to return GC'd nodes */
+ uint64 s_garbage_retry; /* failed to enqueue garbage */
+ uint64 s_free_retry; /* failed to perform immediate free */
+} CHashTableStats;
+
/* Opaque handle for a concurrent hash table. */
struct CHashTableData;
typedef struct CHashTableData *CHashTable;
extern bool CHashInsert(CHashTable table, void *entry);
extern bool CHashDelete(CHashTable table, void *key);
extern bool CHashSearch(CHashTable table, void *entry);
+extern void CHashStatistics(CHashTable table, CHashTableStats *stats);
#endif /* CHASH_H */