-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement fake hot/cold splitting and corresponding stress mode #69763
Implement fake hot/cold splitting and corresponding stress mode #69763
Conversation
Tagging subscribers to this area: @JulieLeeMSFT Issue DetailsThe The
|
74705d6
to
dce5861
Compare
cc @dotnet/jit-contrib. |
dce5861
to
d748767
Compare
d748767
to
b5021de
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments
src/coreclr/jit/compiler.cpp
Outdated
// Loop alignment is disabled for cold blocks | ||
assert((fgFirstBB->bbFlags & BBF_COLD) == 0); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like there's no need for this assert, since we're not aligning this loop. In fact, we're specifically choosing to NOT align it, here.
src/coreclr/jit/ee_il_dll.cpp
Outdated
@@ -1122,9 +1122,39 @@ void Compiler::eeDispLineInfos() | |||
* (e.g., host AMD64, target ARM64), then VM will get confused anyway. | |||
*/ | |||
|
|||
void Compiler::eeAllocMem(AllocMemArgs* args, UNATIVE_OFFSET hotSizeRequest, UNATIVE_OFFSET coldSizeRequest) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should have the same signature as allocMem
: no need to pass hotSizeRequest
or coldSizeRequest
since they can be found from args->hotCodeSize
and args->coldCodeSize
.
src/coreclr/jit/ee_il_dll.cpp
Outdated
{ | ||
#ifdef DEBUG | ||
// Fake splitting implementation: hot section = hot code + 4K buffer + cold code | ||
const UNATIVE_OFFSET buffer = 4096; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: buffer
is a pretty generic name. How about:
const UNATIVE_OFFSET buffer = 4096; | |
const UNATIVE_OFFSET fakeSplittingBuffer = 4096; |
src/coreclr/jit/ee_il_dll.cpp
Outdated
args->hotCodeSize = hotSizeRequest + buffer + coldSizeRequest; | ||
args->coldCodeSize = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that changing args
fields works out because the caller doesn't (I think) consult these values afterwards. To be perfectly clean, you might want to copy args
to a local temp before modifying its fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha; for brevity, I simply copied and restored hotCodeSize and coldCodeSize, so args' input members behave like they're read-only.
src/coreclr/jit/ee_il_dll.cpp
Outdated
// Fake splitting currently does not handle unwind info for cold code | ||
if (isColdCode && JitConfig.JitFakeProcedureSplitting()) | ||
{ | ||
return; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be better to move this immediately before the call to reserveUnwindInfo
, below, and add:
JITDUMP("reserveUnwindInfo for cold code with JitFakeProcedureSplitting enabled: ignoring cold unwind info\n");
which would add a line to the JitDump in this case, but also let the normal reserveUnwindInfo
printf be executed first.
src/coreclr/jit/ee_il_dll.cpp
Outdated
// Fake splitting currently does not handle unwind info for cold code | ||
if (pColdCode && JitConfig.JitFakeProcedureSplitting()) | ||
{ | ||
return; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here as for reserveUnwindInfo: move to just before the allocUnwindInfo
call, and add a JITDUMP printout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the earlier out is only for DEBUG
, is that intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the JitFakeProcedureSplitting
flag is defined only on Debug/Checked builds. For now, we skip unwind info for cold sections only when fake-splitting.
src/coreclr/jit/jitconfigvalues.h
Outdated
// For now, this disables unwind info for | ||
// cold sections, breaking stack walks. | ||
// Set COMPlus_GCgen0size=1000000 to avoid | ||
// running the GC and breaking things. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// running the GC and breaking things. | |
// running the GC which requires stack walking. |
src/coreclr/jit/compiler.cpp
Outdated
// JitFakeProcedureSplitting overrides JitNoProcedureSplitting with a fake splitting implementation | ||
if (JitConfig.JitFakeProcedureSplitting()) | ||
{ | ||
opts.compProcedureSplitting = true; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why this is necessary. If someone sets COMPlus_JitFakeProcedureSplitting=1
and COMPlus_JitNoProcedureSplitting=Main
, it seems like that should work: do fake splitting on all functions except Main
.
It seems like the real issue is:
opts.compProcedureSplitting = !opts.compDbgCode;
maybe should be:
if (opts.compDbgCode && !JitConfig.JitFakeProcedureSplitting())
{
opts.compProcedureSplitting = false;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, looks like I misunderstood what precedence JitFakeProcedureSplitting should (not) take over other configurations. I've changed the logic so JitFakeProcedureSplitting can be partially overriden by other configs, but can still override opts.compDbgCode if enabled.
src/coreclr/jit/ee_il_dll.cpp
Outdated
// Fake splitting currently does not handle unwind info for cold code | ||
if (pColdCode && JitConfig.JitFakeProcedureSplitting()) | ||
{ | ||
return; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the earlier out is only for DEBUG
, is that intentional?
b5021de
to
0af0032
Compare
The 20 failing checks all seem to build/run correctly, but fail when sending the results to Helix -- an Azure DevOps API is returning a 401 error, citing a bad |
That was a general infrastructure problem, now fixed. You can request the testing to be rerun. (See the "Re-run" "button" on "runtime" here: https://github.com/dotnet/runtime/pull/69763/checks?check_run_id=6607345705 (rerunning the "parent" will cause rerunning all the failed "children")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Implementation splits after first basic block in method, assuming there is more than one block. Accompanying this implementation are the following fixes: - Loop alignment is disabled for cold blocks, as moving blocks into the cold section may invalidate the initial decision to align. - Long jumps are no longer reduced to short jumps if crossing hot/cold sections.
0af0032
to
5965366
Compare
Thank you! I couldn't find any re-run button in the Checks UI, but force-pushing an empty commit triggered the run. |
The
COMPlus_JitFakeProcedureSplitting
configuration flag enables testing the JIT's hot/cold splitting functionality independent of the VM. This configuration "fakes" splitting by requesting only a hot section from the VM with the following layout:hot code + 4KB buffer + cold code
. Hot/Cold code pointers are manually set to their respective sections of the buffer, and the JIT continues to operate as if the VM allocated separate sections. This implementation does not currently split unwind information, and only reserves/allocates it for the hot section -- this breaks stack walks and, thus, the GC. When using this configuration, suppress the GC with a large memory threshold (ex: setCOMPlus_GCgen0size=1000000
).The
COMPlus_JitStressProcedureSplitting
configuration flag runs a stress mode for hot/cold code splitting by always splitting a method after its first basic block. This mode exposed the following behaviors incompatible with splitting that have been corrected: