Skip to content

Commit e1512c2

Browse files
committed
perf: hash-adjacency overhaul — agehash + flat-array VertexEdgeArray
Replaces the per-graph adjacency map with a Robin Hood open-addressing hashtable (agehash) and an embedded flat-array edge list, removing the hottest dynahash path on IC1 and shrinking the largest hashtable AGE keeps. Stages land as one commit: S1 MurmurHash3 fmix64 for graphid hashtables (replaces tag_hash) S2 Precompute graphid hash; share across paired DFS lookups S3 Replace ListGraphId adjacency with embedded flat-array VertexEdgeArray (single palloc, contiguous iteration) S4 Batched MLP lookup pipeline in add_valid_vertex_edges S5/C1 agehash library: INLINE Robin Hood hashtable with _with_hash API, freeze, iter, and a regress-only selftest S5/C2 Wire global graph edge_hashtable through agehash; drop edge_id from edge_entry (key lives in slot header); AGEHASH_MAX_LOAD=0.85; MemoryContextAllocHuge for SF10+ Performance (SF3 LDBC SNB, 5 runs/3 warmup, vs clean master baseline_v2): IC1 8,625 → 7,117 ms −17.49 % (the headline; hashtable-bound) IU1 40 → 35 ms −11.86 % (heaviest update; lookup-bound) IC sum 198,958 → 197,367 ms −0.80 % (suite-level noise) IS sum 1,009 → 1,028 ms +1.86 % (IS3 jitter; sub-ms) IU sum 77 → 72 ms −6.64 % IC2/3/4/5/6/7/8/9/10/11/12: parity (within ±3.3 %, mostly ±1.5 %) The VLE-DFS-heavy queries (IC3/5/6/9/11) sit at parity: with hash_search_with_hash_value at ≤1 % inclusive on their baseline flames, no hashtable swap can recover meaningful wall-time on them. Memory: removing edge_id from edge_entry saves ~416 MB on SF3 and ~1.4 GB on SF10 for the global graph's edge_hashtable. Slot capacity uses MemoryContextAllocHuge so SF10+ edge tables can be built. Adds: src/backend/utils/cache/agehash.c, src/include/utils/agehash.h regress/sql/agehash.sql + expected/agehash.out (boundary selftest) _agehash_self_test() in both fresh-install and upgrade SQL Tested on PostgreSQL 18.3 (REL_18_STABLE): all 35 regression tests pass (installcheck), warning-free build. Co-authored-by: Claude <noreply@anthropic.com> modified: Makefile modified: age--1.7.0--y.y.y.sql new file: regress/expected/agehash.out new file: regress/sql/agehash.sql modified: sql/age_main.sql modified: src/backend/utils/adt/age_global_graph.c modified: src/backend/utils/adt/age_vle.c new file: src/backend/utils/cache/agehash.c modified: src/include/utils/age_global_graph.h new file: src/include/utils/agehash.h
1 parent a1b749a commit e1512c2

10 files changed

Lines changed: 1372 additions & 173 deletions

File tree

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ OBJS = src/backend/age.o \
127127
src/backend/utils/ag_func.o \
128128
src/backend/utils/graph_generation.o \
129129
src/backend/utils/cache/ag_cache.o \
130+
src/backend/utils/cache/agehash.o \
130131
src/backend/utils/load/ag_load_labels.o \
131132
src/backend/utils/load/ag_load_edges.o \
132133
src/backend/utils/load/age_load.o \
@@ -153,6 +154,7 @@ REGRESS = scan \
153154
graphid \
154155
agtype \
155156
agtype_hash_cmp \
157+
agehash \
156158
catalog \
157159
cypher \
158160
expr \

age--1.7.0--y.y.y.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,3 +459,13 @@ BEGIN
459459
END LOOP;
460460
END;
461461
$$;
462+
463+
-- Internal selftest for the agehash open-addressing hashtable. Returns "OK"
464+
-- on success or "FAIL: ..." with a diagnostic message. Intended for the
465+
-- agehash regression test only.
466+
CREATE FUNCTION ag_catalog._agehash_self_test()
467+
RETURNS text
468+
LANGUAGE c
469+
VOLATILE
470+
PARALLEL UNSAFE
471+
AS 'MODULE_PATHNAME';

regress/expected/agehash.out

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* agehash internal selftest.
3+
*
4+
* Exercises the Robin Hood open-addressing hashtable used by AGE's hot-path
5+
* caches at boundary sizes (1, 7, 8, 9, 63, 64, 65, 1024, ...) and across
6+
* grow thresholds. A success returns the literal "OK"; any failure returns
7+
* "FAIL: <reason>" describing the offending case.
8+
*/
9+
SELECT ag_catalog._agehash_self_test();
10+
_agehash_self_test
11+
--------------------
12+
OK
13+
(1 row)
14+

regress/sql/agehash.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* agehash internal selftest.
3+
*
4+
* Exercises the Robin Hood open-addressing hashtable used by AGE's hot-path
5+
* caches at boundary sizes (1, 7, 8, 9, 63, 64, 65, 1024, ...) and across
6+
* grow thresholds. A success returns the literal "OK"; any failure returns
7+
* "FAIL: <reason>" describing the offending case.
8+
*/
9+
SELECT ag_catalog._agehash_self_test();

sql/age_main.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ CREATE FUNCTION ag_catalog._label_id(graph_name name, label_name name)
8686
PARALLEL SAFE
8787
AS 'MODULE_PATHNAME';
8888

89+
-- Internal selftest for the agehash open-addressing hashtable. Returns "OK"
90+
-- on success or "FAIL: ..." with a diagnostic message. Intended for the
91+
-- agehash regression test only.
92+
CREATE FUNCTION ag_catalog._agehash_self_test()
93+
RETURNS text
94+
LANGUAGE c
95+
VOLATILE
96+
PARALLEL UNSAFE
97+
AS 'MODULE_PATHNAME';
98+
8999
--
90100
-- utility functions
91101
--

0 commit comments

Comments
 (0)