Repository navigation
feat(recrawl): write a CPU and a heap profile per run, by name - #202
Merged
Merged
Conversation
The timing line answers which phase of the pool held the time, which is the right first question, and it stops being enough as soon as the answer is a phase rather than a knob. A pool spending 55 percent of itself extracting says extraction is the cost and does not say which of the passes inside it is. A run holding 4.5 GB resident says nothing at all about where, because the timing line does not measure memory. --profile-dir writes both profiles and --profile-name decides what they are called. The name is the point: pprof diffs two profiles with -base, and a diff is what a change is worth judging on, so two runs being compared have to be told apart on disk rather than overwriting each other. Profiling wraps the run and nothing else. Setting up the work list is a few hub requests and a Parquet open, and leaving it in makes two runs differ by however long the hub took that morning. The heap profile is written as soon as the run stops and before anything is closed, since the encoders hold their buffers until a shard is sealed and a profile taken after that has lost the largest thing in the run. It runs a GC first, because the question is what the run was holding rather than what it had not got round to freeing. Off unless asked for. It costs a few percent and writes tens of megabytes, and no fleet unit sets it. Refs #200
11 of 12 tasks
This was referenced Aug 23, 2026
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.
Second box of #200, moved up the list by what #201 measured.
The timing line says which phase of the pool held the time, and that is the right first question. It stops being enough as soon as the answer is a phase rather than a knob.
A pool spending 55 percent of itself extracting says extraction is the cost and does not say which of the passes inside it is. A run holding 4.5 GB resident says nothing at all about where, because the timing line does not measure memory. That second one is live right now: #201 bounded the write queue in bytes, the queue peaked at 32.9 MB of its 134.2 MB bound on a real run, and the same binary at 288 workers still climbed to 4.5 GB. The memory is somewhere the run does not report, and a heap profile is the only thing that will say where.
What this adds
--profile-dir DIRwritesNAME.cpu.pprofandNAME.heap.pprof, and--profile-name NAMEpicks the name.The name is the whole point rather than a convenience.
go tool pprof -base first.cpu.pprof second.cpu.pprofdiffs two profiles, and a diff is what a change is worth judging on, so the two runs being compared have to be told apart on disk. A single file that every run overwrites is a profile of the last thing anybody tried.Profiling wraps the run and nothing else. Setting up the work list is a few HuggingFace requests and a Parquet open, which is not what anybody profiling a crawler is asking about, and leaving it in makes two runs differ by however long the hub took that morning.
The heap profile is written as soon as the run stops and before anything is closed, and this is not a detail: the encoders hold their buffers until a shard is sealed, so a profile taken after the sink is closed has already lost the largest thing in the run. It runs a GC first, because a heap profile is of what was reachable at the last collection and the question is what the run was holding rather than what it had not got round to freeing.
Off unless asked for. It costs a few percent and writes tens of megabytes, and no fleet unit sets it, because neither is something a unit that is up for months should be paying by default.
Tests
No directory asked for means no profile started, no files written, and an empty summary line rather than a nil dereference on the stop path.
Both files land under the name given, both are non empty, and the summary line names both so a reader of a log knows where to look.
The name is slugged, since it is going to be something like
288w-6writerstyped on a command line and a stray slash would write the profile somewhere nobody is looking for it.../../etcbecomesetcand an empty name becomesrun.Next
The heap profile from a 288 worker run goes on #200 as a comment, along with whatever it says about the encoder buffers, which is the box this was moved up to unblock.