Repository navigation
DDIR: count what each arrangement's chunks hold, with tickets the chunks carry - #1065
Open
frankmcsherry wants to merge 2 commits into
Open
frankmcsherry wants to merge 2 commits into
frankmcsherry wants to merge 2 commits into
Conversation
DD's arrangement logging reported how many updates each batch holds, not how much memory they take. Batches now report their heap bytes too, and the spine logs them next to the lengths, so a consumer can maintain a trace's bytes the same way it maintains its updates. DD: - `Chunk::heap_bytes`, a metadata operation like `len`: the bytes the chunk's buffers hold, by capacity where the implementor can see it. `VecChunk` counts its vector; `ColChunk` counts a resident trie's columns by length, and a paged chunk nothing. - `SpineBatch::heap_bytes`, zero unless a batch measures itself. `ChunkBatch` sums its chunks and `Rc<B>` passes through; the ord_neu batches stay at zero. - `BatchEvent` and `DropEvent` carry `bytes`, and `MergeEvent` carries `bytes1`, `bytes2` and `complete_bytes`. The spine computes them only when it has a logger. They balance as the lengths do. DDIR: - `CorgiChunk` counts its key and value columns, time lanes and diffs. Corgi leaves count their stored length, as corgi builds them at size and shows no capacity; offsets, time lanes and diffs count capacity. - A `diag.bytes` source, `((worker, op), ())` with the count carrying bytes, maintained from the same events as `diag.records` and retracted the same way. - Views `diag.arrangements`, `(program, statement, operator, address) -> (updates, bytes)` summed over workers, and `diag.arrangements_by_program`. On reach over a 1M-node, 4M-edge random graph, the arrangements report 296.8 MiB, and the allocator's live allocations made by chunk-building code total 296.9 MiB. That is 87% of the program's live heap, all of which a drop releases. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XsakwFGwtESwZ1gSYwG4o
The first commit had DD's batches report their heap bytes and the spine log them with its batch, merge and drop events. That counted only what the spine holds, relied on every holder to report, and needed a DD API. This moves the accounting into DDIR's `CorgiChunk` and reverts the DD changes: no `Chunk` or `SpineBatch` method and no logging fields; DD is as it was. - A chunk's shared contents carry a ticket. Made with the contents, it logs +(updates, bytes) against the account current at that moment, and its drop logs the same amounts back. Clones share the ticket and log nothing; a copy of the contents gets its own. Bytes are the key and value columns, the time lanes and the diffs, by stored length. - An account is an operator's, with a handle to DDIR's logger `"ddir/chunks"`; one is current per worker. The arrange operator's batcher is wrapped to enter its account around `insert` and `extract`, the reduce enters its own when it writes output chunks, and the chunk methods that make chunks from chunks (merge, extract, advance, settle) make them under their inputs' account, wherever they run. What is made with none current goes to the worker's unattributed account. With no logger registered there are no accounts, and tickets measure nothing. - `diag.records` and `diag.bytes` come from these events, so they count chunks in batchers, merges in progress and batches readers still hold, not only the trace. An operator that shuts down while its chunks live hands what they hold to the unattributed account, which `diag.arrangements` shows as program `(unattributed)`. On reach over a 1M-node, 4M-edge random graph the arrangements' chunks hold 8,418,369 updates in 256.9 MiB, 78% of the 330 MiB the program's drop releases by the system allocator's count; SCC (300K nodes, 1M edges) 347.3 MiB of 461 MiB, 75%. The rest is mostly capacity allocated and never written, and the reduce's working state. Nothing was unattributed in either. With diagnostics on, the events cost under 0.02 ms per tick on a small reach workload, and the two views 0.24 ms, as in the first commit. Implemented and validated by a Claude subagent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XsakwFGwtESwZ1gSYwG4o
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DDIR's chunks now count themselves: each chunk's contents log their updates and bytes when they are made and again when they drop, against the account of the operator that made them, on DDIR's own logging stream. DDIR has a
diag.arrangementsview that reads "this arrangement's chunks hold N updates in B bytes", wherever the chunks are: its trace, its batcher, a merge in progress, or a batch a reader still holds. On reachability over a 1M-node, 4M-edge graph, the chunks hold 256.9 MiB, 78% of the live heap the program's drop releases; the rest is mostly capacity never written, which is not counted by design.DD: no change. The first commit's
Chunk::heap_bytes,SpineBatch::heap_bytesand byte fields on the batch, merge and drop events are reverted.DDIR
A ticket in
CorgiChunk's shared contents. Made with the contents, it logs+(updates, bytes)against the account current at that moment and remembers it; its drop logs the same amounts back. Clones of a chunk share the ticket and log nothing; a copy of the contents gets its own ticket in the same account. Bytes are the key and value columns, the time lanes and the diffs, by stored length rather than capacity. Every chunk with contents is built by one constructor, so none escapes a ticket.An account is an operator's id with a handle to the
"ddir/chunks"logger, and one is current per worker (runtime/account.rs). Accounts are entered in three places:AccountedBatcher, passed toarrange_core) to enter its account aroundinsertandextract, which covers the chunker's flushes and the batcher's merges;peek_identifierjust beforereduce_with_tacticbuilds the operator, as nothing in between allocates one);CorgiChunk'smerge,extract,advanceandsettlemake their outputs under their inputs' account, which covers the batch builder and the spine's merges wherever they run, with no change to DD.Chunks made with no operator's account current go to the worker's unattributed account. With no logger registered there are no accounts, and tickets measure nothing.
diag.recordsanddiag.bytescome from these events instead of DD's batch, merge and drop events, keyed by(worker, operator)as before. Viewsdiag.arrangements(program, statement, operator, address → updates, bytes, summed over workers) anddiag.arrangements_by_program. Unattributed holdings are their own row, program(unattributed); an operator that shuts down while its chunks are still alive hands what they hold to that row, where their drops are counted.Validated against the allocator, with a server built on the system allocator and live heap read from
vmmap:The allocator counts capacity. The first commit counted it too, and found 40 MiB (reach) and 64 MiB (SCC) of capacity beyond length, mostly time lanes a merge reserves for both inputs and diffs grown by doubling; with it the shares would be 90% and 89%. On SCC the chunks hold 40 updates the spine's events did not show: the batchers of the two
labelsarrangements hold them for later iterations.Cost: median ms of CPU per tick, reachability on a 10K-node, 40K-edge random graph replacing 10 edges a tick, five rounds of builds and settings alternating.
Tickets cost nothing measurable with diagnostics off, and the events under 0.02 ms per tick with them on. The two new views add 0.24 ms per tick, as in the first commit.
What it does not count, largest first:
CorgiChunk, and the chunker's blocks between a batcher insert and its next flush.CorgiContainers. Containers made from chunks (as_collection, peeks) share the chunks' key and value columns, which are counted once, in the chunk.Checks:
cargo test -p differential-dataflow -p differential-dogs3 -p interactive -p ddir-server(485 passed) and the 1,000-case release difftest. A new test shows a merge's output counted in its inputs' account, and an input a reader keeps after the merge counted until the reader drops it.tests/diag.rschecks that every arrangement holds at least 16 bytes per update, that the per-program view equals the sum of the per-arrangement view, and that after 30 install/drop cycles nothing is left in the dropped program's accounts or the unattributed one.Implemented and validated by a Claude subagent.
🤖 Generated with Claude Code
https://claude.ai/code/session_019XsakwFGwtESwZ1gSYwG4o