Skip to content

docs(r25): the capstone comparison — example 56 + chapter 56 (closes Part 14) - #25

Merged
patterncatalyst merged 3 commits into
mainfrom
docs/r25-ch56-capstone-comparison
Aug 27, 2026
Merged

patterncatalyst merged 3 commits into
mainfrom
docs/r25-ch56-capstone-comparison

Conversation

@patterncatalyst

Copy link
Copy Markdown
Owner

Closes Part 14 and the C++ compendium. examples/56-capstone-comparison + _docs/56-capstone-comparison.md + two diagrams.

The argument

ch50–ch55 each produced real numbers that cannot be compared with each other, because each measured its own model, with its own instrument, on its own workload. This chapter is the correction: one workload, seven models, two uniform instruments.

cpp/src/workload.hpp is shared verbatim — 8 tasks × 5000 rounds = 40000 folds into one accumulator, commutative so the total is order-independent.

Gate B runs before any comparison is drawn: all seven models fold to 0xb75768f6610642a0. A model with a different total is not a differently-priced answer to the same question — it is a wrong one.

What the two instruments found

tier arms distinct_tids futex calls
one thread sequential, coroutine (ch53), fiber (ch54) 1 1 / 1 / 1
many + a lock pthreads, std-thread, boost-thread 8 230 / 427 / 451
many + a strand asio (ch55) 8 51
many + a lock senders (opt-in) 8 562
  • The line falls at one-thread-or-many, and nothing else predicts it — a plain loop, a stackless frame and a stackful fiber all land on the same side.
  • A single-threaded model reaches the futex zero times. Not cheaply; not at all.
  • std::mutex and boost::mutex are the same futex — the stdlib-vs-Boost axis predicted nothing.

Magnitudes are never gated (locks spanned 230–549, strand 51–65 across runs); the three tiers are.

The ch49 debt, settled

ch49 promised a live P2300 comparison "if and only if the toolchain has caught up." It has not — __cpp_lib_senders is still undefined, <execution> is still C++17 par. So the seventh arm links NVIDIA's stdexec reference implementation and says so in its output, its gate message, and its recipe.

stdexec is not in ConanCenter, not in Fedora, and upstream publishes no releases — so cpp/conan/recipe/ pins tag nvhpc-26.05 by tarball sha256. A tag can be moved; a hash cannot.

Isolation, proven both ways

The six-model core takes zero third-party fetch (system Boost only). The senders arm is an isolated Conan sub-target on ch46's pattern.

  • verify.lua → PASS 57 / FAIL 0 with Conan present (B, C, D, E, F all real)
  • verify.lua → PASS 52 / FAIL 0 with conan hidden from PATH and the Conan build tree deleted, gate E printing an informational SKIP

The second run is the acceptance criterion, because it is what CI does on fedora:44.

  • test-all-examples.py --only 56-capstone-comparison → 1 passed, 0 failed, 0 skipped
  • validate.py → OK; 9/9 chapter code blocks verbatim; no banned words; ch39/46/49–55 cross-refs resolve

One flagged deviation

assets/diagrams/specs/56.py is committed, against the convention of treating diagram specs as throwaway. Regenerating Figure 55.1 by hand last iteration cost real effort for a four-number change; committing the spec makes the next correction a re-run. Easy to drop if you'd rather keep the convention.

Opens r25, the capstone that closes Part 14. No example or chapter yet;
this is the plan file and the audit that had to precede it.

Overlap check: every one of the six models already has a chapter that owns
it, so the capstone's novelty is NOT any model -- it is the UNIFORM
INSTRUMENT. Each prior chapter measured its own model with its own
instrument on its own workload, so no two numbers in the compendium are
strictly comparable. ch56 runs one workload through all six and measures
with the same two instruments (gettid() tid sets, and futex counts under
strace). Two further axes -- bytes per paused computation, and the four
cancellation shapes -- are tables ASSEMBLED from named constants carried
forward, not re-measured, and the chapter must say so.

D1 (user): live P2300 senders via NVIDIA stdexec + Conan, over
forward-looking prose. The audit cleared one obstacle:

- __cpp_lib_senders is STILL undefined on GCC 16.1.1 and <execution> is
  still the C++17 parallel-algorithms header (201902), so ch49's measured
  claim stands and must be repeated, not dropped
- stdexec is not in ConanCenter (0 recipes) and not in Fedora
- upstream's own conanfile is named p2300 and versions off git.get_commit()
- there are no GitHub releases, only nvhpc-* snapshot tags
- so: an in-repo recipe pinning tag nvhpc-26.05 by tarball + sha256
  9d2396fecd604698c1eae58f0cb6e4517aa727013846240d1a7b2f35e49884dc
- proven to compile AND run at that pinned tag on BOTH compilers:
  sync_wait=42, on_pool=7 under g++ 16.1.1 and clang++ 22.1.8

The binding constraint is recorded: the six-model core must take zero
third-party fetch, and the senders arm is an isolated Conan sub-target on
ch46's pattern, gated skip-if-absent, so CI on fedora:44 without Conan and
the runner without network both stay green. Acceptance requires proving
the SKIP path, not just the happy path.

validate.py OK.
…ckpoint)

Example complete and green; chapter, README and diagrams still to write.

examples/56-capstone-comparison: the comparison Part 14 promised. Every
prior chapter measured its own model with its own instrument on its own
workload, so no two numbers in the compendium were comparable. This runs
ONE workload (8 tasks x 5000 rounds = 40000 folds into one accumulator,
workload.hpp, shared verbatim) through every model and measures them all
with the SAME two instruments.

Gate B is the capstone gate and runs before any comparison is drawn: all
seven models fold to the identical total 0xb75768f6610642a0. A model that
disagreed would not be a differently-priced answer to the same question,
it would be a wrong one.

Instrument 1, gettid() (ch50's, ch49's terms):
  sequential / coroutine / fiber   1 tid   -- 8 tasks in flight, one CPU
  pthreads / std-thread / boost-thread / asio / senders   8 tids

Instrument 2, strace -f -c -e trace=futex (ch51's), three stable tiers:
  one thread, any model      1 call     (nothing to synchronize)
  a lock                     230-549    (park and wake a blocked thread)
  a strand                   51-65      (defer a handler instead)

The seventh model is P2300 senders, and it is the ch49 debt come due.
__cpp_lib_senders is STILL undefined here, so this links the NVIDIA
stdexec REFERENCE implementation and says so in the output, in the gate
message, and in the recipe. stdexec is not in ConanCenter and not in
Fedora, so cpp/conan/recipe/ pins tag nvhpc-26.05 by tarball sha256.

Isolation is the load-bearing design choice, on ch46's pattern: the
six-model core needs no Conan, no network, and no third-party package.
PROVEN BOTH WAYS this session -- PASS 57/0 with gates D, E and F all
real, and PASS 52/0 with conan hidden from PATH and the conan build tree
deleted, where gate E degrades to an informational SKIP. That second run
is what fedora:44 CI will do.

Gotcha worth the chapter: CMakeDeps gates include dirs behind
$<$<CONFIG:Release>:...>, so the conan preset MUST use Release. A
RelWithDebInfo configure silently drops them and the arm cannot find its
headers while still "finding" the package.

runner 1 passed; validate.py OK.
Completes r25 and closes the C++ compendium. Example was green at the
previous checkpoint; this adds the chapter, both diagrams, and rewrites
the example README off the template text.

_docs/56-capstone-comparison.md — the chapter's argument is that ch50-ch55
produced real numbers that cannot be compared with each other, because each
measured its own model with its own instrument on its own workload. One
workload and two uniform instruments is the correction, and gate B (all
seven models fold to 0xb75768f6610642a0) runs before any comparison is
drawn, because a model with a different total is answering a different
question.

Findings the chapter is built around:
- the line falls at one-thread-or-many, and nothing else predicts it:
  sequential, coroutine and fiber each hold 8 tasks in flight on exactly
  1 OS thread; the other four report 8
- a single-threaded model reaches the futex ZERO times, whatever its
  suspension machinery costs elsewhere
- a blocked thread is the expensive event: locks 230/427/451 vs a strand
  at 51 for byte-identical work
- std::mutex and boost::mutex are the same futex -- the stdlib-vs-Boost
  axis predicted nothing, structure predicted everything
- P2300 is still absent from the standard library, so the seventh arm is
  NVIDIA's reference implementation and says so in output, gate and recipe

Two diagrams, catalogued: 56-one-workload-seven-models (the three tiers)
and 56-two-assembled-axes (the paused-computation and cancellation tables,
labelled as assembled rather than measured).

DEVIATION, flagged deliberately: assets/diagrams/specs/56.py is COMMITTED,
against the convention of treating diagram specs as throwaway. Regenerating
Figure 55.1 by hand last iteration cost real effort for a four-number
change; committing the spec makes the next correction a re-run.

Gates: verify.lua PASS 57/0 with Conan present and PASS 52/0 with it hidden
(the SKIP path is the acceptance criterion, and is what fedora:44 CI does);
runner 1 passed; validate.py OK; 9/9 chapter code blocks verbatim.
@patterncatalyst
patterncatalyst merged commit 1b18a05 into main Aug 27, 2026
8 checks passed
@patterncatalyst
patterncatalyst deleted the docs/r25-ch56-capstone-comparison branch August 27, 2026 20:58
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.

1 participant