Roll back kernel trimming that cannot reach its size target - #437
Draft
pvelesko wants to merge 2 commits into
Draft
Roll back kernel trimming that cannot reach its size target#437pvelesko wants to merge 2 commits into
pvelesko wants to merge 2 commits into
Conversation
…ed back When checkSubroutine() finds a unit above SubroutineThreshold it calls reduceKernelSize(), which marks candidate functions noinline until the unit drops below KernelTotalSizeThreshold. Trimming can consume every candidate and still end above that threshold, so it misses the objective it exists to meet while still turning on subroutine mode for the whole module, deferring inlining to SubroutineInliner and paying the compile-time and register-pressure cost. undo_ineffective_trimming.ll builds a unit where that is unavoidable: three helpers, each called five times, whose three bodies together are already larger than the trimming target, so trimming all three cannot get under it. It checks all three outcomes on the same module - trimming kept with the rollback disabled, trimming rolled back with it enabled, and trimming kept regardless once the unit is over the large-kernel threshold (KernelTotalSizeThreshold * LargeKernelThresholdMultiplier). undo_ineffective_trimming_shared.ll covers the second exclusion: when the trimmed helpers are reachable from another compilation unit the subroutine removes whole duplicate copies from the module, so the trimming is kept even though the target is missed. Both are ocloc tests rather than igc_opt tests, and reach the default SubroutineThreshold of 110000 through the loop trip count rather than through a 110000-instruction .ll: with LoopCountAwareTrimming a basic block counts blockSize * tripCount, so a 56-instruction loop body with a trip count of 200 is worth 11200. That matters because every regkey they set - LoopCountAwareTrimming, MaxUnrollCountForFunctionSizeAnalysis, KernelTotalSizeThreshold, PrintControlKernelTotalSize and UndoIneffectiveKernelTrimming - is declared releaseMode=true, and IGC/ocloc_tests gates its `regkeys` feature on AND(Release, MSVC) rather than on Release alone. The tests therefore still run, and still gate the fix, in a Linux Release build, which is where a regression in this area would actually be shipped from. Both fail without the following commit. Signed-off-by: Paulius Velesko <pvelesko@pglc.io>
Compile time steps by an order of magnitude once a kernel's fully inlined size
crosses SubroutineThreshold. On an STK derived module (11 entry points, one
growing kernel) bmg-g21 goes from 2.1 s to 26.7 s between 1.97 MB and 2.75 MB of
SPIR-V, and the larger module also spills around 310 registers and triggers a
full recompilation.
The trigger is kernel trimming, not register pressure. At 2.75 MB the kernel's
expanded size is 130543, above SubroutineThreshold (110000), so checkSubroutine
calls reduceKernelSize. Trimming then marks all 108 of 108 candidate functions
noinline and still ends at 65243, above its KernelTotalSizeThreshold target of
50000, so it misses the objective it exists to meet while paying every cost:
- isTrimmedFunction suppresses the alwaysinline attribute ProcessFuncAttributes
would otherwise apply, so CheckEnableSubroutine turns on subroutine mode for
the whole module.
- Inlining is deferred from the start of the pipeline to SubroutineInliner at
the end of OptimizeIR, so the switch bodies are never specialized. Every pass
from there on then sees several times more IR.
- The unoptimized code spills around 310 registers, so RetryManager reruns the
entire pipeline, roughly doubling the total again.
When trimming has consumed every candidate and the unit is still above the
threshold, restore the inline decisions it made. Two cases keep their trimming,
because there it pays for itself even when it undershoots: large kernels, using
the same notion of large as isLargeKernelThresholdExceeded, and units whose
trimmed functions are reachable from another compilation unit, where keeping a
subroutine removes whole duplicate copies from the module. The second exclusion
matters on the full 10.5 MB STK module, where several kernels share the trimmed
helpers.
Measured on bmg-g21 with ocloc, regkey off versus on in the same Release build,
three interleaved repetitions each, machine load 2.2 to 2.5:
1.18 MB 0.68 s -> 0.68 s binary byte identical
1.97 MB 2.14 s -> 2.11 s binary byte identical
2.75 MB 26.87 s -> 3.95 s 310 reg spill and 1 retry removed, binary -0.4%
3.53 MB 39.04 s -> 6.13 s 310 reg spill and 1 retry removed, binary +2.1%
10.5 MB 190.69 s -> 203.80 s spills and retry unchanged, binary -1.7%
The 10.5 MB module is the one the exclusions are written for, and it is worth
being precise about what happens there: its largest unit stays trimmed, but the
guards are evaluated per unit, so the module's smaller units are still rolled
back and the output is therefore not identical. The compile time is unchanged
within noise and no spill or recompilation is added, which is the property that
matters; the module is simply not where the win is.
Controlled by UndoIneffectiveKernelTrimming, default on. With the regkey off the
pass takes exactly the path it takes today.
Fixes intel#420
Signed-off-by: Paulius Velesko <pvelesko@pglc.io>
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.
EstimateFunctionSize::trimCompilationUnit()can consume every trimming candidate and still leave the unit aboveKernelTotalSizeThreshold. The noinline decisions are kept anyway, which switches the module to subroutine mode: a trimmed function loses the alwaysinline attributeProcessFuncAttributeswould give it, inlining is deferred toSubroutineInlinerat the end ofOptimizeIR, the passes in between run several times slower on the un-inlined code, and register pressure rises enough to spill and rerun the pipeline throughRetryManager. The trimming bought nothing, since the size target was missed regardless, and cost a large multiple of the compile time.Measured on the 2.75 MB module attached to #420: expanded size 130543 against
SubroutineThreshold110000, all 108 of 108 candidates trimmed, final size 65243 against a threshold of 50000, 306 register spill and a retry.IGC_ControlKernelTotalSize=0on the same module compiles in 5.6 s with no spill. On a real application kernel with the module cache cleared, master averaged 49.6 s and this change 6.9 s, with runs interleaved to cancel load drift.When trimming cannot reach the threshold, roll this attempt's decisions back and keep the unit inlined.
isTrimmed()identifies exactly the decisions made here, since every pool member wasFA_BEST_EFFORT_INLINEwhen pooled (the implicit-arg pool is restored throughsetForceInline()). Two cases keep their trimming because it pays even when it undershoots: units abovethreshold * LargeKernelThresholdMultiplier, the same notion of large asisLargeKernelThresholdExceeded, and units whose trimmed functions are reachable from another compilation unit, where a subroutine removes whole duplicate copies from the module. A new regkeyUndoIneffectiveKernelTrimming(default on) disables the rollback.Two ocloc lit tests under
features/kernel_trimming: one where rollback must happen (master printsThe size is still above threshold even though all candidates are trimmedand keeps the subroutines), and one where a trimmed function is shared with a second unit and the trimming must be kept. Both fail on master and pass with this change. Note thatIGC/ocloc_testsonly builds with-DIGC_OPTION__ENABLE_OCLOC_LIT_TESTS=ON.Fixes #420