[llvm] Backport ORC dependence propagation performance fix - #23249
Open
pcanal wants to merge 1 commit into
Open
Conversation
Test Results 23 files 23 suites 3d 15h 36m 51s ⏱️ For more details on these failures, see this check. Results for commit 7685bba. ♻️ This comment has been updated with latest results. |
Member
|
Thanks, the description should be heavily condensed, it's a lot of LLM bla bla |
Member
You mean the commit message, yes? |
…tion.
Backport of upstream LLVM commit db5ffb04ab0b5c4e193d2c31e635e69f42deaaaf
("[ORC] WaitingOnGraph perf: faster dependence propagation.", PR
llvm/llvm-project#183272), which is not present in the LLVM 22.
This fixes the intermittent timeout of rootest-root-io-cpp11Containers-unorderedMap.
The roottest test root/io/cpp11Containers/unorderedMap (and related)
showed a bistable runtime: the same test, in the same
environment, would sometimes complete quickly and sometimes timeout
(300s). With a debug build of LLVM we observed the test to usually
run in ~800 seconds (40 seconds with a Release build) and sometimes take
~18 hours.
This commit replaces the core dependence propagation algorithm in
WaitingOnGraph to avoid worst-case behavior in the common case where
dependence graphs are sparse. This algorithm showed up as the underlying
cause of the bug in #179611.
For each call to MaterializationResponsibility::notifyEmitted,
WaitingOnGraph would build the transitive closure of all SuperNodes
whose "waiting on" relationships were affected by the newly emitted
symbols, then propagate any remaining unemitted dependencies through
this transitive closure graph. This approach is simple, but pushes the
algorithm towards n^2 complexity even for sparse dependence graphs.
The new propagation algorithm:
1. Inverts the edge direction in the SymbolDependenceMap data structure:
SymbolDepMap[SN] now contains the set of SuperNodes that depend on SN,
rather than the set that SN depends upon.
2. Pushes dependencies through the SymbolDepMap iteratively until it
reaches a fixed point.
This updated algorithm converges much more quickly than the original for
the testcase reported in the issue, and for other cases tested so far.
Validation
----------
* Performance on a synthetic dependence chain (time in simplify()):
N old new speedup
500 0.0485s 0.0002s 221x
1000 0.3762s 0.0005s 811x
2000 1.9467s 0.0014s 1376x
4000 21.4621s 0.0034s 6321x
The old implementation scales ~8-11x per doubling of N; the new one
~2.4x.
References
----------
* Upstream commit: db5ffb04ab0b5c4e193d2c31e635e69f42deaaaf
* Upstream PR: llvm/llvm-project#183272
* Upstream issue: llvm/llvm-project#179611
* Follow-up (perf regression infrastructure):
llvm/llvm-project#183251
The commit was released in LLVM 23.
---
Diagnosed, backported and validated with the assistance of Claude Opus 5.
pcanal
force-pushed
the
orc_loading_performance
branch
from
September 4, 2026 15:15
7685bba to
2fe3d65
Compare
Member
Author
|
The commit log has been shortened (core is now the LLVM commit log). |
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.
Backport of upstream LLVM commit db5ffb04ab0b5c4e193d2c31e635e69f42deaaaf ("[ORC] WaitingOnGraph perf: faster dependence propagation.", PR llvm/llvm-project#183272), which is not present in the LLVM 22.
This fixes the intermittent timeout of rootest-root-io-cpp11Containers-unorderedMap.
Analysis
The roottest test root/io/cpp11Containers/unorderedMap (and related) showed a bistable runtime: the same test, in the same environment, would sometimes complete quickly and sometimes timeout (300s). With a debug build of LLVM we observed the test to usually run in ~800 seconds (40 seconds with a Release build) and sometimes take ~18 hours. Profiling attributed essentially all of the extra time to WaitingOnGraph::propagateSuperNodeDeps().
propagateSuperNodeDeps() computed, for every SuperNode, the transitive closure of its dependence set with a per-node DFS. Its total cost is
which means the result depends critically on the order in which nodes are expanded:
The visitation order came from iterating a DenseMap keyed on SuperNode*, and DenseMapInfo<T*>::getHashValue() hashes the raw pointer value:
so the traversal order is a function of ASLR and heap layout. Two runs of the same binary on the same input could therefore land on opposite ends of that complexity range. The algorithm also mutated Deps in place while iterating (Deps = std::move(Reachable)) and de-duplicated work on pop rather than on push, which further inflated the worklist.
A second amplifier was sinkDeps(), which re-expanded the computed closures back into per-symbol dependence maps. This inflated the dependence sets and prevented the Coalescer from merging SuperNodes (coalescing requires exact equality of the dependence sets), so the graph stayed large and every subsequent emit paid the cost again.
Because both traversal orders compute the same closure, this never showed up as a correctness failure -- only as the 800s / 18h runtime split.
Solution
Adopt upstream's rewrite:
Validation
Differential test: 4000 randomised emit/fail scenarios driven through both the old and new implementations produce identical Ready and Failed sets (assertions enabled).
Performance on a synthetic dependence chain (time in simplify()):
The old implementation scales ~8-11x per doubling of N; the new one ~2.4x.
libLLVMOrcJIT.a builds and links cleanly.
References
WaitingOnGraphllvm/llvm-project#179611Build a performance test suite for WaitingOnGraph llvm/llvm-project#183251
The fix first ships in LLVM 23 (verified present at tag llvmorg-23.1.0 and absent from release/22.x as of 22.1.8), so it cannot be picked up from an LLVM 22 point release. This patch can be dropped once ROOT's vendored LLVM moves to 23 or later.
Diagnosed, backported and validated with the assistance of Claude Opus 5.