perf: parse the files of a program in parallel #214
Loading…
Reference in a new issue
No description provided.
Delete branch "loop/206-parallel-parse"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #206
What
torb parse,torb checkandtorb buildparse the files of the program in parallel withstd/parallel. The texts are read in order on the caller, the parses run withlist.parallel().map(parse).toList(), and the trees come back in input order.SourceTree.parsedInParallel(paths)(compiler/src/project/source-tree.trb) reads each file the tree keeps no parse of, parses them in parallel and keeps the parses.parsedAtalready answers a kept parse first, sobuildGraphreads them in the order it always parsed. Diagnostics keep their order.parsedAhead(tree, paths)(compiler/src/semantics/check.trb) parses ahead every file of every package a check of the paths parses, which isbuildGraphwithoutonlyReached. Finding the packages moved intopackagesOf, whichfrontOfuses too.torb parse(checkSyntax) reads the files in order up to the first one that cannot be read, parses those in parallel, then prints the diagnostics in file order. After that comes the read error, as before.torb buildgoes throughbuildParsedAhead.buildstays synchronous fortorb runandtorb test. Under--timings, the parse ahead is one more line,parsing, in parallel.Why the commands are tasks the top level does not wait for
await()is only allowed in a function that returns aTask, so the parallel parse cannot run inside the synchronousbuildGraph. It cannot run behind anawait()at the top level ofmain.trbeither. That would make the whole entry file a task, and the VM thattorb runandtorb teststart refuses to run its scheduler from inside one (the package manager and the language server work around the same constraint). Soparse,checkandbuild(without--embed-vm) start their work as a task the top level does not wait for. It runs once the top level is done (docs/design/CONCURRENCY.md), astorb lspdoes.endWiththen endstorbthe way the top level did:?:error: <the error>, exit code 1.Only
std/paralleland the prelude's tasks are used, which the seed already compiles (no change to the seed). Run, test, the language server and format still parse one file after the other.Test
compiler/tests/parallel-parse.test.trbchecks five scripts, one of them broken in the middle. With the parse ahead, every file gets the same tree and diagnostics as a sequentialparse. The check reports the broken file's diagnostics as the check that parses one after the other does, in the same order.Measurement
Main (
6374a3f0) and this branch are bootstrapped on the forge runner and run alternately, 3 runs each, mean (as in PERFORMANCE.md 9.5). Wall time is the point of the change; processor time is reported too. The same job comparestorb tokensandtorb astover every.trbfile, the output ofparseandcheck(including a failing file among several), and thetools/agree.sh emithashes between the two binaries. The workflow ran on the throwaway branchloop/206-measure(run 326: https://git.torb.dev/torbscript/language/actions/runs/326); the numbers and the equivalence results are in PERFORMANCE.md 9.8 and in the comment below.Review at
578a581b(CI green). Code and test look sound; the acceptance criteria of #206 that decide whether this PR is taken at all are not evidenced yet.Checklist: Tests ok (parallel-parse.test.trb, failing file among five, same diagnostics order; CI green on this head) · Correctness ok (read order, input-order trees, exit codes and
error:report preserved inendWith; unreadable-file behaviour kept) · Security ok · Data n/a · Workflows n/a (none changed) · Architecture n/a · Scope/acceptance: gap · Docs: gapmainin the alternating-run comparison on the CI runner", small build "not slower", otherwise "measured and not taken" in PERFORMANCE.md 9.7). The PR body only says "Numbers and the link follow in a comment and in PERFORMANCE.md 9.8"; there is no comment, no link, and the diff does not touchdocs/PERFORMANCE.md. Please post the alternating-run numbers (torb parse std compiler tests,torb check tests/conformance/adts.trb,torb build --emit-c ..., 3 runs each, mean, runner core count) and record them in PERFORMANCE.md in this PR. If the parallel parse is not faster or the small build is slower, the issue says this PR is not taken and the numbers go in as "measured and not taken".torb tokens/torb astover every.trbfile, sameparse/checkoutput including a failing file, sametools/agree.sh emithashes asmain) is claimed in the body for a throwaway-branch job, but the result is not in the PR. Link the run or paste the outcome with the numbers from 1. The unit test covers five scripts only, not the repository-wide equivalence the issue asks for.Non-blocking:
parsedInParallelparses an unreadable/missing file as empty text (text(normalized) ?? ""); the same as before only ifparsedAtdoes likewise - worth a one-line comment saying so.Addressing the review at
578a581b. New head:ef9d94e3(docs and one comment only, no code change).Measurement (acceptance criterion 2). Forge CI job
measure-206, run 326: https://git.torb.dev/torbscript/language/actions/runs/326 (throwaway branchloop/206-measure). Runner: 16 cores, AMD EPYC-Genoa.main6374a3f0vs this PR at578a581b, both bootstrapped there, alternating, 3 runs each, mean:torb parse std compiler teststorb check tests/conformance/adts.trbtorb build --emit-c tests/conformance/adts.trbtorb check compilertorb build --emit-c ./compilerThe 830-file parse is faster, the small check and the small build are not slower, so the PR is taken, not "measured and not taken". The cost is processor time (+6% to +66%). The compiler's own build is level within the noise. Recorded in PERFORMANCE.md 9.8 in this push.
Equivalence (acceptance criterion 1), same job:
torb tokensandtorb astover all 1145.trbfiles: identical (SYNTAX identical, sha256 prefix 74ccb5ef86b5d27b on both);parse std compiler tests,check tests/conformance/adts.trb, andparse/checkof a directory with a broken file between two good ones: byte-identical output and exit codes (OUTPUT parse|check|broken-parse|broken-check identical);tools/agree.sh emit: both binaries give the same hash for all nine targets (AGREE identical).Non-blocking point:
parsedInParallelreads an unreadable file as empty text, asparsedAtandparsedEverythingdo; the doc comment now says so.Note for merge order: PRs #205 and #207 also add a section 9.8 to PERFORMANCE.md; whoever merges second renumbers.
Follow-up at
d909cab8(docs only): PERFORMANCE.md 9.8 and the comment above said the parse case has 830 files; run 326 parsed 1014 (60 problems in 2 of 1014 files, both binaries), 830 was the count of 9.5. The text now says 1014 and that the three runs of one case differ by up to 4% (build --emit-c ./compiler, PR runs 28.93 to 30.05 s). Raw lines of the measurement, verbatim from the log of run 326 (https://git.torb.dev/torbscript/language/actions/runs/326):The throwaway branch
loop/206-measureis deleted; the run and its log stay on the forge.Re-review at
d909cab8(CI green, combined status success). Both earlier blockers are resolved; the code is unchanged since578a581bapart from a doc comment.Checklist: Tests ok (parallel-parse.test.trb: five scripts, one broken, same trees and diagnostics order as sequential; CI green on this head) · Correctness ok (read order, input-order trees, exit codes and
error:report kept inendWith; unreadable-file behaviour kept incheckSyntax) · Security ok · Data n/a · Workflows n/a · Architecture n/a (no new boundary) · Scope ok (exactly #206, run/test/lsp/format untouched) · Docs ok (PERFORMANCE.md 9.8).Earlier findings:
tools/agree.sh emithashes identical, with the raw log lines quoted. Criterion 1 met.parsedInParallel(unreadable file as empty text) is added.Non-blocking: the issue names PERFORMANCE.md 9.7 for the numbers while this PR uses 9.8 (9.7 is taken, fine); #205/#207 also add a 9.8, as you noted whoever merges second renumbers. No one-core measurement; stated openly in the doc.