This patch adds block frequency analysis to LoopUnswitch pass to recognize hot/cold regions. For cold regions the pass only performs trivial unswitches since they do not increase code size, and for hot regions everything works as before. This helps to minimize code growth in cold regions and be more aggressive in hot regions. Currently the default cold regions are blocks with frequencies below 20% of function entry frequency, and it can be adjusted via -loop-unswitch-cold-block-frequency flag. The entire feature is controlled via -loop-unswitch-with-block-frequency flag and it is off by default.
Details
Diff Detail
Event Timeline
lib/Transforms/Scalar/LoopUnswitch.cpp | ||
---|---|---|
496 | I chose to use loop preheader's frequency for coldness comparison. This will ignore the size of the loop because it does not count backedges or iteration numbers. My reasoning is that if the loop was only entered once in the pass 1000 executions, we should be confident that it will never be entered again. Therefore, no matter how many times the loop iterates, it shouldn't affect our decision. But I could see the other way around. I am open to change it if anyone prefers the other way. |
lib/Transforms/Scalar/LoopUnswitch.cpp | ||
---|---|---|
76 | What are your plans for testing this and getting it enabled by default? | |
82 | Have you explored this parameter at all? My guess would be that disabling loop unswitching should only be done for *really* cold loops. I have no evidence, but a cool loop in a really hot function seems like something we should unswitch. | |
433 | Minor comment clarification: "function entry baseline frequency". "Loops with headers below this frequency" | |
496 | I feel pretty strongly that making decisions based on the number of times the loop executes rather than the number of times the header executes is likely to produce negative results. Consider: if (loop invariant) { } if (loop invariant2) { } if (loop invariant3) { } do work(); } Your change would completely disable unswitching in this loop which seems non-ideal. | |
498 | Given there's already handling for OptForSize within UnswitchIfProfitable, I'd like to see the handling combined. A cleanup change to move the OptForSize check here - I think that's NFC right? - and then using that in this patch would help. | |
test/Transforms/LoopUnswitch/cold-loop.ll | ||
21 | This is perhaps a bad example. In this particular case, we could split the loop into two subloops without code growth. Maybe tweak your example so it's more obvious why we want to avoid unswitching this? |
The main problem with the patch is that the cold loop detection can have many false positives. With profile feedback, cold loops can be reliably detected but not with the method described in this patch. Without profile feedback, the cold loop filtering should probably not be turned on by default unless it is optimized for size. When the filtering is not on, we should not pay the compile time penalty of computing BFI unconditionally either. Cong has recently made it possible to compute BFI without relying on running the wrapper pass.
lib/Transforms/Scalar/LoopUnswitch.cpp | ||
---|---|---|
496 | The hotness of the loop should be measured by the frequency of the hottest BB in the loop relative to the function entry. With PGO, you will need to check the execution count of the hottest BB in the loop (relative to some threshold set by the program summary). | |
test/Transforms/LoopUnswitch/cold-loop.ll | ||
21 | Unswitching duplicates the loop, so size overhead is the loop setup code (in this case probably just a branch associated with backedge). |
Do you mean by using other profiles together with BFI or using BFI differently?
Without profile feedback, the cold loop filtering should probably not be turned on by default unless it is optimized for size.
Yes, you are correct. Initially I was thinking this could take cold loop's unswitch budget to hot loop and thus make hot loop unswitch more aggressively. But it turned out separate loops will have the same initial unswitch budget and not effect each other. So it will only optimize code size.
When the filtering is not on, we should not pay the compile time penalty of computing BFI unconditionally either. Cong has recently made it possible to compute BFI without relying on running the wrapper pass.
Do you mean this patch http://reviews.llvm.org/D11196 ?
lib/Transforms/Scalar/LoopUnswitch.cpp | ||
---|---|---|
82 | I was thinking about that too. Maybe adding function hotness to its loops is a good idea, ie. lower this threshold for hot functions and increase it for cold functions. | |
496 | I think the hottest BB should be the header, assuming its inner loops will be processed by their own unswitch passes. | |
498 | I will have a look at this. | |
test/Transforms/LoopUnswitch/cold-loop.ll | ||
21 | Yes, this is not a good case. I will add some side-effect code after the first condition (so the first one is still trivial and not affected by coldness check, but unswitching the second one will grow code size). |
By the correct cold loop detection, do you specifically mean to check the execution count of the hottest BB in the loop?
Update patch to fix the following issues:
- Use standalone BFI without relying on the wrapper pass.
- Use the hottest block (loop header in this case) to determine the loop's hotness.
- Update the non-trivial condition test case to have effect of code growth.
I also plan to have a follow-up patch to introduce function_entry_count to hot/cold region detection.
Minor comments below. Once addressed, I plan to give a LGTM unless David has further comments in the meantime.
lib/Transforms/Scalar/LoopUnswitch.cpp | ||
---|---|---|
75 | This comment is a bit deceptive. I'd just drop it. | |
82 | Right now, we're only limiting code size increase. Please adjust the string. | |
86 | I think the default here should be much lower, but we can tune this in separate changes. My guess here would be something like 1/100 rather than 20/100. I also think we should have an absolute cutoff, but again, we can tune separately. | |
174 | Add a comment which says what these are used for and that they're only used with the PGO option enabled. | |
test/Transforms/LoopUnswitch/cold-loop.ll | ||
40 | Please add a couple of instructions here per previous comment on test case. |
lib/Transforms/Scalar/LoopUnswitch.cpp | ||
---|---|---|
86 | This is not frequency. Maybe call it 'ColdnessThreshold' or something. | |
87 | It is the 'Coldness threshold in percent. The loop header frequency (relative to the entry frequency) is compared with the threshold to determine if non-trivial unswitching should be enabled.' | |
446 | Note that this also triggers the check when PGO is not enabled (e.g, with static prediction). To check if PGO is on, do: HasProfileData = F.getEntryCount().hasValue(); if (HasProfileData) { .. |
lib/Transforms/Scalar/LoopUnswitch.cpp | ||
---|---|---|
446 | Do you mean that without PGO enabled, this should never be triggered? |
Right. I won't trust static profile data to tell you what is cold or hot.
In fact I think the right way to determine coldness is to use the
actual profile count of the loop header to determine loop is cold or
not -- the actual exec count of a loop header can be computed by
multiplying the relative frequency with the the F.getEntryCount(). A
common threshold can be 0 or 1, which means the loop rarely executes.
David
This comment is a bit deceptive. I'd just drop it.