Skip to content

Topic 00: reading-drepper — Steps 5-6 rewritten, plus a concept→profiler map - #4

Merged
AviAvni merged 1 commit into
masterfrom
docs/drepper-steps-5-6-profiler-map
Aug 4, 2026
Merged

Topic 00: reading-drepper — Steps 5-6 rewritten, plus a concept→profiler map#4
AviAvni merged 1 commit into
masterfrom
docs/drepper-steps-5-6-profiler-map

Conversation

@AviAvni

@AviAvni AviAvni commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Reader feedback on reading-drepper.md: Step 5 was unclear, Step 6's page-walk diagram was under-explained, and neither answered the general question — how do you identify these eight concepts in a program you did not write?

No new measurements. Every figure below already existed in topic 0's notes.md; it is now cross-referenced from the concept that explains it.

Step 5 — dependent loads

The old text asserted memory-level parallelism in one sentence and jumped to code. Now:

  • What "dependent" means at the instruction level — chain[idx], node->next->next, a B-tree child pointer inside the parent you are still waiting for — and the idea it turns on: the cost of a miss is a property of how many other misses can keep it company, not of the miss.
  • An ASCII timeline for both cases: three overlapping misses ≈ 105 ns total (~35 ns apiece) vs three serialized ≈ 300 ns (100 ns apiece).
  • The distinction the step existed to teach and never stated: latency-bound and bandwidth-bound are opposites. A chase moves one 128 B line per ~104 ns ≈ 1.2 GB/s, against the 24–57 GB/s a single core reaches on topic 12's streaming scan — slow while the bus idles, so no layout fix touches it.
  • Both sides priced from this repo's own numbers: lookup_shootout hashmap at n=1e7 with 1024 independent probes → 9.3 ns; cache_ladder at 128 MB, a dependent chase → 104 ns. Same DRAM, same misses, 11× apart on overlap alone — which is the real explanation for why the hash table looked suspiciously flat at ten million keys.
  • Each of cache_ladder's three construction choices justified separately: the dependency lives in the data so nothing can speculate around it; the cyclic permutation both defeats the prefetcher and stops a short sub-cycle living in L1; the cursor carried across criterion iterations, with the original ~25 ns "DRAM" reading cited as the failure it prevents.

Step 6 — virtual memory

The diagram now has the reasoning it assumed:

  • Why a tree at all — a flat table for a 47-bit space at 16 KB pages is 2³³ × 8 B = 64 GB per process.
  • A bit-field diagram of one x86-64 address showing why 9-bit indices (one table = one 4 KB page = 512 entries), with each arrow annotated as a real load whose address comes from the entry just fetched — Step 5's chain, in silicon, before your access can issue.
  • The three naming vocabularies (PML4/PDPT/PD/PT = PGD/PUD/PMD/PTE = L0–L3), since all three appear in the sources.
  • The Apple Silicon variant derived, not asserted: 16 KB granule → 2048-entry tables → 11-bit indices, and 14 + 11 + 11 + 11 = 47, so the walk is three levels, not four.
  • Four reasons it isn't catastrophic: hardware walks it (a page fault is the kernel, microseconds — a different event), the tables are cacheable, walk caches hold partial translations, huge pages turn an L2/PMD entry into a block descriptor.
  • Closed against the measurement: cache_ladder's 87 → 113 ns tail is +26 ns of walk once 32K pages overflow the TLB — not the +400 ns of a fully cold walk, not zero.

New section — "Finding these concepts in a real program"

The general question, answered as a method. Three instruments in reach-for order, leading with the trap: a sampling profiler attributes stall time to the waiting instruction, so memory-bound and compute-bound loops look identical — and this repo has the exhibit (the lookup_shootout flamegraph's 21% SipHash / ~79% inlined probe loop, which no amount of staring could split). Counters name the wall; a differential experiment is the only portable instrument and the one this repo leans on, because a counter says a number is high while a differential proves fixing it would help.

  • A mermaid decision funnel: IPC → branch-miss rate → achieved GB/s → dTLB misses → one of five verdicts (compute / branch / bandwidth / translation / latency-bound), with the last split — same flamegraph, opposite fixes — as its point.
  • An eight-row table: concept → signature in a profile → Linux counter → the differential that proves it. Pad a stride by one line to separate conflict from capacity; sorted vs shuffled indices to price the prefetcher; k interleaved cursors to prove spare MLP (what batched lookup APIs collect); perf c2c plus a 128 B pad for false sharing.
  • Two closing habits: always pair a counter with a differential, and compute useful-bytes ÷ bytes-moved by hand — it needs no profiler and decides row-vs-column in topic 12.

Also

A fourth notes.md question (make lookup_shootout's 1e7 probes dependent and report the new ns/probe — prove the MLP claim with the differential, don't reason it), two new Done when items, a Tools block in the references, and a SESSION-LOG entry.

Verification

  • mdbook build clean.
  • The new flowchart renders to a real SVG under headless Chrome with no syntax error (all five verdict nodes and edge labels present in the rendered DOM).
  • Every relative link on the rendered page resolves, including experiments/benches/cache_ladder.rs, which mdbook copies verbatim.
  • No verify.sh lane or FINDINGS.md row changes — no new measurement was produced.

🤖 Generated with Claude Code

…r map

Reader feedback: Step 5 was unclear, Step 6's page-walk diagram was
under-explained, and neither answered the general question — how do you
identify these eight concepts in a program you did not write?

Step 5 (dependent loads) now defines "dependent" at the instruction level,
contrasts overlapping vs serialized misses on a timeline, and states the
distinction it existed to teach: latency-bound and bandwidth-bound are
opposites. A chase moves one 128 B line per ~104 ns (~1.2 GB/s) against the
24-57 GB/s a single core reaches on topic 12's scan. Both sides are priced
from this repo's own measurements — hashmap at n=1e7 with independent probes
at 9.3 ns vs cache_ladder at 128 MB with a dependent chase at 104 ns, an 11x
gap that is overlap and nothing else. Each of cache_ladder's three
construction choices is justified separately, including the carried cursor
that fixed the ~25 ns "DRAM" reading.

Step 6 (virtual memory) gains what its diagram assumed: why a radix tree at
all (a flat table is 2^33 x 8 B = 64 GB per process), a bit-field diagram
showing why 9-bit indices, each arrow labelled as a real load whose address
comes from the entry just fetched, the three naming vocabularies, the Apple
Silicon derivation (16 KB granule, 11-bit indices, three levels not four),
and the four reasons it is not catastrophic. Closed against the measurement:
cache_ladder's 87 -> 113 ns tail is +26 ns of page walk.

New section "Finding these concepts in a real program" — three instruments in
reach-for order, leading with the trap that a sampling profiler blames the
waiting instruction; a decision funnel from IPC, branch-miss rate, achieved
bandwidth and dTLB misses to one of five verdicts; and an eight-row table
giving each concept its profile signature, its Linux counter, and the
differential experiment that proves it. The differentials carry the weight
because macOS has no perf.

Also a fourth notes.md question (make the 1e7 probes dependent and report the
number), two Done-when items, and a Tools block in the references.

No new measurements — every figure was already in topic 0's notes.md and is
now cross-referenced from the concept that explains it. Verified: mdbook
build clean, the new flowchart renders to a real SVG under headless Chrome
with no syntax error, and every relative link on the rendered page resolves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AviAvni
AviAvni merged commit 1cb091a into master Aug 4, 2026
4 checks passed
DvirDukhan added a commit to DvirDukhan/database-learning-path that referenced this pull request Aug 5, 2026
1009 lines became 3377. A topic about proving things had the loosest
citations in the repo.

**Z3 does not use Nelson-Oppen.** The old Step 4, and a Done-when item,
taught it as Z3's theory-combination method. TACAS'08 names it only as
the traditional approach Z3 *avoids*: "Z3 uses a new theory combination
method that incrementally reconciles models maintained by each theory
[5]" -- ref [5] being Model-based Theory Combination, SMT 2007. The
guide also attributed triggers and e-matching to the tool paper, where
the word "trigger" never appears; that is Bjorner & de Moura, CADE 2007
(its ref [4]), with Simplify (ref [8]) as the "well known approach".
And "CDCL"/"DPLL(T)" were presented as the paper's vocabulary: it lists
two-watch literals, lemma learning from conflict clauses, phase caching
and non-chronological backtracking, and says "DPLL(T)" exactly once,
under relevancy propagation.

Two more Z3 claims were checked and one failed. `euf_egraph.h:22-23`
does not "literally cite egg's deferred congruence repair"; it says the
worklist "is in reality inherited from the legacy SMT solver. It is
*claimed* to have the same effect as delayed congruence table
reconstruction from egg." And Z3 does *not* defer repair the way egg
does -- `merge` fixes the congruence table inline (:542, :551) and only
queues the cascading merges (:596-597 -> :654-677).

**Lean 4's runtime is Counting Immutable Beans, not Perceus.** README:145
had it backwards, and so did the guide's Step 5, which built a ladder in
which Perceus extends borrow inference. Perceus §6 says integrating
selective borrowing is *future work* and "would make certain programs no
longer be garbage free"; §5 says Perceus is "closely based on the
reference counting algorithm in the Lean theorem prover as described by
Ullrich and de Moura". The direction of inheritance is the opposite of
what the guide taught.

That correction has numbers behind it now. "Most inc/dec pairs simply
vanish" under borrow inference is Beans Fig. 6's `-borrow` geomean 1.27
against a 1.24 base -- **2.4%** -- and `const_fold` is 0.90, i.e. 10%
*faster* without it. Reuse is 1.74/1.24 = 40% (3.23x on `rbmap`) and
atomics are 1.89/1.24 = 52%, so reuse is worth roughly 16x what borrow
inference is. "Garbage-free" does not mean peak memory equals live data
either: the paper's definition is that the program retains only
reachable references, for cycle-free programs, and §4's `cfold` measures
no-opt using 11% *less* memory. Lean's `Cons` is 24 bytes at v4.24.0
with an 8-byte header, not Beans §7.1's 32/16.

The AWS CACM'15 table was fabricated. Real figures: S3 804 + 645 lines
of PlusCal, DynamoDB 939 of TLA+, EBS 102 of PlusCal, the lock manager
223 + 318 -- against the old "~800 / ~1000 / ~450". The 35-step trace is
DynamoDB's, not S3's. The author is Michael Deardeuff, not "Deroche".
And the small-scope hypothesis is Daniel Jackson's; it is not in this
paper.

`raft.tla` asserts no properties at all -- zero INVARIANT, PROPERTY or
THEOREM, and `Spec == Init /\ [][Next]_vars` (:469) carries no fairness.
The guide claimed it checks safety. The repo's own README points at
PR AviAvni#4 and the dissertation instead, and the guide now says so and works
out the state space rather than repeating an unsourced "checked only for
tiny bounds".

egg: the union-find does not path-compress in `find` (:30); `find_mut`
(:37) does path *halving* through the grandparent at :40, and the file
is 93 lines, not 60. `machine.rs`'s instruction set is :24-28 and has a
`Lookup` the guide omitted, with `Scan`'s quadratic risk at :66-74.
`pending: Vec<Id>` holds e-node ids, not class ids (:69). The 88x
speedup is now scoped to what it measures: a geometric mean over 32
tests of egg's own math and lambda suites, congruence closure only,
against egg itself rebuilding after every merge -- 21x end to end, with
8 of the 32 hitting the iteration limit.

The Lean guide had zero source anchors; it now cites lean4 v4.24.0's
`lean.h` field by field, and states that there is no pin-table entry
along with the `--ref v4.24.0` command that reproduces it.

Removed as unverifiable: "~10-40 cycles contended" for atomic RC,
replaced with Beans's measured 52% geomean and Perceus §4's 5-59%.

README also had the `SyncCommit = FALSE` row as "123 checked" in a
column headed "states (distinct)" whose other row reads "2583 (1080)";
notes.md records 183 generated / 123 distinct. And notes.md's question
worksheet still listed the old five-per-guide questions, two of which
encoded the Nelson-Oppen and Perceus errors; it now points at each
guide's own list.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants