From 3bb0029234e444885a41b2637e149a8f0f04974b Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Thu, 2 Aug 2012 14:01:20 +0000 Subject: [PATCH] Rewrite statistics system. --- contrib/hashtest/hashtest.c | 47 ++++------------- src/backend/utils/hash/chash.c | 96 ++++++++++++++++++++++++++-------- src/include/utils/chash.h | 46 ++++++++++------ 3 files changed, 115 insertions(+), 74 deletions(-) diff --git a/contrib/hashtest/hashtest.c b/contrib/hashtest/hashtest.c index 440f1c5026..554457df0e 100644 --- a/contrib/hashtest/hashtest.c +++ b/contrib/hashtest/hashtest.c @@ -278,45 +278,20 @@ chash_collision_test(PG_FUNCTION_ARGS) Datum chash_write_stats_to_log(PG_FUNCTION_ARGS) { - StringInfoData buf; - CHashTableStats stats; + uint64 stats[CHS_NumberOfStatistics]; + CHashStatisticsType i; + StringInfoData buf; - CHashStatistics(chash, &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); + for (i = 0; i < CHS_NumberOfStatistics; ++i) + { + if (stats[i] == 0) + continue; + appendStringInfo(&buf, UINT64_FORMAT " %s; ", stats[i], + CHashStatisticsNames[i]); + } if (buf.len > 1) { diff --git a/src/backend/utils/hash/chash.c b/src/backend/utils/hash/chash.c index 4cb8371835..5586ec5ac7 100644 --- a/src/backend/utils/hash/chash.c +++ b/src/backend/utils/hash/chash.c @@ -162,7 +162,7 @@ typedef struct CHashTableData * unused. */ uint32 gc_next; /* next garbage list to reclaim */ - CHashTableStats stats; /* statistics */ + uint64 stats[CHS_NumberOfStatistics]; /* statistics */ } CHashTableData; #define CHashTableGetRaw(table, offset) \ @@ -171,6 +171,8 @@ typedef struct CHashTableData #define CHashTableGetNode(table, ptr) \ (AssertMacro(!CHashPtrIsInvalid(ptr)), \ CHashTableGetRaw((table), CHashPtrGetOffset((ptr)))) +#define CHashTableIncrementStatistic(table, stat) \ + ((table)->stats[(stat)]++) /* * Bucket scan. @@ -183,6 +185,33 @@ typedef struct bool found; } CHashScanResult; +/* Human-readable statistics names. */ +char *CHashStatisticsNames[] = { + "searches", + "searches failed", + "inserts", + "inserts failed", + "inserts retried", + "deletions", + "deletions failed", + "deletions retried", + "scan expunges", + "scan expunges failed", + "scans restarted", + "cleanup scans", + "cleanup expunges", + "cleanup expunges failed", + "cleanups restarted", + "allocations failed", + "garbage enqueues retried", + "garbage dequeues failed", + "garbage collections", + "garbage collection spins", + "garbage collection fast reclaims", + "garbage collection reclaims retried", + "" +}; + /* Function prototypes. */ static CHashPtr CHashAllocate(CHashTable table); static void CHashAddToGarbage(CHashTable table, uint32 bucket, CHashPtr c); @@ -205,6 +234,9 @@ CHashBootstrap(CHashDescriptor *desc) CHashTable table; uint32 bucket_shift; + /* Sanity check. */ + Assert(!strcmp(CHashStatisticsNames[CHS_NumberOfStatistics], "")); + /* Allocate table and copy descriptor. */ table = MemoryContextAllocZero(TopMemoryContext, sizeof(CHashTableData)); memcpy(&table->desc, desc, sizeof(CHashDescriptor)); @@ -260,6 +292,9 @@ CHashBootstrap(CHashDescriptor *desc) /* Each arena element must be MAXALIGN'd and include per-node space. */ table->arena_stride = SizeOfCHashNode + MAXALIGN(desc->element_size); + /* Zero out statistics. */ + memset(table->stats, 0, sizeof(uint64) * CHS_NumberOfStatistics); + return table; } @@ -388,6 +423,9 @@ CHashSearch(CHashTable table, void *entry) pg_memory_barrier(); MyProc->hazard[0] = NULL; + CHashTableIncrementStatistic(table, CHS_Search); + if (!scan.found) + CHashTableIncrementStatistic(table, CHS_Search_Failed); return scan.found; } @@ -461,7 +499,7 @@ retry: if (!__sync_bool_compare_and_swap(scan.pointer_to_target, scan.target, new)) { - table->stats.s_insert_retry++; + CHashTableIncrementStatistic(table, CHS_Insert_Retry); goto retry; } } @@ -485,8 +523,12 @@ retry: * element makes it back to the freelist is trying to allocate some * other node. */ + CHashTableIncrementStatistic(table, CHS_Insert); if (scan.found) + { + CHashTableIncrementStatistic(table, CHS_Insert_Failed); CHashAddToGarbage(table, bucket, new); + } /* The insert succeeded if and only if no duplicate was found. */ return !scan.found; @@ -543,7 +585,7 @@ CHashDelete(CHashTable table, void *entry) cleanup_scan = true; break; } - table->stats.s_delete_retry++; + CHashTableIncrementStatistic(table, CHS_Delete_Retry); } /* @@ -552,7 +594,7 @@ CHashDelete(CHashTable table, void *entry) */ if (cleanup_scan) { - table->stats.s_cleanup_scan++; + CHashTableIncrementStatistic(table, CHS_Cleanup_Scan); CHashBucketCleanup(table, b, hashcode); } @@ -562,6 +604,9 @@ CHashDelete(CHashTable table, void *entry) MyProc->hazard[0] = NULL; /* We're done. */ + CHashTableIncrementStatistic(table, CHS_Delete); + if (!scan.found) + CHashTableIncrementStatistic(table, CHS_Delete_Failed); return scan.found; } @@ -569,9 +614,9 @@ CHashDelete(CHashTable table, void *entry) * Provide backend-local statistics to caller. */ void -CHashStatistics(CHashTable table, CHashTableStats *stats) +CHashStatistics(CHashTable table, uint64 *stats) { - memcpy(stats, &table->stats, sizeof(CHashTableStats)); + memcpy(stats, &table->stats, sizeof(uint64) * CHS_NumberOfStatistics); } /* @@ -653,7 +698,7 @@ zap: * all non-deleted items (and possibly some deleted items) * that were present at the time we began the scan. */ - table->stats.s_scan_expunge_ok++; + CHashTableIncrementStatistic(table, CHS_Scan_Expunge); CHashAddToGarbage(table, hashcode & table->bucket_mask, target); target = CHashPtrUnmark(next); @@ -680,11 +725,11 @@ zap: * have to be pretty unlucky to have it happen even twice in * a row. */ - table->stats.s_scan_expunge_fail++; + CHashTableIncrementStatistic(table, CHS_Scan_Expunge_Fail); target = *pointer_to_target; if (CHashPtrIsMarked(target)) { - table->stats.s_scan_restart++; + CHashTableIncrementStatistic(table, CHS_Scan_Restart); goto retry; } } @@ -797,7 +842,7 @@ retry: CHashPtrUnmark(next))) { /* We removed the item. */ - table->stats.s_cleanup_expunge_ok++; + CHashTableIncrementStatistic(table, CHS_Cleanup_Expunge); CHashAddToGarbage(table, hashcode & table->bucket_mask, target); target = CHashPtrUnmark(next); @@ -805,11 +850,11 @@ retry: else { /* Someone else removed the item first. */ - table->stats.s_cleanup_expunge_fail++; + CHashTableIncrementStatistic(table, CHS_Cleanup_Expunge_Fail); target = *pointer_to_target; if (CHashPtrIsMarked(target)) { - table->stats.s_cleanup_restart++; + CHashTableIncrementStatistic(table, CHS_Cleanup_Restart); goto retry; } } @@ -891,7 +936,7 @@ CHashAllocate(CHashTable table) pg_read_barrier_depends(); if (__sync_bool_compare_and_swap(b, new, n->un.gcnext)) return new; - table->stats.s_allocate_pop_fail++; + CHashTableIncrementStatistic(table, CHS_Allocate_Fail); new = *b; } @@ -902,7 +947,7 @@ CHashAllocate(CHashTable table) if (CHashPtrIsInvalid(garbage)) ; else if (!__sync_bool_compare_and_swap(b, garbage, InvalidCHashPtr)) - table->stats.s_gc_pop_fail++; + CHashTableIncrementStatistic(table, CHS_Garbage_Dequeue_Fail); else { uint32 i; @@ -929,16 +974,22 @@ CHashAllocate(CHashTable table) * might want to eventually enter a longer sleep here, or PANIC, * but it's not clear exactly how to calibrate that. */ + CHashTableIncrementStatistic(table, CHS_GC); MyProc->hazard[0] = NULL; for (i = 0; i < ProcGlobal->allProcCount; i++) { volatile PGPROC *proc = &ProcGlobal->allProcs[i]; void *hazard; - do + hazard = proc->hazard[0]; + if (hazard == b || hazard == fh) { - hazard = proc->hazard[0]; - } while (hazard == b || hazard == fh); + CHashTableIncrementStatistic(table, CHS_GC_Spin); + do + { + hazard = proc->hazard[0]; + } while (hazard == b || hazard == fh); + } } /* Remove one item from list to satisfy current allocation. */ @@ -953,8 +1004,11 @@ CHashAllocate(CHashTable table) * we needn't update the list any further; otherwise, we've got * to adjust the next-pointer in the last of the reclaimed nodes. */ - if (!CHashPtrIsInvalid(fhead) - && !__sync_bool_compare_and_swap(fh, InvalidCHashPtr, fhead)) + if (CHashPtrIsInvalid(fhead)) + ; + else if (__sync_bool_compare_and_swap(fh, InvalidCHashPtr, fhead)) + CHashTableIncrementStatistic(table, CHS_GC_Reclaim_Fast); + else { CHashPtr fcurrent; CHashPtr fnext; @@ -978,7 +1032,7 @@ CHashAllocate(CHashTable table) n->un.gcnext = oldhead; if (__sync_bool_compare_and_swap(fh, oldhead, fhead)) break; - table->stats.s_gc_reclaim_retry++; + CHashTableIncrementStatistic(table, CHS_GC_Reclaim_Retry); } } @@ -1019,6 +1073,6 @@ CHashAddToGarbage(CHashTable table, uint32 bucket, CHashPtr c) n->un.gcnext = g; if (__sync_bool_compare_and_swap(garbage, g, c)) break; - table->stats.s_garbage_retry++; + CHashTableIncrementStatistic(table, CHS_Garbage_Enqueue_Retry); } } diff --git a/src/include/utils/chash.h b/src/include/utils/chash.h index 703aa3d7de..8ff7e64039 100644 --- a/src/include/utils/chash.h +++ b/src/include/utils/chash.h @@ -23,23 +23,35 @@ typedef struct } CHashDescriptor; /* Concurrent hash table statistics. */ -typedef struct +typedef enum { - 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; + CHS_Search, /* search */ + CHS_Search_Failed, /* search failed (no such key) */ + CHS_Insert, /* insert */ + CHS_Insert_Failed, /* insert failed (duplicate key) */ + CHS_Insert_Retry, /* insert retried (concurrent update) */ + CHS_Delete, /* delete */ + CHS_Delete_Failed, /* delete failed (no such key) */ + CHS_Delete_Retry, /* delete retried (concurrent update) */ + CHS_Scan_Expunge, /* scan expunged deleted item */ + CHS_Scan_Expunge_Fail, /* scan failed to expunge */ + CHS_Scan_Restart, /* concurrent deletes forced a scan restart */ + CHS_Cleanup_Scan, /* concurrent update forced a cleanup scan */ + CHS_Cleanup_Expunge, /* cleanup scan expunged an item */ + CHS_Cleanup_Expunge_Fail, /* cleanup scan failed to expunge */ + CHS_Cleanup_Restart, /* concurrent deletes forced cleanup restart */ + CHS_Allocate_Fail, /* allocation failed to pop freelist */ + CHS_Garbage_Enqueue_Retry, /* enqueue on garbage list retried */ + CHS_Garbage_Dequeue_Fail, /* dequeue of garbage failed */ + CHS_GC, /* garbage collection cycle */ + CHS_GC_Spin, /* GC spun waiting for concurrent process */ + CHS_GC_Reclaim_Fast, /* GC put garbage on freelist via fast path */ + CHS_GC_Reclaim_Retry, /* enqueue of garbage on freelist retried */ + CHS_NumberOfStatistics /* number of statistics */ +} CHashStatisticsType; + +/* Human-readable names for statistics. */ +extern char *CHashStatisticsNames[]; /* Opaque handle for a concurrent hash table. */ struct CHashTableData; @@ -54,6 +66,6 @@ extern CHashTable CHashInitialize(CHashTable table, CHashDescriptor *desc); 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); +extern void CHashStatistics(CHashTable table, uint64 *stats); #endif /* CHASH_H */ -- 2.39.5