Documentation
¶
Overview ¶
Example 20_concurrent_reads — the lock-free read contract of a frozen CSR snapshot, exercised by many concurrent readers.
A single immutable csr.CSR is built once from a seeded, realistic scale-free network and then read concurrently by a pool of worker goroutines. Each worker runs the same mixed read workload — a batch of Dijkstra single-source shortest paths, a BFS reach count, and a PageRank to convergence — over the one shared snapshot. None of them takes a lock on the snapshot: an immutable CSR is safe for any number of concurrent readers with zero synchronisation on the hot path (Mehlhorn-Sanders, GraphBLAS). That is the contract this example demonstrates and measures.
Model ¶
The graph is a Barabási-Albert preferential-attachment network — the canonical model of a social / web graph, where a few high-degree hubs dominate and the degree distribution is heavy-tailed. It is the right shape here for three reasons:
- It is connected by construction (a connected seed core, and every new node attaches at least one edge to the already-connected component), so BFS reaches every node and its reach count is a constant, regardless of the seed.
- The hub structure gives PageRank a meaningful, well-separated top-k, so "the top-k set is constant" is a robust invariant.
- The high-degree hubs create heavy adjacency fan-out, so each read does real CPU work — the point of a concurrency benchmark.
Edges are undirected (an adjlist.AdjList with Directed:false mirrors every insertion) and carry an integer weight in [1, weightMax] drawn from the seeded RNG. Integer weights keep Dijkstra free of NaN/Inf concerns and make distance sums exact.
Evidence — the lock-free read contract ¶
The example reports the evidence that matters for a concurrency subject (see docs/examples-standard.md):
- Aggregate read throughput (reads/s) of the mixed workload.
- Per-worker-count scaling: the identical workload is run at 1, 2, 4, 8 … workers (capped at GOMAXPROCS by default; pass -cap-to-cpus=false to climb to -workers, e.g. 64/256/1024), and the throughput at each level is printed as telemetry. Throughput that climbs with the worker count is the observable evidence that readers do not contend on the snapshot.
- Intra-query parallel correctness: the parallel variants search.WCCParallel, search.CountTrianglesParallel and centrality.BetweennessParallel are cross-checked against their serial counterparts (exact for the partition and the triangle count, within a float tolerance for betweenness), with the speedup reported as telemetry.
- Live heap, so a reader can see the immutable snapshot is shared, not copied per worker.
All telemetry lines are prefixed with "# " and vary per run and machine. The correctness evidence is printed as bare deterministic fact lines: every concurrent read returns the SAME answer a single reader computes. Specifically — for a fixed seed — every concurrent Dijkstra from the fixed source yields the same distance to the fixed target, the BFS reach count is constant, and the PageRank top-k node set is constant. The headline fact, reads.agree=true, asserts that concurrent reads agreed with the single-threaded reference across every worker count.
Scale ¶
Run with no flags, the example builds a small, deterministic default (a few thousand nodes) that a test pins and that completes well under a second. Every dimension is a flag, so the same binary scales up to a size where concurrent reads do enough work for the scaling curve to be observable:
go run ./examples/20_concurrent_reads -nodes 200000 -attach 8 -workers 16
The data shape is reproducible for a fixed -seed; only the telemetry (lines prefixed with "# ") varies between runs and machines.