20_concurrent_reads

command
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2026 License: MIT Imports: 10 Imported by: 0

README

Example 20 — Concurrent reads over an immutable CSR

What it demonstrates

The lock-free read contract of a frozen csr.CSR: once a graph is snapshotted into an immutable CSR, any number of goroutines may traverse it simultaneously with zero synchronisation on the snapshot itself. The example runs three different read-only algorithms — Dijkstra, BFS, and PageRank — concurrently over a single shared CSR.

Domain / scenario

A 100-node undirected graph wired as a ring with a chord: every node i connects to (i+1) % 100 and to (i+7) % 100, with small integer weights. The graph is built once with the mutable adjlist builder, then frozen into one csr.CSR snapshot that all three goroutines read:

  • Reader 1 — Dijkstra: eight single-source shortest-path runs, one per seed s in 0..7, each measuring the cost to its antipodal node (s+50) % 100; the costs are summed.
  • Reader 2 — BFS: a breadth-first reach count from node 0.
  • Reader 3 — PageRank: power iteration to convergence, counting the nodes that end up with a non-zero rank.

The CSR is never mutated after it is built, so the readers need no lock on it. The only shared mutable state is the results map the goroutines publish into, which is guarded by a sync.Mutex.

How to run

go run ./examples/20_concurrent_reads

Expected output

The goroutines finish in a non-deterministic order, but the report is printed in a fixed key order and the reported aggregates are deterministic for the hard-coded inputs. A representative run:

Concurrent results over a single immutable CSR:
  dijkstra  8 SSSPs, summed cost = 110
  bfs       BFS reached 100 nodes
  pagerank  PageRank 1 iters, 100 live ranks

The summed Dijkstra cost (110), the BFS reach count (100), and the PageRank live-rank count (100) are stable across runs; only the goroutine completion order varies. The PageRank iteration count is shown for context.

Key APIs

  • graph/adjlist.New / AdjList.AddEdge — build the mutable weighted undirected graph.
  • graph/csr.BuildFromAdjList — freeze the builder into an immutable CSR snapshot, the shared read surface for all goroutines.
  • search.Dijkstra — single-source shortest paths; safe to call concurrently on a snapshot CSR.
  • search.BFS — breadth-first traversal with a visit callback; allocation-free on the hot path after the first call.
  • search/centrality.PageRank / DefaultPageRankOptions — power-iteration PageRank, safe to invoke from any number of goroutines on a snapshot CSR.

Further reading

Documentation

Overview

Example 20_concurrent_reads — run three different read-only graph algorithms concurrently over one shared, immutable CSR snapshot.

A single csr.CSR is built once and then read by three goroutines at the same time: one runs a batch of Dijkstra single-source shortest paths, one runs a BFS reach count, and one runs PageRank to convergence. 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. The goroutines publish their findings into a shared map under a mutex (the map is mutated, the CSR is not), and run prints the aggregated results in a fixed key order.

Sample output: run `go run ./examples/20_concurrent_reads` and capture the stdout. Goroutine completion order is non-deterministic, but the reported aggregates (summed Dijkstra cost, BFS reach count, PageRank live-rank count) are deterministic for the hard-coded inputs and serve as the regression baseline a future change should preserve.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL