Linked list vs CSR adjacency

Storing adjacency is a three-way trade among mutability, compactness, and cache-locality: doubly-linked lists chase pointers across the heap but insert and delete cheaply (right for mutating OLTP graphs), while CSR streams a node's neighbors contiguously through cache but must rebuild to add an edge (right for read-mostly analytic sweeps).

Previously

The doubly-linked list mutates cheaply, but it scatters across memory — every hop is a potential cache miss. CSR packs a node's neighbors into one contiguous slice that flies through cache, at the price of never being able to cheaply add an edge. That's the storage fork; now both layouts are just bytes. The thing missing is a way to ASK questions of them without hand-coding pointer walks.

Scene 06

Linked list vs CSR adjacency

  1. Watch
  2. Try it
  3. Predict
  4. Capture
ADJACENCY OFAliceneighbors → [2, 3, 9, 10]workload: OLAP-readonlyDOUBLY-LINKED LISTfixed-size records, scattered on the heappointer chaseCSR — OFFSET + NEIGHBOR ARRAYSneighbors[ offset[v] : offset[v+1] ]offset[]neighbors[] (packed, contiguous)neighbors[0:0]compact, contiguousTHREE-WAY TRADEmutabilityList: insert/delete in place. CSR: rebuil…compactnessList: a record + two pointers per edge. C…cacheLocalityList: pointers scatter across the heap (c…Alice's four friends as a doubly-linked list — each cell lives somewhere else on the heap.
What to watch for

Last scene the relationship chain was a doubly-linked list because it's easy to insert and delete. But pointer-chasing across the heap is cache-hostile. Watch Alice's four friends drawn first as that scattered list — each cell at its own heap address, chained by prev/next — then drawn a second way: packed into one contiguous slice that reads straight through cache. Same four neighbors, two completely different memory layouts.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

CSR.neighbors
a vertex's neighbors are one contiguous slice — a single cache stream
def neighbors(v, offset, neighbor_array):
start = offset[v]
end = offset[v + 1]
# contiguous run — streams through cache
return neighbor_array[start:end]
insert_edge
in-place splice on the list vs full rebuild on CSR
def insert_list(cell, after): # doubly-linked list
cell.next = after.next # rewire two pointers
after.next = cell # O(1) — splice in place
def insert_csr(v, u, offset, arr): # CSR — no free slot
arr = rebuild_with_edge(v, u) # O(total edges) rebuild
return arr
List.walk
the read-side cost the left pane draws — each hop follows a pointer to a new heap address
def walk_list(head): # doubly-linked relationship chain
cell = head
while cell is not None:
visit(cell.id) # use this neighbor
cell = cell.next # pointer → some other heap address
# next cell is elsewhere → likely a cache miss
Store.pickLayout
match the layout to whether the graph mutates or just gets read
def pick_layout(workload):
if workload.mutates_constantly:
return LINKED_LIST # splice edges in place, skip rebuilds
else: # read-mostly: never inserts
return CSR # contiguous neighbors stream through cache

Where this sits in Build a graph database (Neo4j / Dgraph-style)

Scene 06 of 16, in the The store act — Store records, index-free adjacency, the CSR trade.. Doubly-linked relationship lists are cache-hostile but mutate in place; Compressed Sparse Row packs a node's neighbors into one contiguous slice that streams through cache but rebuilds the whole array to add a single edge — the OLTP-graph vs OLAP-graph storage fork.

Up next. We can store and traverse a graph by hand, but nobody writes raw pointer-walks. We need a language where you describe the SHAPE you want — Alice's friends' friends — and the engine compiles it into a traversal. And the surprising part: for expansion, that compiler needs no join planner at all, because the pattern maps straight onto pointer-chasing.

All 16 scenes in Build a graph database (Neo4j / Dgraph-style) · Every curriculum

Built with Arqly
Every scene in Build a graph database (Neo4j / Dgraph-style) builds on the one before it.All 16 Build a graph database (Neo4j / Dgraph-style) scenes