Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86 - #104192

Merged
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86
Jul 1, 2024
Merged

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86#104192
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Closes#103958 .

Local benchmark result for Action<int> on x86:

MethodJobToolchainMeanErrorStdDevRatio
SingleArgJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe3.464 ns0.0127 ns0.0119 ns0.95
SingleArgJob-KBQPJV\x86-main\corerun.exe3.661 ns0.0222 ns0.0208 ns1.00
ManyCastJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe8.188 ns0.0944 ns0.0883 ns0.97
ManyCastJob-KBQPJV\x86-main\corerun.exe8.483 ns0.0888 ns0.0831 ns1.00
ManyArg_RetFPUJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe5.566 ns0.0338 ns0.0316 ns0.96
ManyArg_RetFPUJob-KBQPJV\x86-main\corerun.exe5.771 ns0.0212 ns0.0177 ns1.00

@ghostghost added the area-VM-coreclr label Jun 29, 2024
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@EgorBot -intel -amd -arm64

usingBenchmarkDotNet.Attributes;usingSystem;namespaceBenchmarkGround{publicstructGCStruct{publicobjecta,b,c,d,e,f,h,g;}publicclassBench{privatereadonlyobjectobj=newobject();privatereadonlyGuidguid=Guid.NewGuid();privatereadonlyGCStructgcStruct=newGCStruct{a=newobject(),g=newobject()};privatereadonlyAction<int>singleArg=(Action<int>)delegate{}+delegate{};privatereadonlyAction<int>manyCast=(Action<int>)delegate{}+delegate{}+delegate{}+delegate{}+delegate{}+delegate{};privatereadonlyFunc<int,double,Guid,GCStruct,object,double>manyArg_RetFPU=(Func<int,double,Guid,GCStruct,object,double>)delegate{return123.456;}+delegate{return654.321;};[Benchmark]publicvoidSingleArg()=>singleArg(42);[Benchmark]publicvoidManyCast()=>manyCast(42);[Benchmark]publicdoubleManyArg_RetFPU()=>manyArg_RetFPU(42,123.0,guid,gcStruct,obj);}}

@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 29, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@EgorBot

Copy link
Copy Markdown
Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 8 logical and 4 physical cores
Job-FAGYWG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-LQQTSO : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MethodToolchainMeanErrorRatio
SingleArgMain8.084 ns0.0012 ns1.00
SingleArgPR6.078 ns0.0012 ns0.75
ManyCastMain19.581 ns0.0313 ns1.00
ManyCastPR14.887 ns0.1134 ns0.76
ManyArg_RetFPUMain14.538 ns0.0340 ns1.00
ManyArg_RetFPUPR11.450 ns0.0147 ns0.79

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
Job-LOUVQC : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Job-TLYWZG : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
MethodToolchainMeanErrorRatio
SingleArgMain15.831 ns0.0109 ns1.00
SingleArgPR8.772 ns0.0017 ns0.55
ManyCastMain38.106 ns0.0234 ns1.00
ManyCastPR21.135 ns0.0015 ns0.55
ManyArg_RetFPUMain17.636 ns0.0024 ns1.00
ManyArg_RetFPUPR16.759 ns0.0071 ns0.95

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Amd
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 8 logical and 4 physical cores
Job-DXSXUL : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-BIEGFU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodToolchainMeanErrorRatio
SingleArgMain13.008 ns0.1016 ns1.00
SingleArgPR6.490 ns0.0071 ns0.50
ManyCastMain100.460 ns0.5200 ns1.00
ManyCastPR18.849 ns0.0302 ns0.19
ManyArg_RetFPUMain14.358 ns0.0464 ns1.00
ManyArg_RetFPUPR12.682 ns0.0120 ns0.88

BDN_Artifacts.zip

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBRTRUE(invokeTraceHelper);

And move the debugging path to be after RET:

 pCode->EmitRET();
#ifdef DEBUGGING_SUPPORTED
// Emit debugging support at the end of the method for better perf
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBR(debuggerCheckEnd);
#endif

This should be even better

@huoyaoyuanhuoyaoyuanJun 29, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was exactly what I did locally, and resulted in exact same codegen with current, at least for x86. The compiled asm code block order did follow IL block order exactly.

@dotnet/jit-contrib Do you have any suggestion on this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

@huoyaoyuanhuoyaoyuanJun 30, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well normalizing the loop closer to a for loop results in about 5% improvement for ManyArg case, but 5% regression for ManyCast.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codegen for x64 now:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esicmp qword ptr [rbx+0x30],0jle SHORT G_M000_IG05G_M000_IG03: ;; offset=0x0012test dword ptr [(reloc 0x7ff824c55210)],512jne SHORT G_M000_IG06G_M000_IG04: ;; offset=0x001Emovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG07movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesimovsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03G_M000_IG05: ;; offset=0x003Eaddrsp,40poprbxpoprsiretG_M000_IG06: ;; offset=0x0045movrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG04G_M000_IG07: ;; offset=0x0051call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 87

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Nice simplification!

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shape seems to be missing the invocation of the trace helper at the end. E.g. if the invocation count is 2, the trace helper should be called 3 times. It is only called 2 times if I am reading the code correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Result codegen:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esijmp SHORT G_M000_IG04G_M000_IG03: ;; offset=0x000Dmovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG08movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesiG_M000_IG04: ;; offset=0x0024test dword ptr [(reloc 0x7ff84ea05210)],512jne SHORT G_M000_IG06G_M000_IG05: ;; offset=0x0030movsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03jmp SHORT G_M000_IG07G_M000_IG06: ;; offset=0x003Bmovrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG05G_M000_IG07: ;; offset=0x0047addrsp,40poprbxpoprsiretG_M000_IG08: ;; offset=0x004Ecall CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 84

JIT reorders the debugging block in front of the ret. Benchmark shows no regression. It's just a bit strange with the unconditional jumps.

@jkotas
jkotas merged commit 3dd3488 into dotnet:mainJul 1, 2024
@huoyaoyuan
huoyaoyuan deleted the multicast-stub-x86 branch July 1, 2024 04:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 31, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider to enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86

3 participants

@huoyaoyuan@EgorBot@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86 - #104192

Merged
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86
Jul 1, 2024
Merged

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86#104192
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Closes#103958 .

Local benchmark result for Action<int> on x86:

MethodJobToolchainMeanErrorStdDevRatio
SingleArgJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe3.464 ns0.0127 ns0.0119 ns0.95
SingleArgJob-KBQPJV\x86-main\corerun.exe3.661 ns0.0222 ns0.0208 ns1.00
ManyCastJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe8.188 ns0.0944 ns0.0883 ns0.97
ManyCastJob-KBQPJV\x86-main\corerun.exe8.483 ns0.0888 ns0.0831 ns1.00
ManyArg_RetFPUJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe5.566 ns0.0338 ns0.0316 ns0.96
ManyArg_RetFPUJob-KBQPJV\x86-main\corerun.exe5.771 ns0.0212 ns0.0177 ns1.00

@ghostghost added the area-VM-coreclr label Jun 29, 2024
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@EgorBot -intel -amd -arm64

usingBenchmarkDotNet.Attributes;usingSystem;namespaceBenchmarkGround{publicstructGCStruct{publicobjecta,b,c,d,e,f,h,g;}publicclassBench{privatereadonlyobjectobj=newobject();privatereadonlyGuidguid=Guid.NewGuid();privatereadonlyGCStructgcStruct=newGCStruct{a=newobject(),g=newobject()};privatereadonlyAction<int>singleArg=(Action<int>)delegate{}+delegate{};privatereadonlyAction<int>manyCast=(Action<int>)delegate{}+delegate{}+delegate{}+delegate{}+delegate{}+delegate{};privatereadonlyFunc<int,double,Guid,GCStruct,object,double>manyArg_RetFPU=(Func<int,double,Guid,GCStruct,object,double>)delegate{return123.456;}+delegate{return654.321;};[Benchmark]publicvoidSingleArg()=>singleArg(42);[Benchmark]publicvoidManyCast()=>manyCast(42);[Benchmark]publicdoubleManyArg_RetFPU()=>manyArg_RetFPU(42,123.0,guid,gcStruct,obj);}}

@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 29, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@EgorBot

Copy link
Copy Markdown
Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 8 logical and 4 physical cores
Job-FAGYWG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-LQQTSO : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MethodToolchainMeanErrorRatio
SingleArgMain8.084 ns0.0012 ns1.00
SingleArgPR6.078 ns0.0012 ns0.75
ManyCastMain19.581 ns0.0313 ns1.00
ManyCastPR14.887 ns0.1134 ns0.76
ManyArg_RetFPUMain14.538 ns0.0340 ns1.00
ManyArg_RetFPUPR11.450 ns0.0147 ns0.79

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
Job-LOUVQC : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Job-TLYWZG : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
MethodToolchainMeanErrorRatio
SingleArgMain15.831 ns0.0109 ns1.00
SingleArgPR8.772 ns0.0017 ns0.55
ManyCastMain38.106 ns0.0234 ns1.00
ManyCastPR21.135 ns0.0015 ns0.55
ManyArg_RetFPUMain17.636 ns0.0024 ns1.00
ManyArg_RetFPUPR16.759 ns0.0071 ns0.95

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Amd
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 8 logical and 4 physical cores
Job-DXSXUL : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-BIEGFU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodToolchainMeanErrorRatio
SingleArgMain13.008 ns0.1016 ns1.00
SingleArgPR6.490 ns0.0071 ns0.50
ManyCastMain100.460 ns0.5200 ns1.00
ManyCastPR18.849 ns0.0302 ns0.19
ManyArg_RetFPUMain14.358 ns0.0464 ns1.00
ManyArg_RetFPUPR12.682 ns0.0120 ns0.88

BDN_Artifacts.zip

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBRTRUE(invokeTraceHelper);

And move the debugging path to be after RET:

 pCode->EmitRET();
#ifdef DEBUGGING_SUPPORTED
// Emit debugging support at the end of the method for better perf
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBR(debuggerCheckEnd);
#endif

This should be even better

@huoyaoyuanhuoyaoyuanJun 29, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was exactly what I did locally, and resulted in exact same codegen with current, at least for x86. The compiled asm code block order did follow IL block order exactly.

@dotnet/jit-contrib Do you have any suggestion on this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

@huoyaoyuanhuoyaoyuanJun 30, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well normalizing the loop closer to a for loop results in about 5% improvement for ManyArg case, but 5% regression for ManyCast.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codegen for x64 now:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esicmp qword ptr [rbx+0x30],0jle SHORT G_M000_IG05G_M000_IG03: ;; offset=0x0012test dword ptr [(reloc 0x7ff824c55210)],512jne SHORT G_M000_IG06G_M000_IG04: ;; offset=0x001Emovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG07movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesimovsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03G_M000_IG05: ;; offset=0x003Eaddrsp,40poprbxpoprsiretG_M000_IG06: ;; offset=0x0045movrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG04G_M000_IG07: ;; offset=0x0051call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 87

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Nice simplification!

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shape seems to be missing the invocation of the trace helper at the end. E.g. if the invocation count is 2, the trace helper should be called 3 times. It is only called 2 times if I am reading the code correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Result codegen:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esijmp SHORT G_M000_IG04G_M000_IG03: ;; offset=0x000Dmovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG08movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesiG_M000_IG04: ;; offset=0x0024test dword ptr [(reloc 0x7ff84ea05210)],512jne SHORT G_M000_IG06G_M000_IG05: ;; offset=0x0030movsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03jmp SHORT G_M000_IG07G_M000_IG06: ;; offset=0x003Bmovrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG05G_M000_IG07: ;; offset=0x0047addrsp,40poprbxpoprsiretG_M000_IG08: ;; offset=0x004Ecall CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 84

JIT reorders the debugging block in front of the ret. Benchmark shows no regression. It's just a bit strange with the unconditional jumps.

@jkotas
jkotas merged commit 3dd3488 into dotnet:mainJul 1, 2024
@huoyaoyuan
huoyaoyuan deleted the multicast-stub-x86 branch July 1, 2024 04:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 31, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider to enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86

3 participants

@huoyaoyuan@EgorBot@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86 - #104192

Merged
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86
Jul 1, 2024
Merged

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86#104192
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Closes#103958 .

Local benchmark result for Action<int> on x86:

MethodJobToolchainMeanErrorStdDevRatio
SingleArgJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe3.464 ns0.0127 ns0.0119 ns0.95
SingleArgJob-KBQPJV\x86-main\corerun.exe3.661 ns0.0222 ns0.0208 ns1.00
ManyCastJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe8.188 ns0.0944 ns0.0883 ns0.97
ManyCastJob-KBQPJV\x86-main\corerun.exe8.483 ns0.0888 ns0.0831 ns1.00
ManyArg_RetFPUJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe5.566 ns0.0338 ns0.0316 ns0.96
ManyArg_RetFPUJob-KBQPJV\x86-main\corerun.exe5.771 ns0.0212 ns0.0177 ns1.00

@ghostghost added the area-VM-coreclr label Jun 29, 2024
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@EgorBot -intel -amd -arm64

usingBenchmarkDotNet.Attributes;usingSystem;namespaceBenchmarkGround{publicstructGCStruct{publicobjecta,b,c,d,e,f,h,g;}publicclassBench{privatereadonlyobjectobj=newobject();privatereadonlyGuidguid=Guid.NewGuid();privatereadonlyGCStructgcStruct=newGCStruct{a=newobject(),g=newobject()};privatereadonlyAction<int>singleArg=(Action<int>)delegate{}+delegate{};privatereadonlyAction<int>manyCast=(Action<int>)delegate{}+delegate{}+delegate{}+delegate{}+delegate{}+delegate{};privatereadonlyFunc<int,double,Guid,GCStruct,object,double>manyArg_RetFPU=(Func<int,double,Guid,GCStruct,object,double>)delegate{return123.456;}+delegate{return654.321;};[Benchmark]publicvoidSingleArg()=>singleArg(42);[Benchmark]publicvoidManyCast()=>manyCast(42);[Benchmark]publicdoubleManyArg_RetFPU()=>manyArg_RetFPU(42,123.0,guid,gcStruct,obj);}}

@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 29, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@EgorBot

Copy link
Copy Markdown
Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 8 logical and 4 physical cores
Job-FAGYWG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-LQQTSO : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MethodToolchainMeanErrorRatio
SingleArgMain8.084 ns0.0012 ns1.00
SingleArgPR6.078 ns0.0012 ns0.75
ManyCastMain19.581 ns0.0313 ns1.00
ManyCastPR14.887 ns0.1134 ns0.76
ManyArg_RetFPUMain14.538 ns0.0340 ns1.00
ManyArg_RetFPUPR11.450 ns0.0147 ns0.79

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
Job-LOUVQC : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Job-TLYWZG : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
MethodToolchainMeanErrorRatio
SingleArgMain15.831 ns0.0109 ns1.00
SingleArgPR8.772 ns0.0017 ns0.55
ManyCastMain38.106 ns0.0234 ns1.00
ManyCastPR21.135 ns0.0015 ns0.55
ManyArg_RetFPUMain17.636 ns0.0024 ns1.00
ManyArg_RetFPUPR16.759 ns0.0071 ns0.95

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Amd
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 8 logical and 4 physical cores
Job-DXSXUL : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-BIEGFU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodToolchainMeanErrorRatio
SingleArgMain13.008 ns0.1016 ns1.00
SingleArgPR6.490 ns0.0071 ns0.50
ManyCastMain100.460 ns0.5200 ns1.00
ManyCastPR18.849 ns0.0302 ns0.19
ManyArg_RetFPUMain14.358 ns0.0464 ns1.00
ManyArg_RetFPUPR12.682 ns0.0120 ns0.88

BDN_Artifacts.zip

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBRTRUE(invokeTraceHelper);

And move the debugging path to be after RET:

 pCode->EmitRET();
#ifdef DEBUGGING_SUPPORTED
// Emit debugging support at the end of the method for better perf
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBR(debuggerCheckEnd);
#endif

This should be even better

@huoyaoyuanhuoyaoyuanJun 29, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was exactly what I did locally, and resulted in exact same codegen with current, at least for x86. The compiled asm code block order did follow IL block order exactly.

@dotnet/jit-contrib Do you have any suggestion on this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

@huoyaoyuanhuoyaoyuanJun 30, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well normalizing the loop closer to a for loop results in about 5% improvement for ManyArg case, but 5% regression for ManyCast.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codegen for x64 now:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esicmp qword ptr [rbx+0x30],0jle SHORT G_M000_IG05G_M000_IG03: ;; offset=0x0012test dword ptr [(reloc 0x7ff824c55210)],512jne SHORT G_M000_IG06G_M000_IG04: ;; offset=0x001Emovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG07movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesimovsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03G_M000_IG05: ;; offset=0x003Eaddrsp,40poprbxpoprsiretG_M000_IG06: ;; offset=0x0045movrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG04G_M000_IG07: ;; offset=0x0051call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 87

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Nice simplification!

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shape seems to be missing the invocation of the trace helper at the end. E.g. if the invocation count is 2, the trace helper should be called 3 times. It is only called 2 times if I am reading the code correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Result codegen:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esijmp SHORT G_M000_IG04G_M000_IG03: ;; offset=0x000Dmovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG08movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesiG_M000_IG04: ;; offset=0x0024test dword ptr [(reloc 0x7ff84ea05210)],512jne SHORT G_M000_IG06G_M000_IG05: ;; offset=0x0030movsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03jmp SHORT G_M000_IG07G_M000_IG06: ;; offset=0x003Bmovrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG05G_M000_IG07: ;; offset=0x0047addrsp,40poprbxpoprsiretG_M000_IG08: ;; offset=0x004Ecall CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 84

JIT reorders the debugging block in front of the ret. Benchmark shows no regression. It's just a bit strange with the unconditional jumps.

@jkotas
jkotas merged commit 3dd3488 into dotnet:mainJul 1, 2024
@huoyaoyuan
huoyaoyuan deleted the multicast-stub-x86 branch July 1, 2024 04:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 31, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider to enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86

3 participants

@huoyaoyuan@EgorBot@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86 - #104192

Merged
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86
Jul 1, 2024
Merged

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86#104192
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Closes#103958 .

Local benchmark result for Action<int> on x86:

MethodJobToolchainMeanErrorStdDevRatio
SingleArgJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe3.464 ns0.0127 ns0.0119 ns0.95
SingleArgJob-KBQPJV\x86-main\corerun.exe3.661 ns0.0222 ns0.0208 ns1.00
ManyCastJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe8.188 ns0.0944 ns0.0883 ns0.97
ManyCastJob-KBQPJV\x86-main\corerun.exe8.483 ns0.0888 ns0.0831 ns1.00
ManyArg_RetFPUJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe5.566 ns0.0338 ns0.0316 ns0.96
ManyArg_RetFPUJob-KBQPJV\x86-main\corerun.exe5.771 ns0.0212 ns0.0177 ns1.00

@ghostghost added the area-VM-coreclr label Jun 29, 2024
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@EgorBot -intel -amd -arm64

usingBenchmarkDotNet.Attributes;usingSystem;namespaceBenchmarkGround{publicstructGCStruct{publicobjecta,b,c,d,e,f,h,g;}publicclassBench{privatereadonlyobjectobj=newobject();privatereadonlyGuidguid=Guid.NewGuid();privatereadonlyGCStructgcStruct=newGCStruct{a=newobject(),g=newobject()};privatereadonlyAction<int>singleArg=(Action<int>)delegate{}+delegate{};privatereadonlyAction<int>manyCast=(Action<int>)delegate{}+delegate{}+delegate{}+delegate{}+delegate{}+delegate{};privatereadonlyFunc<int,double,Guid,GCStruct,object,double>manyArg_RetFPU=(Func<int,double,Guid,GCStruct,object,double>)delegate{return123.456;}+delegate{return654.321;};[Benchmark]publicvoidSingleArg()=>singleArg(42);[Benchmark]publicvoidManyCast()=>manyCast(42);[Benchmark]publicdoubleManyArg_RetFPU()=>manyArg_RetFPU(42,123.0,guid,gcStruct,obj);}}

@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 29, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@EgorBot

Copy link
Copy Markdown
Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 8 logical and 4 physical cores
Job-FAGYWG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-LQQTSO : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MethodToolchainMeanErrorRatio
SingleArgMain8.084 ns0.0012 ns1.00
SingleArgPR6.078 ns0.0012 ns0.75
ManyCastMain19.581 ns0.0313 ns1.00
ManyCastPR14.887 ns0.1134 ns0.76
ManyArg_RetFPUMain14.538 ns0.0340 ns1.00
ManyArg_RetFPUPR11.450 ns0.0147 ns0.79

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
Job-LOUVQC : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Job-TLYWZG : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
MethodToolchainMeanErrorRatio
SingleArgMain15.831 ns0.0109 ns1.00
SingleArgPR8.772 ns0.0017 ns0.55
ManyCastMain38.106 ns0.0234 ns1.00
ManyCastPR21.135 ns0.0015 ns0.55
ManyArg_RetFPUMain17.636 ns0.0024 ns1.00
ManyArg_RetFPUPR16.759 ns0.0071 ns0.95

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Amd
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 8 logical and 4 physical cores
Job-DXSXUL : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-BIEGFU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodToolchainMeanErrorRatio
SingleArgMain13.008 ns0.1016 ns1.00
SingleArgPR6.490 ns0.0071 ns0.50
ManyCastMain100.460 ns0.5200 ns1.00
ManyCastPR18.849 ns0.0302 ns0.19
ManyArg_RetFPUMain14.358 ns0.0464 ns1.00
ManyArg_RetFPUPR12.682 ns0.0120 ns0.88

BDN_Artifacts.zip

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBRTRUE(invokeTraceHelper);

And move the debugging path to be after RET:

 pCode->EmitRET();
#ifdef DEBUGGING_SUPPORTED
// Emit debugging support at the end of the method for better perf
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBR(debuggerCheckEnd);
#endif

This should be even better

@huoyaoyuanhuoyaoyuanJun 29, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was exactly what I did locally, and resulted in exact same codegen with current, at least for x86. The compiled asm code block order did follow IL block order exactly.

@dotnet/jit-contrib Do you have any suggestion on this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

@huoyaoyuanhuoyaoyuanJun 30, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well normalizing the loop closer to a for loop results in about 5% improvement for ManyArg case, but 5% regression for ManyCast.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codegen for x64 now:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esicmp qword ptr [rbx+0x30],0jle SHORT G_M000_IG05G_M000_IG03: ;; offset=0x0012test dword ptr [(reloc 0x7ff824c55210)],512jne SHORT G_M000_IG06G_M000_IG04: ;; offset=0x001Emovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG07movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesimovsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03G_M000_IG05: ;; offset=0x003Eaddrsp,40poprbxpoprsiretG_M000_IG06: ;; offset=0x0045movrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG04G_M000_IG07: ;; offset=0x0051call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 87

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Nice simplification!

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shape seems to be missing the invocation of the trace helper at the end. E.g. if the invocation count is 2, the trace helper should be called 3 times. It is only called 2 times if I am reading the code correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Result codegen:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esijmp SHORT G_M000_IG04G_M000_IG03: ;; offset=0x000Dmovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG08movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesiG_M000_IG04: ;; offset=0x0024test dword ptr [(reloc 0x7ff84ea05210)],512jne SHORT G_M000_IG06G_M000_IG05: ;; offset=0x0030movsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03jmp SHORT G_M000_IG07G_M000_IG06: ;; offset=0x003Bmovrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG05G_M000_IG07: ;; offset=0x0047addrsp,40poprbxpoprsiretG_M000_IG08: ;; offset=0x004Ecall CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 84

JIT reorders the debugging block in front of the ret. Benchmark shows no regression. It's just a bit strange with the unconditional jumps.

@jkotas
jkotas merged commit 3dd3488 into dotnet:mainJul 1, 2024
@huoyaoyuan
huoyaoyuan deleted the multicast-stub-x86 branch July 1, 2024 04:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 31, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider to enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86

3 participants

@huoyaoyuan@EgorBot@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86 - #104192

Merged
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86
Jul 1, 2024
Merged

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86#104192
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Closes#103958 .

Local benchmark result for Action<int> on x86:

MethodJobToolchainMeanErrorStdDevRatio
SingleArgJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe3.464 ns0.0127 ns0.0119 ns0.95
SingleArgJob-KBQPJV\x86-main\corerun.exe3.661 ns0.0222 ns0.0208 ns1.00
ManyCastJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe8.188 ns0.0944 ns0.0883 ns0.97
ManyCastJob-KBQPJV\x86-main\corerun.exe8.483 ns0.0888 ns0.0831 ns1.00
ManyArg_RetFPUJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe5.566 ns0.0338 ns0.0316 ns0.96
ManyArg_RetFPUJob-KBQPJV\x86-main\corerun.exe5.771 ns0.0212 ns0.0177 ns1.00

@ghostghost added the area-VM-coreclr label Jun 29, 2024
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@EgorBot -intel -amd -arm64

usingBenchmarkDotNet.Attributes;usingSystem;namespaceBenchmarkGround{publicstructGCStruct{publicobjecta,b,c,d,e,f,h,g;}publicclassBench{privatereadonlyobjectobj=newobject();privatereadonlyGuidguid=Guid.NewGuid();privatereadonlyGCStructgcStruct=newGCStruct{a=newobject(),g=newobject()};privatereadonlyAction<int>singleArg=(Action<int>)delegate{}+delegate{};privatereadonlyAction<int>manyCast=(Action<int>)delegate{}+delegate{}+delegate{}+delegate{}+delegate{}+delegate{};privatereadonlyFunc<int,double,Guid,GCStruct,object,double>manyArg_RetFPU=(Func<int,double,Guid,GCStruct,object,double>)delegate{return123.456;}+delegate{return654.321;};[Benchmark]publicvoidSingleArg()=>singleArg(42);[Benchmark]publicvoidManyCast()=>manyCast(42);[Benchmark]publicdoubleManyArg_RetFPU()=>manyArg_RetFPU(42,123.0,guid,gcStruct,obj);}}

@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 29, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@EgorBot

Copy link
Copy Markdown
Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 8 logical and 4 physical cores
Job-FAGYWG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-LQQTSO : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MethodToolchainMeanErrorRatio
SingleArgMain8.084 ns0.0012 ns1.00
SingleArgPR6.078 ns0.0012 ns0.75
ManyCastMain19.581 ns0.0313 ns1.00
ManyCastPR14.887 ns0.1134 ns0.76
ManyArg_RetFPUMain14.538 ns0.0340 ns1.00
ManyArg_RetFPUPR11.450 ns0.0147 ns0.79

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
Job-LOUVQC : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Job-TLYWZG : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
MethodToolchainMeanErrorRatio
SingleArgMain15.831 ns0.0109 ns1.00
SingleArgPR8.772 ns0.0017 ns0.55
ManyCastMain38.106 ns0.0234 ns1.00
ManyCastPR21.135 ns0.0015 ns0.55
ManyArg_RetFPUMain17.636 ns0.0024 ns1.00
ManyArg_RetFPUPR16.759 ns0.0071 ns0.95

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Amd
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 8 logical and 4 physical cores
Job-DXSXUL : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-BIEGFU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodToolchainMeanErrorRatio
SingleArgMain13.008 ns0.1016 ns1.00
SingleArgPR6.490 ns0.0071 ns0.50
ManyCastMain100.460 ns0.5200 ns1.00
ManyCastPR18.849 ns0.0302 ns0.19
ManyArg_RetFPUMain14.358 ns0.0464 ns1.00
ManyArg_RetFPUPR12.682 ns0.0120 ns0.88

BDN_Artifacts.zip

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBRTRUE(invokeTraceHelper);

And move the debugging path to be after RET:

 pCode->EmitRET();
#ifdef DEBUGGING_SUPPORTED
// Emit debugging support at the end of the method for better perf
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBR(debuggerCheckEnd);
#endif

This should be even better

@huoyaoyuanhuoyaoyuanJun 29, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was exactly what I did locally, and resulted in exact same codegen with current, at least for x86. The compiled asm code block order did follow IL block order exactly.

@dotnet/jit-contrib Do you have any suggestion on this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

@huoyaoyuanhuoyaoyuanJun 30, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well normalizing the loop closer to a for loop results in about 5% improvement for ManyArg case, but 5% regression for ManyCast.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codegen for x64 now:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esicmp qword ptr [rbx+0x30],0jle SHORT G_M000_IG05G_M000_IG03: ;; offset=0x0012test dword ptr [(reloc 0x7ff824c55210)],512jne SHORT G_M000_IG06G_M000_IG04: ;; offset=0x001Emovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG07movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesimovsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03G_M000_IG05: ;; offset=0x003Eaddrsp,40poprbxpoprsiretG_M000_IG06: ;; offset=0x0045movrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG04G_M000_IG07: ;; offset=0x0051call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 87

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Nice simplification!

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shape seems to be missing the invocation of the trace helper at the end. E.g. if the invocation count is 2, the trace helper should be called 3 times. It is only called 2 times if I am reading the code correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Result codegen:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esijmp SHORT G_M000_IG04G_M000_IG03: ;; offset=0x000Dmovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG08movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesiG_M000_IG04: ;; offset=0x0024test dword ptr [(reloc 0x7ff84ea05210)],512jne SHORT G_M000_IG06G_M000_IG05: ;; offset=0x0030movsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03jmp SHORT G_M000_IG07G_M000_IG06: ;; offset=0x003Bmovrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG05G_M000_IG07: ;; offset=0x0047addrsp,40poprbxpoprsiretG_M000_IG08: ;; offset=0x004Ecall CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 84

JIT reorders the debugging block in front of the ret. Benchmark shows no regression. It's just a bit strange with the unconditional jumps.

@jkotas
jkotas merged commit 3dd3488 into dotnet:mainJul 1, 2024
@huoyaoyuan
huoyaoyuan deleted the multicast-stub-x86 branch July 1, 2024 04:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 31, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider to enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86

3 participants

@huoyaoyuan@EgorBot@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86 - #104192

Merged
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86
Jul 1, 2024
Merged

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86#104192
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Closes#103958 .

Local benchmark result for Action<int> on x86:

MethodJobToolchainMeanErrorStdDevRatio
SingleArgJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe3.464 ns0.0127 ns0.0119 ns0.95
SingleArgJob-KBQPJV\x86-main\corerun.exe3.661 ns0.0222 ns0.0208 ns1.00
ManyCastJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe8.188 ns0.0944 ns0.0883 ns0.97
ManyCastJob-KBQPJV\x86-main\corerun.exe8.483 ns0.0888 ns0.0831 ns1.00
ManyArg_RetFPUJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe5.566 ns0.0338 ns0.0316 ns0.96
ManyArg_RetFPUJob-KBQPJV\x86-main\corerun.exe5.771 ns0.0212 ns0.0177 ns1.00

@ghostghost added the area-VM-coreclr label Jun 29, 2024
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@EgorBot -intel -amd -arm64

usingBenchmarkDotNet.Attributes;usingSystem;namespaceBenchmarkGround{publicstructGCStruct{publicobjecta,b,c,d,e,f,h,g;}publicclassBench{privatereadonlyobjectobj=newobject();privatereadonlyGuidguid=Guid.NewGuid();privatereadonlyGCStructgcStruct=newGCStruct{a=newobject(),g=newobject()};privatereadonlyAction<int>singleArg=(Action<int>)delegate{}+delegate{};privatereadonlyAction<int>manyCast=(Action<int>)delegate{}+delegate{}+delegate{}+delegate{}+delegate{}+delegate{};privatereadonlyFunc<int,double,Guid,GCStruct,object,double>manyArg_RetFPU=(Func<int,double,Guid,GCStruct,object,double>)delegate{return123.456;}+delegate{return654.321;};[Benchmark]publicvoidSingleArg()=>singleArg(42);[Benchmark]publicvoidManyCast()=>manyCast(42);[Benchmark]publicdoubleManyArg_RetFPU()=>manyArg_RetFPU(42,123.0,guid,gcStruct,obj);}}

@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 29, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@EgorBot

Copy link
Copy Markdown
Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 8 logical and 4 physical cores
Job-FAGYWG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-LQQTSO : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MethodToolchainMeanErrorRatio
SingleArgMain8.084 ns0.0012 ns1.00
SingleArgPR6.078 ns0.0012 ns0.75
ManyCastMain19.581 ns0.0313 ns1.00
ManyCastPR14.887 ns0.1134 ns0.76
ManyArg_RetFPUMain14.538 ns0.0340 ns1.00
ManyArg_RetFPUPR11.450 ns0.0147 ns0.79

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
Job-LOUVQC : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Job-TLYWZG : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
MethodToolchainMeanErrorRatio
SingleArgMain15.831 ns0.0109 ns1.00
SingleArgPR8.772 ns0.0017 ns0.55
ManyCastMain38.106 ns0.0234 ns1.00
ManyCastPR21.135 ns0.0015 ns0.55
ManyArg_RetFPUMain17.636 ns0.0024 ns1.00
ManyArg_RetFPUPR16.759 ns0.0071 ns0.95

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Amd
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 8 logical and 4 physical cores
Job-DXSXUL : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-BIEGFU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodToolchainMeanErrorRatio
SingleArgMain13.008 ns0.1016 ns1.00
SingleArgPR6.490 ns0.0071 ns0.50
ManyCastMain100.460 ns0.5200 ns1.00
ManyCastPR18.849 ns0.0302 ns0.19
ManyArg_RetFPUMain14.358 ns0.0464 ns1.00
ManyArg_RetFPUPR12.682 ns0.0120 ns0.88

BDN_Artifacts.zip

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBRTRUE(invokeTraceHelper);

And move the debugging path to be after RET:

 pCode->EmitRET();
#ifdef DEBUGGING_SUPPORTED
// Emit debugging support at the end of the method for better perf
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBR(debuggerCheckEnd);
#endif

This should be even better

@huoyaoyuanhuoyaoyuanJun 29, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was exactly what I did locally, and resulted in exact same codegen with current, at least for x86. The compiled asm code block order did follow IL block order exactly.

@dotnet/jit-contrib Do you have any suggestion on this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

@huoyaoyuanhuoyaoyuanJun 30, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well normalizing the loop closer to a for loop results in about 5% improvement for ManyArg case, but 5% regression for ManyCast.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codegen for x64 now:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esicmp qword ptr [rbx+0x30],0jle SHORT G_M000_IG05G_M000_IG03: ;; offset=0x0012test dword ptr [(reloc 0x7ff824c55210)],512jne SHORT G_M000_IG06G_M000_IG04: ;; offset=0x001Emovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG07movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesimovsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03G_M000_IG05: ;; offset=0x003Eaddrsp,40poprbxpoprsiretG_M000_IG06: ;; offset=0x0045movrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG04G_M000_IG07: ;; offset=0x0051call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 87

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Nice simplification!

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shape seems to be missing the invocation of the trace helper at the end. E.g. if the invocation count is 2, the trace helper should be called 3 times. It is only called 2 times if I am reading the code correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Result codegen:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esijmp SHORT G_M000_IG04G_M000_IG03: ;; offset=0x000Dmovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG08movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesiG_M000_IG04: ;; offset=0x0024test dword ptr [(reloc 0x7ff84ea05210)],512jne SHORT G_M000_IG06G_M000_IG05: ;; offset=0x0030movsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03jmp SHORT G_M000_IG07G_M000_IG06: ;; offset=0x003Bmovrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG05G_M000_IG07: ;; offset=0x0047addrsp,40poprbxpoprsiretG_M000_IG08: ;; offset=0x004Ecall CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 84

JIT reorders the debugging block in front of the ret. Benchmark shows no regression. It's just a bit strange with the unconditional jumps.

@jkotas
jkotas merged commit 3dd3488 into dotnet:mainJul 1, 2024
@huoyaoyuan
huoyaoyuan deleted the multicast-stub-x86 branch July 1, 2024 04:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 31, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider to enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86

3 participants

@huoyaoyuan@EgorBot@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86 - #104192

Merged
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86
Jul 1, 2024
Merged

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86#104192
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Closes#103958 .

Local benchmark result for Action<int> on x86:

MethodJobToolchainMeanErrorStdDevRatio
SingleArgJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe3.464 ns0.0127 ns0.0119 ns0.95
SingleArgJob-KBQPJV\x86-main\corerun.exe3.661 ns0.0222 ns0.0208 ns1.00
ManyCastJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe8.188 ns0.0944 ns0.0883 ns0.97
ManyCastJob-KBQPJV\x86-main\corerun.exe8.483 ns0.0888 ns0.0831 ns1.00
ManyArg_RetFPUJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe5.566 ns0.0338 ns0.0316 ns0.96
ManyArg_RetFPUJob-KBQPJV\x86-main\corerun.exe5.771 ns0.0212 ns0.0177 ns1.00

@ghostghost added the area-VM-coreclr label Jun 29, 2024
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@EgorBot -intel -amd -arm64

usingBenchmarkDotNet.Attributes;usingSystem;namespaceBenchmarkGround{publicstructGCStruct{publicobjecta,b,c,d,e,f,h,g;}publicclassBench{privatereadonlyobjectobj=newobject();privatereadonlyGuidguid=Guid.NewGuid();privatereadonlyGCStructgcStruct=newGCStruct{a=newobject(),g=newobject()};privatereadonlyAction<int>singleArg=(Action<int>)delegate{}+delegate{};privatereadonlyAction<int>manyCast=(Action<int>)delegate{}+delegate{}+delegate{}+delegate{}+delegate{}+delegate{};privatereadonlyFunc<int,double,Guid,GCStruct,object,double>manyArg_RetFPU=(Func<int,double,Guid,GCStruct,object,double>)delegate{return123.456;}+delegate{return654.321;};[Benchmark]publicvoidSingleArg()=>singleArg(42);[Benchmark]publicvoidManyCast()=>manyCast(42);[Benchmark]publicdoubleManyArg_RetFPU()=>manyArg_RetFPU(42,123.0,guid,gcStruct,obj);}}

@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 29, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@EgorBot

Copy link
Copy Markdown
Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 8 logical and 4 physical cores
Job-FAGYWG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-LQQTSO : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MethodToolchainMeanErrorRatio
SingleArgMain8.084 ns0.0012 ns1.00
SingleArgPR6.078 ns0.0012 ns0.75
ManyCastMain19.581 ns0.0313 ns1.00
ManyCastPR14.887 ns0.1134 ns0.76
ManyArg_RetFPUMain14.538 ns0.0340 ns1.00
ManyArg_RetFPUPR11.450 ns0.0147 ns0.79

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
Job-LOUVQC : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Job-TLYWZG : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
MethodToolchainMeanErrorRatio
SingleArgMain15.831 ns0.0109 ns1.00
SingleArgPR8.772 ns0.0017 ns0.55
ManyCastMain38.106 ns0.0234 ns1.00
ManyCastPR21.135 ns0.0015 ns0.55
ManyArg_RetFPUMain17.636 ns0.0024 ns1.00
ManyArg_RetFPUPR16.759 ns0.0071 ns0.95

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Amd
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 8 logical and 4 physical cores
Job-DXSXUL : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-BIEGFU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodToolchainMeanErrorRatio
SingleArgMain13.008 ns0.1016 ns1.00
SingleArgPR6.490 ns0.0071 ns0.50
ManyCastMain100.460 ns0.5200 ns1.00
ManyCastPR18.849 ns0.0302 ns0.19
ManyArg_RetFPUMain14.358 ns0.0464 ns1.00
ManyArg_RetFPUPR12.682 ns0.0120 ns0.88

BDN_Artifacts.zip

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBRTRUE(invokeTraceHelper);

And move the debugging path to be after RET:

 pCode->EmitRET();
#ifdef DEBUGGING_SUPPORTED
// Emit debugging support at the end of the method for better perf
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBR(debuggerCheckEnd);
#endif

This should be even better

@huoyaoyuanhuoyaoyuanJun 29, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was exactly what I did locally, and resulted in exact same codegen with current, at least for x86. The compiled asm code block order did follow IL block order exactly.

@dotnet/jit-contrib Do you have any suggestion on this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

@huoyaoyuanhuoyaoyuanJun 30, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well normalizing the loop closer to a for loop results in about 5% improvement for ManyArg case, but 5% regression for ManyCast.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codegen for x64 now:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esicmp qword ptr [rbx+0x30],0jle SHORT G_M000_IG05G_M000_IG03: ;; offset=0x0012test dword ptr [(reloc 0x7ff824c55210)],512jne SHORT G_M000_IG06G_M000_IG04: ;; offset=0x001Emovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG07movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesimovsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03G_M000_IG05: ;; offset=0x003Eaddrsp,40poprbxpoprsiretG_M000_IG06: ;; offset=0x0045movrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG04G_M000_IG07: ;; offset=0x0051call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 87

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Nice simplification!

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shape seems to be missing the invocation of the trace helper at the end. E.g. if the invocation count is 2, the trace helper should be called 3 times. It is only called 2 times if I am reading the code correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Result codegen:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esijmp SHORT G_M000_IG04G_M000_IG03: ;; offset=0x000Dmovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG08movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesiG_M000_IG04: ;; offset=0x0024test dword ptr [(reloc 0x7ff84ea05210)],512jne SHORT G_M000_IG06G_M000_IG05: ;; offset=0x0030movsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03jmp SHORT G_M000_IG07G_M000_IG06: ;; offset=0x003Bmovrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG05G_M000_IG07: ;; offset=0x0047addrsp,40poprbxpoprsiretG_M000_IG08: ;; offset=0x004Ecall CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 84

JIT reorders the debugging block in front of the ret. Benchmark shows no regression. It's just a bit strange with the unconditional jumps.

@jkotas
jkotas merged commit 3dd3488 into dotnet:mainJul 1, 2024
@huoyaoyuan
huoyaoyuan deleted the multicast-stub-x86 branch July 1, 2024 04:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 31, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider to enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86

3 participants

@huoyaoyuan@EgorBot@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86 - #104192

Merged
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86
Jul 1, 2024
Merged

Enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86#104192
jkotas merged 13 commits into
dotnet:mainfrom
huoyaoyuan:multicast-stub-x86

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Closes#103958 .

Local benchmark result for Action<int> on x86:

MethodJobToolchainMeanErrorStdDevRatio
SingleArgJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe3.464 ns0.0127 ns0.0119 ns0.95
SingleArgJob-KBQPJV\x86-main\corerun.exe3.661 ns0.0222 ns0.0208 ns1.00
ManyCastJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe8.188 ns0.0944 ns0.0883 ns0.97
ManyCastJob-KBQPJV\x86-main\corerun.exe8.483 ns0.0888 ns0.0831 ns1.00
ManyArg_RetFPUJob-OSFVND\x86-3-TuneBranchPrediction\corerun.exe5.566 ns0.0338 ns0.0316 ns0.96
ManyArg_RetFPUJob-KBQPJV\x86-main\corerun.exe5.771 ns0.0212 ns0.0177 ns1.00

@ghostghost added the area-VM-coreclr label Jun 29, 2024
@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

@EgorBot -intel -amd -arm64

usingBenchmarkDotNet.Attributes;usingSystem;namespaceBenchmarkGround{publicstructGCStruct{publicobjecta,b,c,d,e,f,h,g;}publicclassBench{privatereadonlyobjectobj=newobject();privatereadonlyGuidguid=Guid.NewGuid();privatereadonlyGCStructgcStruct=newGCStruct{a=newobject(),g=newobject()};privatereadonlyAction<int>singleArg=(Action<int>)delegate{}+delegate{};privatereadonlyAction<int>manyCast=(Action<int>)delegate{}+delegate{}+delegate{}+delegate{}+delegate{}+delegate{};privatereadonlyFunc<int,double,Guid,GCStruct,object,double>manyArg_RetFPU=(Func<int,double,Guid,GCStruct,object,double>)delegate{return123.456;}+delegate{return654.321;};[Benchmark]publicvoidSingleArg()=>singleArg(42);[Benchmark]publicvoidManyCast()=>manyCast(42);[Benchmark]publicdoubleManyArg_RetFPU()=>manyArg_RetFPU(42,123.0,guid,gcStruct,obj);}}

@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 29, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@EgorBot

Copy link
Copy Markdown
Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 8 logical and 4 physical cores
Job-FAGYWG : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-LQQTSO : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MethodToolchainMeanErrorRatio
SingleArgMain8.084 ns0.0012 ns1.00
SingleArgPR6.078 ns0.0012 ns0.75
ManyCastMain19.581 ns0.0313 ns1.00
ManyCastPR14.887 ns0.1134 ns0.76
ManyArg_RetFPUMain14.538 ns0.0340 ns1.00
ManyArg_RetFPUPR11.450 ns0.0147 ns0.79

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
Job-LOUVQC : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Job-TLYWZG : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
MethodToolchainMeanErrorRatio
SingleArgMain15.831 ns0.0109 ns1.00
SingleArgPR8.772 ns0.0017 ns0.55
ManyCastMain38.106 ns0.0234 ns1.00
ManyCastPR21.135 ns0.0015 ns0.55
ManyArg_RetFPUMain17.636 ns0.0024 ns1.00
ManyArg_RetFPUPR16.759 ns0.0071 ns0.95

BDN_Artifacts.zip

@EgorBot

Copy link
Copy Markdown
Benchmark results on Amd
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 8 logical and 4 physical cores
Job-DXSXUL : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-BIEGFU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
MethodToolchainMeanErrorRatio
SingleArgMain13.008 ns0.1016 ns1.00
SingleArgPR6.490 ns0.0071 ns0.50
ManyCastMain100.460 ns0.5200 ns1.00
ManyCastPR18.849 ns0.0302 ns0.19
ManyArg_RetFPUMain14.358 ns0.0464 ns1.00
ManyArg_RetFPUPR12.682 ns0.0120 ns0.88

BDN_Artifacts.zip

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBRTRUE(invokeTraceHelper);

And move the debugging path to be after RET:

 pCode->EmitRET();
#ifdef DEBUGGING_SUPPORTED
// Emit debugging support at the end of the method for better perf
pCode->EmitLabel(invokeTraceHelper);
pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
pCode->EmitBR(debuggerCheckEnd);
#endif

This should be even better

@huoyaoyuanhuoyaoyuanJun 29, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was exactly what I did locally, and resulted in exact same codegen with current, at least for x86. The compiled asm code block order did follow IL block order exactly.

@dotnet/jit-contrib Do you have any suggestion on this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

@huoyaoyuanhuoyaoyuanJun 30, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well normalizing the loop closer to a for loop results in about 5% improvement for ManyArg case, but 5% regression for ManyCast.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codegen for x64 now:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esicmp qword ptr [rbx+0x30],0jle SHORT G_M000_IG05G_M000_IG03: ;; offset=0x0012test dword ptr [(reloc 0x7ff824c55210)],512jne SHORT G_M000_IG06G_M000_IG04: ;; offset=0x001Emovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG07movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesimovsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03G_M000_IG05: ;; offset=0x003Eaddrsp,40poprbxpoprsiretG_M000_IG06: ;; offset=0x0045movrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG04G_M000_IG07: ;; offset=0x0051call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 87

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise. Nice simplification!

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
Comment on lines 2202 to 2209
pCode->EmitBRTRUE(invokeTraceHelper);
pCode->EmitBR(debuggerCheckEnd); // Tune branch prediction to prefer non-debugging path

pCode->EmitLabel(invokeTraceHelper);

pCode->EmitLoadThis();
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has atypical loop. It looks like the JIT tried to reorder the basic blocks to turn it a more regular loop. I am not sure whether there is anything to fix in the JIT (the JIT would have to have profile data to do better).

It may be still worth it to move TraceHelper call to be at the end in IL. It provides stronger hint about the desired code layout to the JIT and makes this optimization a bit less fragile.

Comment threadsrc/coreclr/vm/comdelegate.cpp Outdated
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shape seems to be missing the invocation of the trace helper at the end. E.g. if the invocation count is 2, the trace helper should be called 3 times. It is only called 2 times if I am reading the code correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Result codegen:

; Assembly listing for method System.Action:IL_STUB_MulticastDelegate_Invoke():this (FullOpts); Emitting BLENDED_CODE for X64 with AVX - Windows; FullOpts code; optimized code; optimized using Synthesized PGO; rsp based frame; partially interruptible; with Synthesized PGO: fgCalledCount is 100; No PGO dataG_M000_IG01: ;; offset=0x0000pushrsipushrbxsubrsp,40movrbx,rcxG_M000_IG02: ;; offset=0x0009xoresi,esijmp SHORT G_M000_IG04G_M000_IG03: ;; offset=0x000Dmovrcx, gword ptr [rbx+0x28]cmpesi, dword ptr [rcx+0x08]jae SHORT G_M000_IG08movrax, gword ptr [rcx+8*rsi+0x10]movrcx, gword ptr [rax+0x08]call[rax+0x18]System.Action:Invoke():thisincesiG_M000_IG04: ;; offset=0x0024test dword ptr [(reloc 0x7ff84ea05210)],512jne SHORT G_M000_IG06G_M000_IG05: ;; offset=0x0030movsxdrcx,esicmprcx, qword ptr [rbx+0x30]jl SHORT G_M000_IG03jmp SHORT G_M000_IG07G_M000_IG06: ;; offset=0x003Bmovrcx,rbxmovedx,esicall System.StubHelpers.StubHelpers:MulticastDebuggerTraceHelper(System.Object,int)jmp SHORT G_M000_IG05G_M000_IG07: ;; offset=0x0047addrsp,40poprbxpoprsiretG_M000_IG08: ;; offset=0x004Ecall CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 84

JIT reorders the debugging block in front of the ret. Benchmark shows no regression. It's just a bit strange with the unconditional jumps.

@jkotas
jkotas merged commit 3dd3488 into dotnet:mainJul 1, 2024
@huoyaoyuan
huoyaoyuan deleted the multicast-stub-x86 branch July 1, 2024 04:39
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 31, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider to enable FEATURE_MULTICASTSTUB_AS_IL for Windows x86

3 participants

@huoyaoyuan@EgorBot@jkotas