From e930ff7181b954d9b7f9c48a7c559c20fde428a7 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Tue, 23 Dec 2025 22:01:31 -0500 Subject: [PATCH 01/33] Async refactor --- .../Runners/BuildRunner.cs | 4 - build/common.props | 2 +- .../Attributes/AsyncCallerTypeAttribute.cs | 15 + .../AggressivelyOptimizeMethods.cs | 19 + .../BenchmarkDotNet.Weaver.targets | 2 +- ...chmarkDotNet.Weaver.0.16.0-develop-1.nupkg | Bin 442258 -> 0 bytes ...chmarkDotNet.Weaver.0.16.0-develop-2.nupkg | Bin 0 -> 452261 bytes .../src/WeaveAssemblyTask.cs | 67 +- src/BenchmarkDotNet/BenchmarkDotNet.csproj | 6 + src/BenchmarkDotNet/Code/CodeGenerator.cs | 88 +- .../Code/DeclarationsProvider.cs | 330 ++++++- .../BenchmarkSynchronizationContext.cs | 76 ++ src/BenchmarkDotNet/Engines/Engine.cs | 205 ++-- src/BenchmarkDotNet/Engines/EngineJitStage.cs | 49 +- .../Engines/EngineParameters.cs | 18 +- src/BenchmarkDotNet/Engines/EngineStage.cs | 9 +- src/BenchmarkDotNet/Engines/IEngine.cs | 6 +- src/BenchmarkDotNet/Engines/IterationData.cs | 29 +- .../WorkloadContinuerAndValueTaskSource.cs | 67 ++ .../Extensions/ReflectionExtensions.cs | 30 +- src/BenchmarkDotNet/Helpers/AwaitHelper.cs | 8 +- .../IlGeneratorDefaultValueExtensions.cs | 152 +++ .../IlGeneratorEmitOpExtensions.cs | 43 + .../Parameters/SmartParamBuilder.cs | 7 +- .../Running/BenchmarkRunnerClean.cs | 76 +- .../Running/BenchmarkRunnerDirty.cs | 138 ++- .../Running/BenchmarkSwitcher.cs | 105 +- .../Templates/BenchmarkProgram.txt | 12 +- .../Templates/BenchmarkType.txt | 100 +- .../Toolchains/DotNetCli/DotNetCliExecutor.cs | 107 +-- src/BenchmarkDotNet/Toolchains/Executor.cs | 41 +- src/BenchmarkDotNet/Toolchains/IExecutor.cs | 10 +- .../Emit/Implementation/ConsumableTypeInfo.cs | 61 -- .../Emitters/AsyncCoreEmitter.cs | 903 ++++++++++++++++++ .../Emitters/AsyncStateMachineEmitter.cs | 570 +++++++++++ .../Emitters/RunnableEmitter.cs | 867 ++++------------- .../Emitters/SetupCleanupEmitter.cs | 71 ++ .../Emitters/SyncCoreEmitter.cs | 163 ++++ .../Runnable/RunnableConstants.cs | 27 +- .../Runnable/RunnableProgram.cs | 83 -- .../Runnable/RunnableReflectionHelpers.cs | 70 +- .../Implementation/Runnable/RunnableReuse.cs | 118 --- .../InProcess/Emit/InProcessEmitExecutor.cs | 18 +- .../InProcess/Emit/InProcessEmitRunner.cs | 141 +++ .../InProcess/NoEmit/BenchmarkAction.cs | 24 +- .../NoEmit/BenchmarkActionFactory.cs | 244 +++-- .../NoEmit/BenchmarkActionFactory_Base.cs | 4 +- .../BenchmarkActionFactory_Implementations.cs | 432 ++++++--- .../NoEmit/InProcessNoEmitExecutor.cs | 15 +- .../InProcess/NoEmit/InProcessNoEmitRunner.cs | 20 +- .../NoEmit/InProcessNoEmitToolchain.cs | 22 +- .../Toolchains/MonoWasm/WasmExecutor.cs | 13 +- .../Toolchains/Roslyn/Generator.cs | 27 +- .../AllSetupAndCleanupTest.cs | 64 +- .../BenchmarkTestExecutor.cs | 71 +- .../CodeGenTests.cs | 148 +++ .../CustomAwaitable.cs | 17 + .../CustomEngineTests.cs | 8 +- .../CustomTask.cs | 109 +++ .../CustomTaskAndAwaitableTests.cs | 233 +++++ .../NaiveRunnableEmitDiff.cs | 39 +- .../InProcess.EmitTests/Runnable_0.cs | 33 - .../InProcessEmitTest.cs | 38 + .../InProcessTest.cs | 68 +- .../RunAsyncTests.cs | 79 ++ .../ToolchainTest.cs | 7 +- .../Engine/EngineMethodImplTests.cs | 30 + .../Engine/EnumerateStagesTests.cs | 18 +- .../BenchmarkDotNet.Tests/Mocks/MockEngine.cs | 21 +- .../Mocks/Toolchain/MockToolchain.cs | 21 +- 70 files changed, 4688 insertions(+), 2030 deletions(-) create mode 100644 src/BenchmarkDotNet.Annotations/Attributes/AsyncCallerTypeAttribute.cs create mode 100644 src/BenchmarkDotNet.Annotations/Attributes/CompilerServices/AggressivelyOptimizeMethods.cs delete mode 100644 src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.16.0-develop-1.nupkg create mode 100644 src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.16.0-develop-2.nupkg create mode 100644 src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs create mode 100644 src/BenchmarkDotNet/Engines/WorkloadContinuerAndValueTaskSource.cs create mode 100644 src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorDefaultValueExtensions.cs delete mode 100644 src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/ConsumableTypeInfo.cs create mode 100644 src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncCoreEmitter.cs create mode 100644 src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncStateMachineEmitter.cs create mode 100644 src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SetupCleanupEmitter.cs create mode 100644 src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SyncCoreEmitter.cs delete mode 100644 src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableProgram.cs delete mode 100644 src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableReuse.cs create mode 100644 src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs create mode 100644 tests/BenchmarkDotNet.IntegrationTests/CodeGenTests.cs create mode 100644 tests/BenchmarkDotNet.IntegrationTests/CustomAwaitable.cs create mode 100644 tests/BenchmarkDotNet.IntegrationTests/CustomTask.cs create mode 100644 tests/BenchmarkDotNet.IntegrationTests/CustomTaskAndAwaitableTests.cs delete mode 100644 tests/BenchmarkDotNet.IntegrationTests/InProcess.EmitTests/Runnable_0.cs create mode 100644 tests/BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs create mode 100644 tests/BenchmarkDotNet.Tests/Engine/EngineMethodImplTests.cs diff --git a/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs b/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs index a6c371cedb..c58761d2d9 100644 --- a/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs +++ b/build/BenchmarkDotNet.Build/Runners/BuildRunner.cs @@ -8,7 +8,6 @@ using Cake.Common.Tools.DotNet.Workload.Install; using Cake.Core; using Cake.Core.IO; -using System; using System.IO; using System.Linq; @@ -51,7 +50,6 @@ public void PackWeaver() { MSBuildSettings = context.MsBuildSettingsRestore, }; - MaybeAppendArgument(restoreSettings); context.DotNetRestore(weaverPath.GetDirectory().FullPath, restoreSettings); context.Information("BuildSystemProvider: " + context.BuildSystem().Provider); @@ -63,7 +61,6 @@ public void PackWeaver() Configuration = context.BuildConfiguration, Verbosity = context.BuildVerbosity }; - MaybeAppendArgument(buildSettings); context.DotNetBuild(weaverPath.FullPath, buildSettings); var packSettings = new DotNetPackSettings @@ -72,7 +69,6 @@ public void PackWeaver() MSBuildSettings = context.MsBuildSettingsPack, Configuration = context.BuildConfiguration }; - MaybeAppendArgument(packSettings); context.DotNetPack(weaverPath.FullPath, packSettings); } diff --git a/build/common.props b/build/common.props index 6fe9ca8e92..928642e9bc 100644 --- a/build/common.props +++ b/build/common.props @@ -60,7 +60,7 @@ - -1 + -2 diff --git a/src/BenchmarkDotNet.Annotations/Attributes/AsyncCallerTypeAttribute.cs b/src/BenchmarkDotNet.Annotations/Attributes/AsyncCallerTypeAttribute.cs new file mode 100644 index 0000000000..d59ec46d2a --- /dev/null +++ b/src/BenchmarkDotNet.Annotations/Attributes/AsyncCallerTypeAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace BenchmarkDotNet.Attributes; + +/// +/// When applied to an async benchmark method, overrides the return type of the async method that calls the benchmark method. +/// +[AttributeUsage(AttributeTargets.Method)] +public sealed class AsyncCallerTypeAttribute(Type asyncCallerType) : Attribute +{ + /// + /// The return type of the async method that calls the benchmark method. + /// + public Type AsyncCallerType { get; private set; } = asyncCallerType; +} diff --git a/src/BenchmarkDotNet.Annotations/Attributes/CompilerServices/AggressivelyOptimizeMethods.cs b/src/BenchmarkDotNet.Annotations/Attributes/CompilerServices/AggressivelyOptimizeMethods.cs new file mode 100644 index 0000000000..17b0ec3c6a --- /dev/null +++ b/src/BenchmarkDotNet.Annotations/Attributes/CompilerServices/AggressivelyOptimizeMethods.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.CompilerServices; + +namespace BenchmarkDotNet.Attributes.CompilerServices; + +// MethodImplOptions.AggressiveOptimization is applied to all methods to force them to go straight to tier1 JIT, +// eliminating tiered JIT as a potential variable in measurements. +// This is necessary because C# does not support any way to apply attributes to compiler-generated state machine methods. +// This is applied both to the core Engine and auto-generated classes. +#pragma warning disable CS1574 +/// +/// Instructs the BenchmarkDotNet assembly weaver to apply to all declared +/// methods in the annotated type and nested types that are not already annotated with . +/// +#pragma warning restore CS1574 +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] +public sealed class AggressivelyOptimizeMethodsAttribute : Attribute +{ +} \ No newline at end of file diff --git a/src/BenchmarkDotNet.Weaver/buildTransitive/netstandard2.0/BenchmarkDotNet.Weaver.targets b/src/BenchmarkDotNet.Weaver/buildTransitive/netstandard2.0/BenchmarkDotNet.Weaver.targets index c10ef0575f..7fc664d8a7 100644 --- a/src/BenchmarkDotNet.Weaver/buildTransitive/netstandard2.0/BenchmarkDotNet.Weaver.targets +++ b/src/BenchmarkDotNet.Weaver/buildTransitive/netstandard2.0/BenchmarkDotNet.Weaver.targets @@ -17,7 +17,7 @@ Inputs="$(BenchmarkDotNetWeaveAssemblyPath)" Outputs="$(BenchmarkDotNetWeaveAssembliesStampFile)"> - + diff --git a/src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.16.0-develop-1.nupkg b/src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.16.0-develop-1.nupkg deleted file mode 100644 index 8e3f508ff08ca7ee828b2c1dcf9b13d5ee7e430e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442258 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 dDH877en_ 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Π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≫6SoThoY}8i${ZbwOdVE;|Q?tVMGb- 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

@dNtCfI3$ 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 diff --git a/src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.16.0-develop-2.nupkg b/src/BenchmarkDotNet.Weaver/packages/BenchmarkDotNet.Weaver.0.16.0-develop-2.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..97ef4cdaf88d095e67ee9713f53cc77340d83a99 GIT binary patch literal 452261 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

wvLs3eO%REcgX$Q=3WdZ2sdXlgdT0!u5Krkw}c zmBpr|DGPANbgCVP4)WVAOv30s-LX?KUtmG3^0=-G6T>_njW1(xkds!vK20I+UYMkM z_}=ZPLRn|VL4es$J9+})6`5@r&L6ss_3SnSi@QYuX~-#kW9)X`8iEdAAIu@l-8MSw zv4eVM4e7*BlZgReun^?|@RC09$m7ryJ)tsf?i3nmTC;^bon?Jp757naC-= zWjufdlX$FS$%h71@EQ=!;Z|}p>lGn%a$S)64JJ)5Ho*t1luqVwTkDcvQ2lV}DtiK$ zse@~RI_YeFu+q1W!H9DA-QphULPgCI(K$t%u4|8tKD0pb$`Ec~+~%gQfoguW@Kgz0+fUu8>zTkq#&EjND`{NMo;^1U>>@%^Mc~{n zE*u-u8uI>5B6=A0I&N`NZWN!NEylM(W!)0DR}#Z#@5IQSaYE5K!STC>77`vo1vVWS z*DaAX3ImpA@Y#UgtK2g5ZJ~b(HxBeou1CKTikHPr`pc@4ZyuQwL@2YxSZA+_FHwv$ zv$nKPH+f8ddE`DYwzxMZrrhsyHqE0A5@JB=D3!0ne&~)EJ*ZknE#-8G2*7a@#!_RJ z$yLrad|rzGSI8-o(ADJ|z~A0|^d(i6h~rALFk#xztO>vL_EU@=g1kzQt8Wlu!`yH{VAciELN)|lh+(zC zcs>f)@vF$St5YoD%~1}$kj9$y?kdJyqfWBjA0?v)S2SyljYU(3Lw37@=PkktZPIWc zbO59~L-#q-eIh;kLwW3HTld&TXpM9-=>uub!Ee5Z9Fx{^sy@{kPgVJZa`k zpIKf}z6SqaENM$vn}W0=uu;L7^I2QdxXMa)aMtR7d%@$1NcO`n$;o?LdkCA6bgx<( zFL^%yPn`~0MFWPqIk~z9ePy+D<(0PPW{T##m7!e4V1^#7wHNtPw6-QI+fr?)yS%oU zzqVS$Dk{ZG$=#{2cqU=qW3&9=i-#dr+~gMxGOsns8^i^x9=|1uy&gJwo0pos{@;Vi z{3u3j$2yqst^U#Y`xe`EjIIz?6k;b33Xx{HWYSVtrb6V2a%lT7lD~_{r8O-5&Br2P zl<(yv!ILSfK%qL`%5Q3%{2H46`+R6$_CyoW>bt{w1&A_l^Gt>>(t#HrCRP1I9r0*9 zoGNT78_LALbcuD@lH#sz+7;3-O^U%w-_JC}|5tHROejPOqoZ2y!~0LS0=!@DYH@%=!smT;`X6txANvQX*fK~>OEYs5IO z>2Vzc5vK?;IZg8XfMi-jt_|YqwGP2&>PFNXbD?=KR!AKyAGgE=R%X@-`30mnEsF2h zG*Y||behmBSBE_l3Jcv6dN!Bk1wmPUD{Oyvfi6q9!uvg!<&Y{%fWmZ&Oy|OY&O>23 z7iv0ih4%-hGgr~Mu!2sB>0A`hxh-6`7il_hh4)9Mv#X+WQ3aj#Oy}Z&&S%4PF4lD5 z3hyPRvzwxGaRnV^pLj)W`@}0W9k{~#Q+U<@Q1!j8m&9t`>w1Z%16O!2$3yqJ0#tpk z>!q=p_qtxH>A)4T`#Smvw`Vc7SQ=zn9gOI4qV~=h3O0{I+s<@Ifv<79?-cx zOy_b<2d?m5VLH1jIso~)rEI_XI_s8?HK<#Frzte3dksF5eJ<=p)@s|QU11MmO;~4O zK>v9x!u|*i5xAHieGs^3YTVsmRAP<|>nLYMqmUoxLx{6Z$Jt%QnWy7;HR2#m=ONCB zjxC<2hJ$01zlcZ@Q;+&)7EL3s!(s9nMilb~!#t`QVe55yN+1?j1 z-4dCcVS8UwGOp!Z zowLh#x^g7DOhm|2!_wScrL2twAyYhTxzO5nOygk~mU7OrbeNO0uq zTK)IO&JV@|zOH|Q6RM8ABLj|dEH(dAQ!!;%Rqn=Bm#w>T5QyBJi}gL<+MS=*$Yq zqS52u1t8=di1rm!xVwwxeN}De4cOS#u?jslELyBWhArf*e2a2owksR9?=j$jhlZFUPCA6qc#Ha5hZdmz=v# z7MsKah6-u&ycDiOM!2@6xH-YnnNs#a<@gh3VpZYMM7~C8?6qOrwr##KwpzR5`wNu! z4t?#wo!Y%iyI+r9S)eWo_U{Jn5is9$a^0nS0!nff54ekk9{>3tT*gpw|68Tr4zF0S z?KRfG>McdoS--5wBeZ_`0(jILs=pT=A^Yy%fk(aJ?t9@8vhV&Yc+~6ms(73u`29vx zT}a+6_>D({zjB$}Il}uOhi=iKAL7t0K`6``%b~D$3_^#5cab1%-!`m1jM6GA{Iwci zKH^2;k1N6-yvyr<@c!<6!_=R=MLk~#?|e?$Hnq&)U5GEUbUs2)6@1O2Fjx-Fvb|nz zp&w#~ZD98zqKi@HzA0}?@Z2huzyoe}vpXfh3bfgOzp%+sbgT}=LK6e6`Tq~J6q)&c zx|Kl#)Qj@@OOc-nFGVi)mm>G(rN~iq-}mxTBtlB}VJY$xXo$ZO{uwWeO&VmH9e%*p z;1tLXKup;TCoUZ%=8kAAQ^!)MafRAqD%^Eq;|-Ao$ej>6`so^T=TB9Q-+-m#H@f}^ zV`8=bI4nkyN8#PCmrFlUxm@}Uy<7@2?)}EJqQjfdg3U9U*nwJ3N zkIULnFw>Qdh%8%mPc|+)Pa*LcAU~}*0|ZkeeLH9{R_pQ8T7JI|af3GscVHFaKi0R4 zzO59c#L+pp>f!VrA*Uc9sxvF=CoGWAnLAo!)fKfmdbuW#Q2#Oj9`(dx(*3bpqWfdh zbnA0fvm4ay?AAy-JAftjwi@m1mMVD#mX6<$yk06KOzr(KEZ&sm9WFdQtV~pfgxoo3 zRVPN#U&6r2$C^{s_LlnCEas)r-y9fIjkxB*>oIRz zXN#V(+w~M4(21#!5ls^Y12synG9HJ;RalQ0{hW504%S7d5aA3^TF8S@PHiL~gc#+7 zJOtG=5yL24d$AJUBSx7Pil`a2;HbOOa3Ugv@alC~$V}kg?%IY-5@>^8;yoZtp^PJw&mKZsBcH#8(;FA$sGDa znT=WccEF~kw*w}3T%!>MDCB9-X|SU~6)^IUqVGMd?}$68JkC5VMmPtEbp8#dh{{y7 zEhfJM%hxs`M=Y|6`}e{MiKm`Mn}~*ev>cWweGgX9uzg-&wAl)8md0!9qmedCejo2y z_x9X3W|+dTnCe(6@~?Gs%vrmB8VWx@Knu3&gKjS+Uxg3{7%kg}a;}cwt2$6fB9OvWQoMmrb z!x8P~OuOk0G^NeYq6N0ct+u!-8>`KdKlkbTmjX3yUNiKRX0~6H*`_kH{UXd#RzfpN zR4}uxHd~cK#S6>O zJG5bEYM^*i8G0w6E#}x=I>BGl?j72_Q@c96w4dH~!;lmcg3vke@nCV8*aN+q_2OWMvEiihn_TCst|N=rR6r-vOMr@g z@dRGoPh42s!fhPrDuA^$%okNd4F(7;y+`0yw`#49p#z%EuY;!ZbSxUNH8)->V)3VF zfZQ86{<|)B6VYbAV;=3ckSspAL$JomAhywWG&vp`p!uZ|ohz0!5s^(qQ?+}D_QRc} zUFAo+uc?-|rTk?J`8FTy;v#q^ zD1zUx_E34k9>PadKXxrthMDg+vhrEfaoUj2VW}8rX>D(gjf+%Sjz2*}*YLfdzr`TQ z=$Bj7k;t_xdiqd)X#B&{bY87K~fG}-BU_3e;0H#O2VEZ#a`08~+f#sXInEq=_KQPf@Ha==!6!rNKYF`EF zqA2RaA=E`0b*3=9+k$h!Se+-VW2Z&w_V5TLpR#NolzLypA-misgnR>sHF~>NWQXHM z@ASC|VT?V(_60wi>7S155u*(15u-oy%alEe7xp`@W4Z8qpVw`__mN-VVXGQ+<~yu1 zAm}7;s3D}B{x~yGPQTGi**#Qb?O9>iV=*$jirEP4 z$5_NI?ht|fBu3`6Vj%*1JVxg9;_eaHPh(`xDDD%1{R|7X#UldPkwR>I0(W{g6_1Mm ze-5BiJR<^p62N5f+z9X~08_WirW)=t7lvAg=Cx^wL)|%{ z6rtpEOG;LDj_G*W6-%b;#-G-TU?(ElP3?*EovMecbMkz4;tXX2fIUpi1?isc{N1YE zj}X!IA^4YxXx<%s2m_}Voq3;Q|JT?LL?Q2BKMqO=2~F(GgT0!Ncfs$TD^VwEC}zi9D&+m_*;~kOuxFl-53pxvAs=MVfRNDJu-BCjv8O}G zZ?Y#TB#uz<4GjJsu0ixMzDE*6MPBdU!!?M^J$Asa!~uk)-MwOosWt3jy0EaAWcRi# z*j*6K`==l>GZOE)q!PW07yOmg?>-BD#_qBO=tvI=0}=>YyG24Eooo&n$=7-$E; zApn2Iz;6Y>&jGlFfjb1iJ^*iL;O$f{mjvZg7e(l_-895!|zoD83ndxQXHygO6OI_^aS!=Xq$0oCI4D zSUPJKrO0510R26V`>8gZ@z;@N~zX#c)II zr{$$k9<6ivlrj@5ZyGB6;(uoOt5rQ>ltDdW6dZHi99QXZw8u~KTmn5~($?LeqpPTm zh?fsBokCH}B;u!zA-02BlMfT|7A2~6>304WYIm`Eaw~va5Q76Uj=MDDxcg-tcX8Hn z7iJxIe%5h^vyMBIb=<+MUfWpd$ zr$Vsu!PRhj2#(T+i^>S#D2MYRb1FT7($l8y6-yFC)TIB~^xri7x1;_`^meYu4iC5m zHEBw^u9`0;-I;2tgtOs1OG>(}YJ$XbBTUeexDX2IxS(b_YR}YQ{ryQ(xFinpo6 zY&GNfm@2bX>1I=vWz3;RPKRiR_`6_-_gFLE>=^*)@T`=)c0Jz@sMFb0!%%z9ZGIpwdb|uaxSS z$OHr~&lpHa^)&QkIGmSyR`btE$P|iO(9X4@T=NSdrg=`iORj7zcm5m z=#`_tGXdo2m7~8m0ptkZuH6=#EkJ!5#QQAuXYjPrvDDAdja?_ze4dmeG*<11Bcd1S zi0BWXEu9Q)>`YJ>@002UNB40{s^0u+{~T9@4iD(ma1W63&&~wj zxH({G{uRCGC4M*kI^2ImMdnNO2HoPm6!TFW)e#f;N2!jhS^Wa^|ClRLD6l~Lkc?VJ zx_MzY&%`On@tk3;gR|0iWjLFfrtRizAU@`A=!J<&^KUqtX}-a9bN#;qiEObWmACi~ zkpbn#9_er$Tta`Nm##UfmOJF_#P<8}oguH!{9cgm(~XgWb9Q7S1?Ztt{RM>HQ;6yv zecg>qDQc>Y`YWOqQB+UkW<;sK^MMrl;kXdu_TUVm6F=CN9cw?(Rh~}$?e}T_{3Hf5hsZ|{ns8TBK zJi~6B+-H(EHuRZ`7>3sOi!7h?aiSGQ2eF&%^dcb{c4r;8mX@wM0{R6KU8&~3w0sDT zeWaxXKDLthB$bWNBwINc3zNzTjqgRf;Y68eXIG<;9 z;OOBQ9uf6PwOJqW(%j^4;=qKf12>~VFk<_pvRvWRI{2&Rp-#y#kyObEX(~rU#ECdU z)yZ|uu=JUN=9Icso>?iEf%IzzX>wFJ9z$(FTE$HXPSz_&|7_sMfw~>9?K|i!-NsXW zVx*dEywoQ~ymp7o8L6Pb#qWkRu^CA&M$ub+7&6_?9eq<%7$?vJpvTFe&WU6rjOVSW z=bur}+fmQIqMpt0VBHtetosSLPG+pM$zZ7M>%u}%tUNh^ZP1rWoS?A@9raa`*ADNR zP#lhto6|wJ3#1w)@fTW`d-{WQxmQ#5FNUh**eKNuS2*j>>KTH0F*L;W^_lD{U0*M< ztNVHw(1L5~^=3}QTykVWh&yOa<5(r7@GN5Gu-qjze_hi&@*(<&3CBy9^HC-pA$?8= zf&K4~g%Gbq{-4M87Y8AEL5o#!leG>SR5HUj!_5n2N#U)7<&n(rk2u6e$SxG}^)Mlw zU}uIu$stL=3=_;vk$iF#f@vr(lq2;llBr&h7s`b&F;!nGmRbl%~7x$Fp)%B=m7C5 zhnC1NBfJ=gQHh0q8+9`$S zjZc=4mvdB3LR}bvi8x$-OK_`Y-BGjQqW&(SkWo)hgWQDd>6N2@HM0>pu9TKPgtbG> z!I02A3j25f^z!sp*?ziYwQBw4e(hQwaI_>Z!m)C?rN}iPt?4J)>UAzx{S9ASQocdV zsJep|Y5z|6^yNO^0qM&Xyw$M$cN;+gXn&*Rdr+k~-v6D^mx;6WyKr8zH`q^Uu7q|l zS3>)uG!xq4xKA{@r11Rx=?1WuSq4bUG3!8S6w-h*ZVi(*C00mWnN;otRbMw$B}cCu z{hJ9O2hH8ElYRG=?vj?ek-MZ}CkzPglBC)X&sXwNO_BP($pjRoxo<)_*uUSS?V_W; z{Z8{h%@=6S=wQaY1VrC3h?3*tiA;=C%i%QFS$qUVYh9?kP94rUPD74=H#w4HX=As3 z@W{|-Mpv4U4qVE9)>}tXlwPTFOEj=mj09&2J{_^lXE3eMU!Qq?RtWKl7CAa=%>{fM zc9=`r)I6uQzb*nwLHTz`XBTSUi#Ug-Mo{8k+qcvF0;`yAOHPBceOY5m;89?4o=OJ>LiyX5sMJ{BwzCc-Ut$i z#XjaMP8CAjOM3w*xXY$W15T=YAXCjVY^K`DIb_VKm|SdbQT2pR1#t&0oX1=DpU3Ha?_Lxg^(#+^~YkBh&hAL# z;0b-8GeI8~mML&|=uFVZNXvw>mY<{h(+jn%<%L?-@?1A6Yl$<1&(pewe0l%r#D!?6 z%efTcY6%)1@6bE?1-fe`!Hz&K@X&rO7uZ!Xq8vtmv7|x1XF!~0gWA?@Hql7Mj%qQ* znooH_3l*P2V(&L8+CuMzGZWI^3t7w$^3mH=DZH__cyCqqERK&jkC#^2JJCS*1kk;5 zl)j-X)zn~p=!>m}fv#bihl^gd$YDINc)HLB2Lb5dGnIhO$hYWKd*Z1e4^FTBTT}j` zM01Zci|)m!#bu4fv-Ei#!>YY;D7W7%BY>#qV(6CsVkk%IIsO|oebT{UZc~CZbY><9 zj~+Nnw~JeGNB@}+cFLb7g@B-CaaEgX;o=nvJ|LLuXVVR zHunBnRL7wpDiVXxPJnVpqHK+D~Bc0M5r{>Wpkkp$=3TH;s>sk zC?>`1@ME|>sb?{5J`Y)8yIOSB14(cKW$XWoa>1)+%Ds^rz+O4J z$#Avg=#`_JO#nIcng)IDU!9O=PjybiQJ5FN3-MVgSYfZ)+nC%oJSdr-(u|@85Za8~ zM0#BjW#QdU_b5f^Atws#jLZrC(zl9z?4QZs@n}*WYg~-{AmP~Rslc~5#rZ}(K<4|!Ko3(8lI25O~1#FC&h&Ory z`j>?II&c~NV5o0fla*dk;HaLT1hDFDmxS(Fu{MtCwL)$ZqyM>)7l5vfhg$RVv_|Y9 zX;t(a<#z~6PnbQcGR``-W9qdvuB=}heE>YdD{;l^XV(p-|w(R)ifsb=A<#u`f^r(QWq zU#1WG?KiOQJ;k^XFY7}bcEW?4ZzE@Z&r-}*iw@a{h_bLWnG*|3tOapOzhVw{2J@sA zbFdD|2a~%`JCL3Lq*sp8H^Rq<{N=_i^Bx;p`Af_2m(Ln~{afRKH?~goi1d71^oPhP5=H9Mr9W;V{7)0Y zA8#sT^lI$fDLj}#0hLMR3P|ipy}pp6SB_>(06BW)Xx0RfqgRf0m;iF1UY`&`{P&j5 zoPMv5m~fs{PoSzMgbtJ#=p}u|go7ytG9i(Zsh1}Jy*v@<73?kZmfFc2UzvbPaC}Am z36}QacP0Y z*=h;j%KV}Xv#9hJrNyq3t>PS(-j{4^q|c(1?*wE)d1f@zN_nP9a=KNV0~02)Jf|hi zI|HK4TNchjk81plp0nx0?a}dsBz8&r7(TRhImUW~e_Jca3zRm0AHO3ZT z;Qxnp*x}udOE<68WaDIy?m6j55{Jzh&ZIa;L-dDx;_{vx*Q{dOu{5?d&g$t0%mqz9 z9a7!s2xr<*F9-51`5d>g$fNt!&+Q~e3U-xCDcHUGBGtL0ujE4h-~x~259lHl)7u?= z)6#eJP0!hP^v#IgtYVf;m|51-Mkn}CK3U6}&G2NJ&CQ$i@6h{fgz=m7nlx|Hd2N~+ zgXybx_RD5n3a^hdYIEA(D@XIDDajGM)A=sne^BRMGr}iv!87MS@tHx1#X?%}%e@2q za;^9wAb3ZzTFYf{`Qd8C>lj*P3+q^Tg!_wP2C!gJ-4<9af)QGi()(?T=H%~zC)MZk z^_<0{_(3y@F$!q?R$x?};{1!H!%H*>EQ<>4axqgqPJ?Are7LgLP@gPOq+42*kq);I z5oi^s2LY@S{xk?+bzdT+x%NgVILnu+q~x7NzPk zu|dsO;)yqgr5<}k?#=|Q*E+YIigYa-TDsBEnusC6pVkHKsk1EarN=lGyHhcXY-q<2 z%|>r$!?t+3m3N7h5yPD4gIZRq+c7WL)B|)oVz2ioc!n7)&xIMhJTL%be~~qxd9rCF zLZA*u&I2;!$!4d`3Fgx@O!f)*DvR`vMR=j|fUYX?f@ze151Phd4zw{CDWFvd;O-j0 z5B|KeOg48$KR`Kp)#@2v9P*v88}xrFKxc?-E@3y&!O-;10K23zeRxFR@o;%W=PZPk=CuCB$N zWl}rrDunpqgtMyke4ZaFa3JAX#}C|D+fC7-5MeYoz-S&^Sm&UN;71YOs`rF|hgeq& zj?SG4j0leXx@iC3rx}Zouh=bM8d_W-m#|yN0JJ&>rPX_)S@VM>{;n-t3%9P@CmI_d zZPxAh%E~cierVsa8i$YQqia1avJROlA#!?V9bH5!Tmt)QKL$O`T%MV^d9x7BeaMRD z%*~xboPf{f%_n5s=3+FFVIrqTB-%RCwT_fblPH^iD5kH-reHthb!5ydZM6)cjdr0j zsgOlwQh5fZlqF{6B}cCuU1|czvA0zFr&`s5Q^-WM;8bRiVCIAb6V!so6D*pL;2V(y zi-H8ba&%vVBRQDXhqdx}(Y?&Rl;36{1_mAD1M#_}6ZOio0;@#MVFwA>k#t@72KXm& z*+Y*_?9>a90pYN~VWx-XqUPgZ`HsLiwDMM*E0^`)Mq|Ql$FeXE8UVUoenOn;*AEn7PYRE;7UO5UV*12Pokk@0F(|O7Cq&m{n9=cLl zybz_6sA#>@DG{}tBJatXyGSeq&4Xw?RTnsP?RO!6E`qG~`3~>nc(4XJX_}LN80Xw} zwIgTyyW;wWq*QY@AFaK#F`Kh%ZqC+cc`1K^6y7r7;;5+}?ZAVCbomaZXM7AanpEDa($jdPj(7E}#0%!h=;WAu zr7gWI*e#e`cZX}Bc@mZ%%JX@tUZFFS^G*~(j*X+mlIP(PbnHER6umFr;#>mvB>TJ> zLL|m6HT~QsA-%^jr>#Yx+5$Xb zoz4pqGWYj1tOoDrXUKbHggWXh(L{s;$j$+S%w_!n>hUsFZoAbv`B*Ap<< zF2G>>0D~Q>HRH8|LiFcUme<$LBWv4&li$ z4(Dw)YDr6av9g+B^zFDdc=_>^aBwY7^gP4JB5yhC{rMQ)X7QJW+IpnTVmBzLHcK0w zMwTqyG(Q)UD?F5{#CQ_B3~u^`I(o3WrNLC`jdgM&Pq&E$Tg zJ#i@Ron?m0Y@5y5ZXvS^!^{GE*V~};tTvP_Or&h3Sh>HYu&olS(ldUAp5))lC(yhY z6vtv-i@o#JUf2`Z{@xgegY5J7f#=ay+@Fw$gs37j?%56;s&L9cS<-@$;^@VbdzNy?J1ElB|UHbTnO>M zZTd-u`^hw$Tp721kWG(V@$j~O;xM+W{OUEEs&xlIb0a`2s%tG$b_@MpwGo@$>0G*g zWc6gcBDxlnvsYc^cF2?u>y2p>wA3lsGH2(K6l_B0qzXP7Ql7cQWcP1XCW1Jd!=;WcD+Hs{R5)unLE6q)irLaykV zlIFhoAfJo3&{C=^(Z+U(o}FI>cb7#Ez-{_fi?ME32c)`sV%!pn%g4f&BFw2BX3@)V zo8O01r(b0G2U5_1CaB)SL8SAxHq(xH(CH2)X#DzX!!e9|<)70%9$Ay^;IA1AUa-=H zdEo&Q`&n4bTl#Q6y~UO4lDlFZP_u@vXzgrj{nHG-!-U(0_k1 zvHz-Xr-U;(Ve=Q(knGy{@V(S)$;h))n{AUTTF2n)TQI9k7-sVG8WM zQLw9>sN)CY%n|x8?u36y{dYL%zkIp_7b=lW)r-|$>G&I{Z%&u4e*?MlGDQnoM-@xO z_4L-a5}QX!wF=Hhiq&L91dtW4M8kt=4ig_uO2G_RO%#oMV3T#PI$zPEPql@%7BQ;rEZM{mjXgJXQWvNdq~pVUflI$+ilI^@jo38NNA*!X zMg16(QefrzAZ)?M-}}perZL<`AW_>6e!OC9Ve9@;>1BTeC(!$zgb+_p?Ekc9vgvmq@Ow^q(Y%wLa_6cg1vbk9^_aA+ z!2c=yT$ZlDyAl)s|Ea@2t6_K7|4_5ylozp<7MDDGE&MOK7hJLswl|#eiiObkF$_H< z(pqzi5Ynb!|8v|5bOT}2um1%d1o<$Ve*G`$po=L8zyAF?2q$wk{rdOnpwlP_zy3Ws z=mZMFum5!&bTb9v*S}8(-9SP3_3zR_*HI9D{ZH$lQz;0){%3R$#NBQB^&dh|^IvRW zlg2*J0RA23>`IDVOq;c96)aW|# zhe-S>?IGgctvy86|I!{J=x4QuNcnJ-l;;x>?`%6v!oI#KQR!!Y@5%2Njtq5tRgXmV zH8bx2Uj>aDW%>#x=WTM^%*pw z;dTZoZiw%2(ye>hE#ZJ8dz*d<2M8#!=Hn0t$R4rQW3$Jah{I(Qai9de%eud{RLIrY z^gGnauOw{xr3V&!o7@@h#LB}adCsm)YMln#5DCrn031h~oZ=(_J^n0&cqTH(PP1#> z5y3G#bfS==H~cYLSP|;Pl)Pr_Hi22AOrq`-T?tC@!i1IGvZdOOq@P1O5gRElOAA@%jF6>f&rUM3mA`8*TbUL! z`uTiypm{NlJLx0>YYE;}{+iyL+@kfPIzHx>$%0?$WZc?z`hg7qI?^>StpTbjj+iVY zO-gKUq^~};pZs}AT$;H$bY63T3Wv@CjIpvY=PAjdp*9UT*ot=Dyc z7w=6toRe_m0xfZEmGhNy4)vfJed5alerB*#$HyKXP>6YWKpyk(fQp#Uf!cURFZ+V8 z=5hN18<3QNW~93uAEvu%S;v2p%!SE*S&=49mzL9%q=4RYEyi0kUVcqH5cQDkawNNwr4Gak=MTB@@a|Z+rB3Ja z)R^`|2QQ0_nVd-ySXw2<$b^z0vG~|$ zxOKLk9GfdpCkQqB)r9b>Q}iXFt*;0D2O&}$69MA24STZZX;j&$StoJaNY6|1JkN!N5{9;ro_z{xwy@y_~JI@r0BY~61lCdI68-5;-(q0 z@mbU;P<^LC3DTvGi#N~FY*2^25<LJ-ry?o=9BCurHLmQ~zYKlG1NTb`s_Nn5PQx!n5&+ zjMqoC&Ye`syknjh>b;$B4|l_ z!x+MH5r3alkbU9N2bp5qn+`JiqEh4!v^p14bNy97a-#3@91TZ=;^y zM?GkBpb3wbly37(KB|sq0RwAczsdmV<7;*!3eV-QXPL=q8aa6J{h&dT9D3dDw}kjD zt-Ilzlx!U!v+Rg2Di=(u>#!QCtRxMBDgRJ#2T8&gcMWy4Q$0MOqJ|V=Ay&^9YFEmK~9s^ zu0ks3*77-**=?B60l$qKulf#JhVrmEVk8*~Sn4GZsx%8J6q2GsjKpQ{&SE6lGT_x= zc&?Ba@H^n6ImbF*z=es={ukzSxFa*}i`AG#vdOq3uUB8TNeZKUNx!=x(ndkCBE z1Aa%cFUXhI)MPE?Oz{t=mLVc$9Gg^GQdlut%}du#Q4>%JB;djjgD+MNwd;^j*^Y{~ zY_&a2t|$v~*uQTnYrkb@DTRrH3P#Rw6eah@%O_`|kv z&O7$`xQ>Y|uZ%rj8b_w0vep!~tJb26%jXmI^aLfgIGMdc?+~C9du4+E57z%H-@*UW zc({Y9$SEJOa4DvuW!Ns*cVqT}N-fQx*mu*s^QQLD{BZ=T?bvtIfHzJAoe&9JD(?!- z^G+_w&1{oRn2vO9a+=J^WYT0pCRxL6IwEtoVsd>rt(de;kfbz#bfzV(7#-8tn$&w9bG&Wcv*+M$3$y=lnMYnc#LG<-+8mXc{r4QPe9 z0c=F`cu3FR7|39Pkw65#H|p<)4P!wL<}3D32=PbS>-3p5MXFg?D~7;g6f;uwW3Zlp z8Exlt*jMyV)G7n&Y2ow?9htY(Y(SkPg?AlBIEL)X|%AoCt_D*G+t?}_`TQTWgQi4fBQ&!6Byj<@r<+sAZ@ z+x3_Hv`>bP^Ova|2Z7LMh44y4%Az44)y}klBc|wo27?8rLOd87V%1eRkp&FxGNXXu zwlibbcXurxc8pygMrO0Add>2GhfC){7-mk_3U0c2JmeQ*K(5`6e^KMmZ4qn7CP887+uc{;!2=FB-o`56Cnj1-95a6pX$ zT5l8vpVQIDQFK?OhcEIP?egr`F!xA{wkJik6fhh)K#|g-BVvvTa?|${SDCj&e7B?) zhp7W}4~hloD<{4OP#|jzUH!tI&i6~JW_Pr!YWT|j?)FvPT8%4l(D61fbO=su(}m+| zI|P^V-0XVO%3vBX~5s=wkm{#lS>UgfWbOm}b! zopE`;IYqjqHbiepc9BWO&A^e}tFqOdbh4^WmTC*1Dy_<M4f3SOP1i<_51TrJ z5AhfV8~7UXN490hy*|qc^0Hyi6C>(u5c0Gj?fSC@pRPP9n|ZuQxSOU~nm4?8!Mt%7 z@4A|$KU0W-!>h;1scHoT_p7UAk9M6Xr)k%bQY|OPL9$l|?IX)+?Hrb6Pdn?foYBth zWjU*zC08|2_motJ;022#)xn5JH=CfrCSA+(WxU4afuZhb)QmN1<(#b8(QFf!9QqRo&f6{JBH<`1CTN=)j7!2ojNeZEvIo$;0<(Dr%6Yx zL_hzjbkr(19+i$d8jk;lmHKj8oskw!zvUCJ-K>DCxfmm1wVWD_Dn+7z4|Ko11$@dL zgGG>`ngKzlG>~-49&8S(<9NrWjxp!DN$BK&AX`qVV_T{Nb{{#EGedjlMcgQFeNJAm zby%odqmW*m^uQ3`?g+lkQSfb~d(DH<--WXKx_g9>eZ_KSLtm+PgE}j%jm@G^@Y=lt zUd}CNh9;M?)vU`1QuM zcC_*i8@SFn6=OskHm~uG)QIvp7aa3naj6p`pWV<`nz2ExHUz_%*iCj*?<%-?mqWJC zyR;I-vnizVB9}_7B16sjM2ZbIyJV=jBByc@k3DG%TcxmigF^igd{JL0RsR=woiQ4j zQXLP9jDyL`RCj4hWYbSO(!B%eG!Zyc%|?^DweK*2on|dUV2sTnvp$wRs-s!RYAH#> zvZ_a$2~J}Lpw7vAe;ny-Li&r&b<>auvBEfOL})zX!v`tT0u8vGm!j%yfp`%HCo$r(9~Y34Sr zNs5ta{$)TA&qYrLUb_k53`1F>+cA=)U6T_!Nt*&Y!O=bEA3E@C+0Xy`EKRngF#H?6`UJ=3*g< zH-VVt{a%|+t1hR*=J9)_uyc-1}K#mzSSDEIJy*W5y%jq#os-*zH4e|ot0$AmH9VP*us=~kk%y~BJPwZm~?*{h>v zFK*18uiv{{;|Ccbmjg-2=?Qs z9lCst8IcF{VawO_m;8;!k@N9*7ewailgUU=cpr$JS*)HPur^;uj&c+q|2AwN9yD@l zol=Nog5E+yQX=Cn!AT^}wF>$?YtMubhwJZ2`iov(O$adx_tiQK z0KY&$zyEhc+&8i|W^%u%Pd>NPapoUZZ;X52h#TTHR)V-6@}4EE!89s@|Tu6pFEJ2mzG*X9>~Q@OI<*ow@Bz(T}U2C?cf5MJh+sUmU=gNe#)MU z$%EphrAEmEyPDEcYsvFz_KcC|cJ_>uhfWnO^&awIhafF=32FElm6p1cT<1wkT}G}m zP^rtwd91Y5736}l<@b_nKWV8e$pv*=SCMP3wA9t)0-NnH zO$YSD1bs*xdOe`8CTPT{6$bjd1ic~-{eD2dkDw8w4h8fDor$(6(sPvfn?wy+o2eb= zM!|#0ef7>Q86n0LvWnIULh&1f;-PUAKL8Z3Aruj#4l@<{CP5z-hrS-r*Ag^h)ZqsD zzX|&AIP?bry^f#}qmD4pj}Y_`ap(^L`Z|I}j5^Xle~X}xj6>f5==B7R70DV0{BSsxC4>J$V0I6WH6gt7GYK)x>y$iqKmioZ9gcrHO%`Oq;G;rJOINHz z>II9#uR}`u`w^WAa#m*v zQSIY2vOkqgReHw%m+#P^#{{dYn-ICAx`Q@d>P{)VA=y>w$~gXAV4C}Ee+W;NXE^@d zR775?dvJx8u6dT7?ozvTkmm3m9sd)6pYHhgO5v^fsL+?a8OzSu8f;^KA!#)(o-amh z{~uB~p@ynlviV6=n5EFB>O&BjQdhC!nlE6#B)ckLoNfSX z_PB*wCHuQF8-*L-*eyC|iR6x;z#L0ln-C(6yrzsj4J@XN{fa-o=FfBdc_E^NhVT@- z1bF@;#6BB_{*siSG(SgLu(JJ){xIq{l-aFVgYHAa7Bj;e=^m1TK8203|7mPp+?>1S zd?6CqRDGA6yJmwBiH>xAan4~-@mWs&WSJg&hNDpR>Y-A7Oh@f2bN1Lqjsrca5T?^{RhdhUJ|6jP*%;ND2qnByywu`a2jHU)j@dXhf!+$eUeg-K& zq7AsBGWeo4WSm)lQ3WzmtiM2;48H2`bNc(d{=T5UXp_p(Qr&}+zDgH`DDHFM3XX3L zwn!)-Fp{g*qqlvTIQGxu9Puk~Hixl6`x>~aL#5@*T$fE<|5*BZQS)guDKOp&e7pIG_OJ*$7C1bJ`*$c9#shXC?$WKRo& zh#uy><~R62ZxmGVzezV|lm~Tr^n{GNE95qHFve>(f=NdjN*`0@et$IylHeHTAkrc6 z2%Wy<9rZ12%!kr>M}6C1N;ia<-}oN1=;mDiCLHNNXGO}%rTqVaDkgnSle7Kr;CSX< zJST~g?sSkY9`r6Rka2!8Aw-7O{s40?f_cWRr>#v>rU-$zXyvT`E+MK}r|kJ>Lt(8` z^3S0fIpw|m@6w^FQ{J6jnp{$0o%B!fgyVmYChXxo5<=u%^?mqKsaz_b_8*1$i++fN zCG%KAf$KFcXO z{2wF5Bj6YE9qK2B%7sbrJ{EX8bCbsZi0<$>4$VGv21zJT)BKjh`mT3nh@_2Lae|k zMS{ir@HgHVHMh~1pvsNU2Huy=T(T;hOO9zfm%PSv$$gkh{>+ocPa~7Y&uF6fU;2HL zet#aDG$IoPq^z9CMDbe7L~)G{ijHlRsD2S0+wDPqIRRutE67v9@Lv`yo5oLI%R$rP z(zdkt3)A8wpv7OA7Egl~S1>J}f%{5hTiT}Q^jR8vNXT!MUAo5x^?PFr^*fd8 z_r`?!ZO4I)!-M)obk&YQHo}$I4k)D8jPojYvMxtveL1tf)gB;Ry#6^&B@ZiHDDrf~>X;$EDwJ$Ge_3g;pkq{39rM zk{)emYO1Y^Q|;}ERC~LXB*;#^93e@tJ`{*1Z@0`a1plYV3^SWy8aFK1LMIRFzXtWs z!_*!Lb~(by!(7TP#;!wjti8Rpv35|~u|{Y-za=zYThMN-9Ur9mXE)+scX{o2&BDRp%`WHmU4)BbocZwS<$m{8!P&UE%#fF^+>S@LFCQ!vZJ! z7Yb2DmwydSP6BZd-AG}H{xiJ?=0}~;E*JFa&$sQPxy$uho6} z+wnfVX=0z=)Y_+!UAWl_KsL03{3Xg4Zfe2&6=}noO&iRGMVMa?%oA|SwfLyN0{v$+ z`d4f8UyGxUv{Ce*lA~AYEi%6T<~ESrC{0ZAuWQ>E*Xa0YtqF&}ZE4*kaB9rOdTt~5 zHzIReP-1j$Ltta^!fnoN7xCQI$9){@7nBW@!m_!KM=FD7$*52B?^wB9TEIH0v+@>i zAMJdyT+7)s%u^6&pd}1@9{){DKNc@xk8PZUaUL#Vsc`M8i@7HSYgdHw)V^RU-)!5y zlH2!#XkW_`Hk$JTx;ZTx;z1+HmgbD})?sX%_iXU^lI`e?z{a|5)BD{_?`a{Q61FvE z9OIEt4dfTJ^_I(iY2m-cBQ1*&As9KsvK?h@rtHn2Y?4O~KI$!uoP~Js9a(?4F7_5^ zcq!A+ZdVTkHjdqGoAtPu>rqENuF-i#M-<`q&(^i^*kErL6khJpz;CzN+z~bp?zYX% z-6(JF4gfajoCmZep~ED+1tfHJ=lq!NoJYnxr_<6oHU1|Cv<(3IT8MiAh#RFSARAq~ zZ=FqpH)HZfRBjA26A z7O%yrHgomQ+P0U+?R5>>E2D?QbGluA8*dk{&0HPMnO;C-KID{ZkzIKD=tVM!5*dMw zE4E$5F(&eWo@>0;9zmrgt(LjwT>&3X2_k$r!8=xr4<}rHxW_j6@NvV3BSFB2m(&Na z=U*116c3%&vGaD@w#af@T#dHq;IZRc!UL8#BTKA^SBM%f8%BX?Uh7qz1HOLumU8g@D6e&o9DPM6dnJC0 z5y95nVq{y}6@*ZpwZ;8awxwMOJ`- z0Nl>gVAr3Y>un0(WSP;rTn!JT!(XtnFW9J@tY&|WcO`F2eu%65dm9>P| z?69B;NvKH{_{X_)0vOXVXUvk^|A2zlgcMJB2t=)^Zh>_qe?-swnUA3=1qkk*C zv_f6k&<_u)r?)K^2|USZnt71T92@Owj*X&NRnm-=ZAfPmyNN3)kS^&Ltneu5;nt%h zkWSOCDwdwdv;$ps59Zu0weQg_^l@T@!6_E@=dHC{Z}yuXx7y84lX!MokJ+h{XQwXA zNOxJf-ya|J`{{BmAk-AZb0?qFu9G9Mamk+Bock}~PQIiauRbYQclOZh4i#ODEbdz_ z`+M6i`%*4@mf|KLU}40vgux&jl_oQq%iA=+zx~z;5}| z;LfpoI@|?z&wzUhyL;g-vAYa+C)`}Lnu&ndIb-4ow?n|&2#{*FC!FR8(CDHGQZ2!% z@iIp)g?mTJN*!hkN4-R*C?HG$ZTB*zes2k9__sog&;pK+FYwv@$M7 zr|1c%-;Pc=Qt8br)(+?s>Bac6jLAvqR2sYOjrSJ8y%q~nEs&$=b(Za)myV?;`+-P5 z&!oSsUHagibeZnl2yIAsc}_iPcdaN^Y$HAmAw(|}E?=gBF$r2U2UdjP8mdYi4ykPKmvxJu}(E}jlGx$h~-@M@5WxKE~sN5a=D+v|H-yI!o z5AZa_Y_*=Z$~&Y=D=B-I6QrG#zb7$t6tu|F*3mfi;G-__0Z^E_eM>n#R6mr1 zDmM4h<;=@lHwT{47LOw2=t_zmyors)?})}#WcRXw-|v0VYSi~ zpV|&W(MLx^mZN_jj9}0NJn*a*n#}ayhraCihhV?DbD87HGntcciv9}P=pDUwu2hShq04)n)l%Yncq(HXvizgbjxAxB z)hhGlDAse!KPKioR{M&vxMD00tFq-E7xNvjeOPua|Ad(DMD4@6YWXL{d?&+qyd0fo z3A>h(qtii4JQ+57{7=F7N}P6m)A`DD0tDY&Ven|FAPJOkuT=aaOl$~pd-_-NfkocrLXQdK{LYlQe zJKY6aPt|^q`$!@;U@ZSE-2~CF)imHS-b>mBkBLW4Sn}TmEMP}>bcXI;_)2vwb+7kR zK}ZinGu2l5Wc&{heRehUIU6)NM=x?U*Gc>5T3YDS4~Sr6@k^#iCUk^|z-t>45B?y9 zIvhhd0u#$WFQ~%~=KR1?$I*tkoN04R(20#uOCAm6(U7CPx{mltbrRL_x>y}+t#xEE zP_Mke)Ug@#0aM5GO&!;?t>YPyIyQA3x0h1UHfgz3ClaE)u||xBQ6pX!4}&2k0uB!4 z^c;YFXPxTM7TmM+VaEm0!;U~kMwYYbRyj`_(lQrXTal3o&xAtRn1&oJYhvRo)tM1u z>vIp#d!Uuxll<$6-n$ukUo?T6+lc$@UTDC|>b5IxEXhj`t8k3kizP%UqjD^AM4geTdg zy3`y-r7&r_>731|Y7PjA2Y=uKtr0?NyBMm`FoWv$8dZFyS`($P(f2agf@|8pfpNVo zifc7*bvr5la)as>TSpaX2Y*PYo=<3PA44@7W>DQhql&Lo?~Zi_gX>TWu2cOR8Q1r= z!Szam>s4FF^}Qjk7ZF}N#PHftjh}YZ}ygL04GhSDl)>dhGT}aEz2&?`W zEoW)0@RjPywzS-(m6jd;M~IdS4J|(~ftJ@BT7GbwwERFw%l8t7vs!osyk{D2p^ZjC z2jN&WCsH$*J&%lyV_d2s}}?|7Voo#tCyqqX?Kma zEQT{t>-Y&61fsO9;Ur3nM*nJCf(|f2*MXq3nV??=5x=g9c$6mM<8dMmOeEq!D-n^M zx<4Rd{QhX5<^G6(zY@Pc0!-tUeYSajbc?w^8i?H=%}ceBbq)}3C+mDzTUlp9V5WBUM>qK4>1ag^&XK@w7&kov(XLe5z&kSD}Y*9RuUl^p!na^A?_sl>> zb6{eZ8fe$0&~FuM1!ri#GZBtVbO-Gao>0iJ+r-Uxtyh+b&k$E2cfgj91U%5z_-A@PmK z7|q>=jL~~_GJGR4Msr(ajQIU>C=+$5PKK}lP@81&8Xnjp6D3;nyG-YYumAOJ=C^!X z`CYE_!`Htfv90_L-Bx~A==|{Y@7!j7hfkCNyGrMPuYcDT+vmb)`#dE_uhz-%_3z$d zvWuh12=_fqUmd+hXMwN3=QeR)hO8bW#7SmwZtG%(j;$UF7PBpaJRDi?B-xlEd^qwO zft=~w2_m`yfDzpYP<42|Ul>_eU=wr?YNM741=A`jggYP9i7p2-j{i-P+M1KM)PHNK ztsr%G&W@ZWVF@cFaqoi*!n~y(0pj1~_2r<*X%eq1Lt^)lQ054_qxak@ZW#K}3U;?S zYvolG`SlqZZhfSwj|RGU-ccHiCbi+;OM?-kZvXevU{t7&MQp{wP~;-er!&Dqb>G6i zsWU-Bb(!jp`2-2oWutRvirVFFsPFN=O_M(h(fxGeB#5|&LUh6XPEqWtYwpH4uN(xr*2i#70dA_p^+MA zFNKjcGMhwVMr28&*TR!QvaYB#hCS{H=MtSU>O4;fv6%GQE*dO19$YF$*k-J3{tSJ3 zd?CqX`9H?#8GG`={|U}~enqwtoebHS-VHhS%2E14ZeX8;5KHKc=6}H?xl6X!s!=Cv zI|lu@u)?7$7ItkjY`Hd72w~aN1*=NE018KhAi?eRY*_X`{{OHLVgn8{VT6qI2z$e~ z!TFt~a6%fe@jN+ET0`tBM!f2Hqno}LH0IFv+{QBcp58c>zQ;E%q3@B6Tj;yIaX)>R zHXftz9*vjjJHL_I58r{tj`Zzs>`mX^#>w<8HZG&@q{asNI&8QICMX}Xgm(#uUpZP@ zP`&2a^t;ls)zj!JM@wsLDH_Kf+m{Bx*nYY}mg?`m`b*(@J_DJTQX9c?py$l|@uROk zJWy=g;=0v~z^GqZ+I6H|(dazH)}mhjF|n;@H)%j72A1v=!C zxr_!-uX@7SkaKdGoV_7u=O-zd5e@`KV5R`r=Q?cvEqwLKbZY%sd;V?{)av~f=uFN} zw$yW;a4ZHVV}TYEmfe~fVO zBT_7+t36g@@p3VeOZVY8eClxus>#{Tp+$9^vHFDi)3Dj5?iQk&OBM<+^*JS1$ayyw zx~yCmjjO=kXJP$WgD>Qq7}OLiH>C}f#Rc@NUYWDcU3m^cqzgqPoSG}dLE|CSWu4JlYa zO0R)c$f6}gZf0@{&YN-Z)8(c}S$l^w?PO>#?zVRd|C z2hdEZe(DKl>_80X%eD-~0AF|~*~p~&J4XD5ZRBR)dgUlW@ufn}m1~z4Ym4;Lp z35^4nWBv)o6b+}!BUaMih=ry!;Mm;**=m=xCfVwrK%87}!__!^xiQ+M-<~~W3D`}@ zN%aPfyAqX1&K|@OzE7Ici-Rf0^&chWqPxD^g2*45ESoB4R*_{u*u=nDa#C9b{0f%s zLjYLL&=}}Qmxty}NtbtC_+ueNx;(TH)hk8&}?E;lGgbNko>tmXe_w9VJ6w`tTPempq>W&tC|A>%$vj znJy;Cbouaw3z6bdL=x{o<_s24;Mo)i)`9~+jKHHP@RlI(ZUpX4fsX}&UqWD=0$&aS zKMw<`#Cfp)ivUUy5I7wU{25UFD?x%c;lNt~xsd|F2yx&y0r`Cj><$7SLLf?ah(+SS z2M~BW1uhE$sTHrGz)ONaXn`MILxJ}Pf&YuZ6DbgE5(lD5h7X{?lY_vS2n5kB?sMlu zCmu5mI;6!stO*Tm#F~)MHmnH=O~;y$&}6I$3GK$3kkELn2?;I8nvl?ptO*Hi$(oSR zpsWcAt;(8^eG&5OXl;)n{k3^pJ&UC%R>U_*n{7UcmqR)VwnH&O8N&ZX} z$1<(Wo>Nc5ZY)g*4z1AH6fya$6Fh25+CBm7kFOAdBpIM}3YHCT7J{Ti(Q|$xgm{jv zrNNp4UPOqa{xAL)=>OqE|NlM_>l3*2+d`K>Zwb4E9!J<^W-NF8S>N z=6N{W{53M9odofSIR3?Mrt!JuVgy3}UVZnLnAd`s9U3PdjCD8`-NN2>CL%dw+q4od z#XO0!9SE6pjJ*p|&9se?DxHA}+0sBcjcvwVls&>)w;3k+*qI>iD1M8xGY#huV^2rN zXIumi2Eo$3?nxo!*x5)>u&dLg^1uj!Cba*Xl7ZP^Er;z6KHHh-#REI9Gtoyx6?pI3J|X@xgMuvcw5WnyF z8_X$E+r3O&6;jjQ$djYU8O&tfN#zqNkr58<7KoK($iWw#@r%sH>q6RieGMD0mucgL z7cQXY|fCtKORIhKrg^n-f=Z+rzU|v zt4@)Y>LgdDaXQ+W`iE2btuM(TV4__*pp$oU+cYmj-Mt4%rQ3d$te) zQdUWA-}6y0I!8trTgY~mhvt=XPI>2rSQz152#Y$yvn}$?UT%V#eXP<`x_&s}&Y z;yXkBmU7vfINO|4Uc4}e74cQs8owjQ@3YWB{7)iWZVZiC3^ue7X5WYJl^P8!vpJ`H z{=&bY^y^!4U%YTO$7^=?ymsvFvF+I1eOuVwPatrxh28xs0%x?ayI*X_?!K9bv%9c- zH2gvfyL&Srf84_E!Zy?J*IL+J;%x6~VRyfZ!1uSXyZ0mT;udxnw&I3QYhibPLED=a zcDD$W7Phdv6bOyixT~Q{&EY}z{fh2@O>PF`I`<03-&Oc zhUKM7AqX6A!KN89FI5^rV0jC6_58e49{gZ<3wHI?yi^(Z!R{99>fw2*{$dGl9hlvM zU0tlRZ2lF2%c7KWU8#@=AgN{le4k0-=9=l`jPGaXC zOaK`d23x~?#M%&mGOM^WJ#?glQ*Icw$I%ANn;ooAv!R0N2F>_Fo%k==Oft-n;Mp<6 z;c*-*PDlu`?*HQdqJ$8ap_g2h5TYwPyaBDwI8%?w4zG)ZM0-tkxDkm7dQNuun3e?8 zd$PleBf&ur$`02f!9g#|4i{VVMo-EPZx&&2(3`TuzmEh5Jt{l=cqBOJRoUSOBEdn= z$`0Qd2@X0~mZV%waL~`P)r(A6(ATonQ=^5cznT7)tsW4K2>V>NI+z!#D*pjp5fH<3z;byj0?USM(vTtCqNCoRhlY`xm=VqT(05Qamah;a>hIu> zwwD$=`mu4F1vd9HF}o`dObxEm&SN&a!Iue5o#%P_+t6PMzf3mY1haN=^tfv+rype` zM)9r7-bxP`>au36V_&S_Fx0XlA##@gQVL6V_o^k+^pT*0BP*!)rj`?;2$3B<#B@wL<`u5g%=mVcQm992gXb5P|D19h4!oIYzP*ZBDgF=9n@4CoT#oV{?X^1jqy_ z<`On(DYQ^X3oVpF=@E{$l>01DXz4|v7e`BJfj|!^M@#S03+Mm&zL}L)vJL$D|9yV6 zj%H@(c<;^i=FNL=!n{zz#wb?Paax(@+U^1Q^yW^X4Yz+4zqlZ;9G=ntO0%tJYncS( z9(b(skOT|tifc3ffOdT~ac(~Cs#;^2@X#bR)@ECQP3i@a{JO@yRhU6+RUc7mfVFx=875$GxQ!R zBMDq!Rd&U!=R)1A7a)prbx3SJ2ho~`2fNQ=j0;Py#P@5=cJjgac5(rY-##lJCUPSm zGMfs1YvXRW6pm-uVX(~|iJHa-Oxw347#pnZGxVak-f5zzTepItDwL}kV|z13hDawH z@g)=yKW20ZzG%djll?_v%{v*RN7|!wFfohXf{ZkmtE4bG7QG2yrnT6M4c~s!5PP&C z=iLpl8?i0cVxMk^U5xEAiiM-~V5OEPJf)s`6qmbWF6T{rUlWhM@7NUYYsx=E$2$(M z<%n;C$=+%jQ?R$1!33WiIVa(Jn?qO7~-Z-@L%= zn-|pg%?o1tCI%kYN!T|pc&~kv+Q6teDjGgVHSd|VF;&|%4{6AY`7nr~GvS_@ay$BS zZlSBcC+v$h&ckS@+%DQU2W$K25r`f-!9KdcQhiW_((pFyswW*Z>8^^3i^rLL)QiyI zmK@S>TXaY{TNu(^J)wz+D$>$z9y#)_qAySLPtsN3h3kcX$32t9<3(Bc@ERyo>2 z751armYH|;j@X<*w-{}UkmbF>7(0U2nK>kd(E~?eTg#<#=|#8UrDNKdJ=*d!N!sw2 z;OXF`-txO>%TG?Y<0s?$JxEI|!_pKJr54evXj5B7tp&E)A)Y) z63L_+*p+Em=C_aK8BCFwF~&zM-x;QEu1)2L0-6VN=%wc)-v#eiCc*o0!u!eugojxn zzYgodt;$y&#MUL%F$pk6b=AAS$H-7C@f1Wl_WwjWCvYYvoiicnL^28c)CM{Qj;0h1 ziSgv}Cn6WE51ov$qqN-tlz&!DTLmFMRwbbq6_YRDl&>k*0GCs2=F4NE`LR7z)L{R) zinckqR~jR`eAvNkow}CxAyE`2ej`M~CQ0&Op|r&ZSC1JZDI)7zYmcJu0oBNmGS z(<<+PwhJsSPe7qSb7uNt;?P>*)tD=`Jk`{3VN?eNx_R}YD1idk@bU)7z^6WfqUB2( z;+2z8QMo*PFs=OxbynJocaiyFv zu^Jf^0bjnpANb0)UY|?dqatafDn|b-gTBTirm*1{cbu)UX9gUnzjRc3p^HGfQ-GqF2K0}>H z`F5}6+e_u*y{JaIM-e(CH`K z7Ao4PPY87egdsxafU$rNNMCM1)(#{%@&~B<$>{tE)zDSsW9vh-k8}>Lw2f>arLPy* z44mJ$;{>1|&g6tVNt67rz18Nyt}K_! zsn27TAQfVLZ#6EDm5=A?=9TBr$N(n_SAH6g{CG8pHRMRAsT*TSr^b@ZnF7L@rHlnR(_WUz z>82H(K#noN#2?PBKDYdtL)f~|Dc^{JL#-M3(YePbSdd#|Dw+`iJ;PL&TN8t}y9RBn z{0S45FHBUvr&hk35C0ab>fL`Z=|&U1Jy&zFgojo;(dV?Y;k#fwI-h3l*kewGaQ&k#awY_E2C)}^)=r7Igxxy&ExqZAATLWqAk3X;=^w+#@olN zFp>8ylWvt#RxCXw++#{SUnHJXZG~!c#eQy4qZl7KZjB|d=e&)9fAr{ekSY2!FE$`d z31k<$bAqvx5-gD4B{nIiX5!eZw#FHQGz0a&1^#vRy&nf>{|E*v@U6j8n%19)nJ`Lh<`q$tYw))Vk{v?xDR zO9;;rcEZgWKW7g46H zQ-0|Xwodk9K6){W zt63yKp?OOeRDBtjW-mobK`bI4K~b0V?)6upXAmF12)RPuLk;QnG#uO5jGcnDa9bQ5 z+f%tT^?rsHVRDm^YZJLPchL_lT;J=C^8&9hd;WoeSLm5v3cNy6U&9{28QYUHc9Et3 z5JsM*dvL}UosElDuP}4z4{?L(6-rC--Ysfc>R5~|8r0v9n!iop6_zZ;jZ5Gaju={L zF~+Jml5=y)@7Fo2{nG-kFtQZ4W`WnUWsI?~$+?G~e_+M*XH*u{)s8tnhaufZ^A#7a* zw+!bB71|hMssuUI7!Sm%(o{FFlQhTzAs+)AXfT2!;H+vBbTu|X1-LkSA8?O@5v@QyG2iAc zf&on6EZzZCHP*fzwCnSO>A+cxYrklqCWB<*r1?RREF9N=7{dKWFS-oFC0SV8zbHr+ z%9`bY0nO=#_1PUP&#B2m(p0TgVKXKMWzA$EYW6O~x=jRvqGTa$Vh|KT-DR5UgM`vg z27V9fm;E;LpVFDUX@lwTjAUS%3QrpSELNSJ1E+6CFF&YPgxLeAhVUuOp4aQ;2Yb`z z%L0RE!S4^FU}oNnVRs{S!j{IkR>ILs)OP>fvL$dzsq)-Iqe15CO{vt<&^tx9b~f2B zw*jkTZUZ*I{D#k(bHV4Ma{&eXJpR8MuYL~a>DkyAzYpH&8Y?%@VwvPQHuDEuUg@VL zi_$DL7yHydYy*W$>)8}Ou2@D#$oA5Y3fpR)KFR4S&up%xq0B(6y7$O>-?-+bJ16Viw19uYuwM1 zUeOVHu*7iobsv|k+!Q;P3U_mJbCXt~r1Q1d`FXCTmf9wqwhBGAGpKg3Yn8Y}Da&>S zofexoZZ|)ea>c-XDA1}47kX@6#wr}8YcB4^D?8fEA0Zma$IW5)i_vhSfZt(;+ZO=s zG7PuJG2Axl6iY3~7MO`*JJFc2Dzh<4-WcEYu`(?tW~-g`Kr^uw)S2MXw$mEAff*wN z!7jBiLfP>ea)_O{&f8gJo_8wlI3^zI^?DKw)xbLf4tTy!6b7(?SP5-=c@$KmCaS+w zeE%5gnA`!Hp9<2kt3spUv z^1hxrG>hY~FZvKYUu?kdtIzD5|E_b}D$JyO24VREGciCPY> zrLn0WNia^!%BE;pp}-PdE{YE5@A`;$QG~fp9FKT(hQmBBYrnwX#ysTtqBRVQ3SVGF zLSaG7d(j$xmF@=35}(x{U0pxr$VD(HIRu z!z9Jun#!}x*J`824 zzG-|I+EIO3J`A0zKA#Um@uz*vD(0yj_#&u)7JWQdZ0M}t(cP`-l~xV7pnt~_MN6jB zNVepDwB(s&^pvLf2SDrMD7k-akI5RDj}-}f&^tGn-nk*_ zof{g-e#69MN3s?`lr0@=5)x_BpG$PGh9TPn(#U9$-A{xa!FT{*oXK;#wf#nK!r~%E zZ$zw8Kr7wRLEX!!VDa35$b&}3tK45L=-NYTibeJ}x7L#nreuebzgtf}1j)|Gq4=>! z4#VU;npX$0WO%$f1lB`vdAR1IY`lxh))PMg5aRYlx=cowDLF6VCX`nJ3_Wnw%d2QZ z-A#7!RWw9Tgj9}zVgkMIp>5$ILs|q44|0$ycol#L!V4UABnlmcpT71b8->NhR=RHTx>t<|mqufW6c|6A7c!W08X7FzG!VOA9_K z04H`F5qI=>&=!tj=(Cj{TB~WearK%3Y%CP9@8k& zPNrdT3i7Mf`ed0Oxs$Xo0wP65ba;~fQ2{BiVAo=zaI1rGS$ zPTx^RHJ33og#`FaoK=R`Cs-GrIH#@JcZugI#(q|X)zRcS0@G}(R^*JR^zz2?y) zUA7t9X&taeZQ#oftIgB9wff*OAeS?8pV`UpjrxuPMJ|s{Gm#&B3g4nU_u%(F{Rx`P zccapokFU1e(TKZDu?FMU2QW95Zqc(Fsf`8VtLC4X%z^KAu!@vO)j;$}@EJ`W= zbC9j8=ff@Ow-d(K58a5yDw+MG_%x+-u7=}upURdW9N9ItYZ6Z-5)(~{a7UM?W^v_y z9F5Zxfg$uWiv4tTy8)FJ|H3P0;^1(9v-;=QV6FeYMi1moomx36&BVxql(T}C26z?a z)L8(m$g2-PR5oToDHQ3JI@`=sl)>*qQJ+85G+)8*@hTcp+|r$t3mti#(26RkxIb%J zI|nO;7(p7B>j}4EKm#8CJB+b&XpP6`z9es#+RTAZ0Czf)O_G3G%igv7U8DhzcSE&&s-07$zkPY!0& zMp_kvXW%h?p9~eoY8{?KEPB+5w-<$a5Gpm~QgrUJSnM(=gTk+TrXnDwW0J=a$za$T# zs*l8?H6~gECMxHF;kmc2?*}(fBRKPD%FIiZ{h?@XTMBDK$<@C_OJUs@-2!YXpRh^v zQL8`Y()-T&p>(*7>!}fR>!)limiTve+V%2-LXDyqi(Y;x?d4`a=jNgm4aQOx8n@M8j!o<{+ za0g&ed3u_OF}s*EdK3MT?PXL(Or7VMavm2_ku=+kGbYH#2fRXfyxPa}%r! z4B>g^d_;2fZeIO*v=&gHn^zx>5-6bQv}bKEk1b8f!)r^^Bebq4c(QVnkP(FiIEbo1)Bq67+TwDI(n7`76O_jz1?46BXc z@?!LXtwvGH5-!gtK0;oAXsxM{Z|gJIcTCOSHop|VhRaW(2S1dK3`c8GF0V{8QTY)1 z_L}m_!?DKG#|>EGp;)JLXct?wPDvUWTaPi_&xrBE79PC9u_(@W6np6N`pc|I|w1LS9dv1k^?FgokRR0=Ko^6lcI71`giqqB zg3nRW=&1r!q&|kV?B=Arv^Fnn@;h*Tf(5M>)!~bhBl7)SMZ42#wWxM^8IDaG(d;J( zkAgqhCo|W!fvR_c1Bu^=2EuqjSI{(MfyzQuu$L9pf!7%BASfhrYhMJM+<5rOi zxrtN+V{c!6i1dO#0WLf!4;|j9fvi8M%L$pjpnQGPcP`}hsL;am8aH6w<6hBHSD?0x zr9KG=KV>sN3@mjeagtXd>C@;HS6yv0KdC;0fA#qeew8kuJ>fAUr@aO>by}5cwdfwn zg1`~#vw9H(uJAwpVd&KO;ei%aE7Wxm&vF9ArGMwm)3H(qCn#L{SKt44z}}+B31FB0 zo##-J$PfBLSE)XS9{&QFUzq&^-b5}Wc)zb5FnpdQf$9rrX4@>2U$o(4fPVUm<+l%K z>rAFN4daC^wJCPj0o#NN%{#7RQ=ee0OUN(P#)d`u5H~Rvv~u|XbZIlZ`a|NDu16)U zT;8C`_BfI4MnE;aeAp3eU6#q4Z046&AJL#SYjRrjGzS;F`UGTE&>bE@2q`~63iMw` z5CY$ZK2cvXmnlF+eK|TO|0u!OMHpZCgLT#FE9lQQ4W_!8j(+lf?dQGz-PPZVmImNp z-KvfZQWa)At=z>K0P#-zICxTgk9RWNmpHu_aU_Ys=fwfp?&HEP1DG-1k~z zxeIDya-A24q8)Vep1KQ_IiamD+z8y+N#Rm@-y5-%z@fU0pN1N4vy&#zz^(D*r8w8? z=JG?abZ0via&r%`ti1yJRQVb{0*<&plQDSX!5UX8Y8zW$8=a;t6s+{eg{Q79l47lNgcU|B7v=A_hFB~ zTQ|2N;00dE4ZM8+*5U3vaO4Aa&CPFVK_CK`td-P**uiH7!U`PuAP8y>j1N)Rn-w^f zucL>)hTVmB$e%@%k%82c!l<8L=-+g#YH9=4g^vt|lQ%=$VPDf2lmLZenWE+sR=;xTebSz(cmdl6qNBj&?3BL!%+o>$0G?!q> zGF^JfCy492_uZR}u$mNT1B?N!872*k0qwnEqPr7^ebVrx7 zY~7~bJeL@3m;RSAzNxpPd?uxKYT|9`omT!RC3I#sAY3|8GF$Q+!KPoMjs0iKaMjid#)_yD9E8#RcMWsnRfI$z^llszJWc`&lPXe@zF0v=A&OA^U>)zc7Bu0NAX2IK$3Bt*HYg?;Uy+! zt8XKE39r6jo(sb-ujB$e7xo~%F-xE7<-dahwb*wXYx|zr$_dQJ8ghQWG3VnAAD?La z_=DQrzqaSRnE1@Y;g}|D7wU(^o>69(iq0|;!rwMI>C}6T%X*Bz$Xa_pYHaVv4Z!?F zf3$4*5Y8sAXr>7~2|TG>f;0b<;Eingb;d&VH0dGCcGWWw&sg4ICYc*(NyhqWAXT=H zs@cvb)nk~`8sf(ro>f*qCH*@)`~U>1kCW3KT~FW;SmcIEggqhO8a!=ZDnK zNT81#fe`9hwES%S0bz=NsiyO=bYD%AZeE!ZXak{Fv1EKQ!FXdtukmwWGOFbkKgX_y z)osAl5!~Hklkx=)S=geA7Hv-{m*l*7AM8@%u?-rrg!aAie*cjF{r-J8 z!PqAfjD0piZfIdn4wDq9Es@Wa=Ms#4Il&k_VSY2g7<7l-9yqmF`ilf(Maxo~Ax|Oi zkT2oXG5Rw2kY7=!bNN&x0f-ouqqK0Y#geTVCY~aHDWPi#*^*(x)bQ(i4eB?K|$Be#1zlV*E(eDRFx6tqIqs+nY z^3eqSE*$0bJA2fk-)%>2`Ynt)^qU_Q^xHb>(yyyA$t2T@o|y!%@c$QHnUYHrc3$p@vVMYKi%m(0vnGnZI2R!LH%r7M^ zk7h}}m?K#koMT;1HhowZWzu1OZ`F60?^ja}>`*wB)`h2VL4~AyJ$4yFu%Rh zLOr(YXlpFCV020>CP#xb)h684J!e$f0vnMmg*u3d=P;&#V5xiow3&qShFdb;Q z)Pa^)>p;UBz#;S}(TB9pjqAASpo$N(kKA&Y_6urbNBH&jTYi5aKSd(6HkFh;?b?*DezM~$)=o1_O*@91nr(CPiRym3@9lnR26b?T}!y@ zBvpW5eQmUTo1I-(lBFatov~U1N6(8C&5LxnlclyMl4?@SnKQZ#zJ{{7i+prZR^$d+ zOinS{`~Erx{Q^C{PvLIBsm#Kxgbu^6!37rAw(32mtKYRNr)A(5!m``t->~KV*nBHx zO`y!N9| zqu=!kQ~Fb3n?u}IDh!;H$vIsrjB0g#ZrMUt@*z{QGmef!uH|%wiz29k#vIV;Y^-8N zV}>1S8E%fM@EZ_2Z31dM8)a?juhB5uP-^tuO10HXwKtUd!~~_DHd*uaLg|J=b*FIS z3r(A>keeM}NZ-{!Z}lq39Q#t`f$32$Q;RS1{V6f6KM;j@lUPdTR z+a|0tlfhDVgVhYn&IXoR>(k}{f=)55e`=T(g=BvV9;V4Jh*wt!I$m9^KKvKKk*vXC zk8bNQUrJ234oB_oFfsg5Siz>sH=t8aZ$!$}2ai3U9uHranAV>UTSOr_lwZ&UVXI|) z%oKw8B{#1vR%e>OuBKnN;P=mP3qgNJ*a~Mffm;as({5p1;}|BtAa%I(n{-zvSdjO5 z!)dNioOhztXSIh}OUBnb+-#@Z(w-Krg6aV1Yz?@i@=!khXtu6=`O$2h{40FM~$qAg{dGttJUnX#d4<*mT zed(~%Q`>WF7-2S9A31GyXtiS#Nx6MfvMi$iJPXRTuIhwiD;V1oBk2ZiVY~j8z%5kf zwFDwR)Fwg^i94cwn6pF1(XmK$EVK`;^d}%?^IxkWQ<%}{bD09}26NrUF^sWmsg2q( z5lE-pzD!^f(rK?R73N(+IulOu>NsaY-kwR~PBm8)G#L8Ycq7kTs6Yi9de>ZLaF7h0 zy8Z3ZY!%Lq&L0rZD{Meu9Nslo$)G_}=gOmuwFiQ|7+XjLZ z`AdGFABxC?-GR_2^I`?$S}#t)FyddxnYd5Rq*$!?LvrGm76HAWOHb`tN2gzfT%r9U!89O8FEAa32d3|qx zj=^(&hF;wTZ^%V?n_~eNE> z{^%23@5fqJa~&PooBPe?|W!wJv#%xXuq8UwjA8O3ICN?Csu!IE1u%LJ({z=Yrn4g>786Uwl zu`_ZTn=*%lKw5{5=1_`Pws)AXCmv0dz8p8T)D)9wKI#JHg(uWz)XpbBLj}MRFQevT z**%m`9r;!4hDOc+9^Jh9tLP?_0^Pj&>nMQ&SjRfeTrcm5MkH287<<+9ZHtF;S~92h zr^+8VAzDo^hdDbCt{F+*w!mq$6QBgOkPcC4Pc3qoKUH;Q;4qEu6n?+i_e#Qx^rRO% z14A;z*&8n|bxw#`e7Om`r;()*;?hMSsl|rqcb2craq)Rst=+!69 ztH|zUEwziIwQ?w4*%cj=Q7a)saMf;*F6cpdck}ysnl4we173}Mw^|H7*KeeKs}TwC zN{~1M!(6U1BVYmymA?oNO6X*@2Z37ML|V9w#RqnXr8IxUy_wddMtYLgq@)($RxYLE zLlxB1HD3>E;-r9^_|gJ+7{gs&^?HZd{RinPVh!j*R6o@_zCvA;?)+~+-FQoPTR@pj zNJ%x|yO`_hfT4lf6P>sthqs&PoMmwapKx*7S@q_zQz2ct8SMO1`+bWlbce!34!Xfv z{XGt+C#0WYVlXYVAT(=f?sVIE9p2(kLs?^7k$TR(jLc_cGI~q^ms$zNE{lcnhv(4t zf}{T;`9vYcEg2+<eL)Y$8yS7%vQnQfsZF^Ykgp<-QK{mz=JdM?RMOOzb9CuQP|TYyK4R zjt<*4;=${*FzBQ&zeadr6I zvY@Vys)SyA>{{FUjboBw=4S6Ues!BSzPj?<6B|Z{mLO`ssQcj{q1O?+#jY==uv+{b z#zcl!U$dCqK9(SlWyJ+t?nn#g?+l~!dc^2Zpqp3Uh!QBkYW9|dtE_K3bMjo`ZbM<0 z&do`3IXlBd_gom!%7)cmM`MX*nf4oKRM?3$qX5F2T1mw~3iy%w`v@=!3~;$11843X zV~OH%T*nIU9AlC3LB}e&5EM7LwEy>7t}&AGk-Msp@D1icX|M*`eJ^f>0YGPFHb8_ebN3jnB(hoSypO@s8EBDq*rI7m}5cyQT1^PK#kxh<0@+GXbmb#EkQ9c5xe-%k| z5~v&o>~2DiF)ltMj;Wp9~XeQvd+;rM=1lIKh(IzR4yJ02A72vSBzsmGsDd zpmHF1A@gA_KtBk3qHR)u%-84qBs#~ZV^5?zTzhXKeb|0RlFMy1IM)Mv!de}LWtU#D7wTxua~=cWT(t_)W|g%($zhvV$EVHCT3MSAYO!5) zEFd`!c-aOoFKcVOHqU8uCPA%ul0hwS>bPxYx-VRHJht`934rj#8Y{bG2>VO}H=B^a zdpFi|QiS(hwtO9%1H55Paocwy9@l0P=L^!yx8;GDZ)R|LLVf^36Ff*Nz=-)TmiaAG4aHrP@z$wY4M(XJj#I&t*INNey0(i-f+3^)lDY`_=! z2+Yj$8L#9d3p=gKaLe}OL3Bx)3~ZuFo8uCl0*9#M>b(jAxjRTlqPEiad7~%OiQ=eO zV&!?4iBv8V_PA=Zi}{I9Np%XSJsr8$C(J8=u~Gw1yOrJGYMs-h_KYGg@)W^cyBjx?h2>KEo3)F_TU zjkW4=wvp}jQ$7CRhX@KWcM%=X+YP|v(&04AKx7M#Bxa60I66e@@iOqDX*g4FH(8U# zwXwOo>b5X0-$*caH{?v}A#%(D2SnwyCq=e_4jmwkH7zk#zC&WTcv8wC?k}E{c1REx zPs+5|rGCpHhLavSwakwzhIGKwKS@u=!?`B+Q}nvA75%zm$UH#rnUthwQgnWtcZr?F z9}LXkmhf5gOD}loA=u_P3cAm2sUS6sHxO`9(KkIv4dVp_FvWc{gVZqIKft-BPlk&F zCvbJYtD`Za{P4iQ2|SauN+&H3oFHkEPSHsvu2->_Ph#tIqk0wlZ&zfA16Epo11{_R zz;6>)R!D{Uq#tc|h=-1o(|pM3UsL^@lTm$5e_YOYw4)APJ&kerO<4z_0S1-P_6A4P zE-|KyZ#1YgpLcRip7Hm(p=8V?@*nzvuD8J`Z06N}LTD#>74cFJHxfF;2ow_25xOJD zsqsc@R5uK}i6bPJFUGg|c{6V7%sVd!PTZF!a8 zk%Ji~${SCHNRXQS^~sSW^P9LG!d!^jyqDU^^E!JGDZa zJ*G|krovekxoj$xEPAy&9Ts|UX~L`M0io7pm{^qw=Vuc%J|==f9g!?KAL?{Iv<*B`Nsx z`=UGJZH;mu9vB@pzfhiX3;t5r=r6)MHf|wUis_8Y^^eElV_1p4tgL@IfxZkyU-qnjSxsM- zM_+cSe>su9^hRF}t$(3?GBo%9<|(%UUVh<}=y^T8p2L3{$%R`JMnk4hK4Ucr^~(-f zB_j3GpGR+Pgt_wL_0Q$kSDOhs>63l6*CIy#70Jrs+x-DL8zZUIV-0VwZB}zaE#x@V zA-&(T?8=H(CeGFUuIpTv`F|dwF?bwf?7`6B^bi901mZ0+!pd^B9zpIEb4@F1*P7be`EPB!{hzw6`NI0g|Hj^nfE1>i zVEwnTkA=zEtcJI&3M~G8e(XCZCC!JZJZdG5%(rp_Ny{*1@V;OX_onY~p`iP9$`9C- zRs*-xxyF!OShf3Jn^hQN57XIs7m|gIyrZ#VHf;=J7PB2XEBT_d6&Gq4%s!YjF7Qf^ zF9#?rF9Cnn!n^gioKA)#r?y#^x&|*VN$~!>GK!<$7IRV^Grv(Bt>RHhI-R>*f;}(B z1s7UgQ9F%l9C{)gEO{}BE@}rnUzCy@RzDv@Mc`54U+)pZx9Pl=Wn5mEVWM&t_%zE^ zXJhyNMVg7t{b^nWS@adKoZxuo=_AaSlT2)8WaokZ`ubQu_(Q;mr@p>HYvwr)^NA^! z=VFZE<{6a}k@QzHKuVp$XmONUjpoJ$TGe6JB=GFJCYt?^f>Y4keHvCR-I|DAV3UiF zN+;d=;{;)hR6KHcGVq+X{c#u^JbzQ)@vgGytRd8S`glm^!ril69~9|us1JdCb|D?| zLPsVnI2tVS^kE|_9TIN=M}?b-EKkbkVfy@YW2sF?Q5?JnuGl=M&7K6m`kB#gAb8D} zDP!x`>$Bf&ynO!}w$3K$n?9=0dP&P6w?+~e-}VT{fa~Vfg!!fT5-!K|WUx#!{f=_^ z3E=b&a9Y1BxX^>)Y5_)b7+&>aHnL9}b#!G_L@is5V8E^&{W6{TdU56(#bNA<3=w(--Hv`$%ZK_6?*6G)*74R$YD9vr8s#_p<%rbR89h0C-Z!=L8>tV=Rd_VbJhw}Hl z-~3y2{R2_{jd+Y0^#(33?(d=MC?py(+YLc$Fgo z<)?63dr^=ig--HCSAEc7J~s7)Q9Y^~RYo1`nx3KGODuIgbk71VZvwnCY@97WM4}^G zzGF78cKH=8E3-&XO8djR0F1nkp(xMx;QRvd6vbnE&XinjTk&YvLPyTExw;TCgFbf_ zuUv&yHEE#o%x@I;%`Ym(S)sjM-W3T_O%OJBG!o^V=1aS+znF2ip$79sfUy;DR6c^> z6bgP6!GMAnBbcS&B?z`r@H#-)-i+FAr`Aww(K9kbxt$g!M$$I3ol=_PsXyCMAH)3V z=2cWe;!O(7;#OYXSK|xtwBmOr;7vR9vkkZyf!e}!^NVW%x6s}{-!0_(mxue9*Yj-o zAhByJxaJk*BNUy-HG?Q0rsy`@TAxGoQHoCG*7^@1`VESj$1ROs6_oA23%KA6V2W_B zNpnx$n_*&>w$tCZdf*M%`*j(gJKrN5E+yGbt5BDbeALw?%)RdG4Nlo&HkVy9wakl1 z14K-{cv9do$Y}9ECRSZM!nbH^y8OfQcvBL*ijK9^MhD)8)gAER^l?%RZ*|otAlu^} zAwSC)QNA8GqBO6neHDh;VUSd}(8DJ@u!(%L%uA!ci@!8#sk>0*a;@y14IFg^iDG#m zh>WuWN0)kKb2@NJaM@tW-Gg$UL}oY42Cn)Pf*ub%>Wx`}TQBF9+`xcnmlj&;Miku< z&pRZ;AV{hm5cEah6F~w4=c}93L9*lq22!D}q?_NT#j$0#wJTU@q2&9R=1rd<J0HpJJo!rVRM?mW|Mx~$u-;&JeG@S&xS#6Hng{@A?Q>zb=h zZE)pcsUZh!ATnCuv_$1F2gvB1Sg7lOy59VR`W#loE4U?ppwak^{@JX5ey@MtGPvn` z!5=o`wnKy)?sa}h_n2tPRiDQ$h{ez>z^1VAoslZ45%_cxQ#BzDmvi3A_Gh9OU~t=Ia;q5-~k+)E8-q5R`5OLjDLtWRn($ z)6japaj~$~BdFney;$rRxatO4ECi+N2D6^L79*6!Q$8D|RIWkjMl@m8l?R*F6=DHO z^9U(X3S%gWEGPB}+y{`&BunWetwDmA1e+c(B^{qm*q-CP@4%*~Oe-J4()>+;WDEIY zt??w}Pti-Zkk6U*L*M-@W!s$)turHRug7#4;}6df|Cn@oftRHYz-;}q%|vA%P8Z<1 z?wU_ayj$=S>Zj?*xh*Y7okGpRf@D)7cjZq&^_%KyMlVe`pL^#<`dtothcWgf_Mf-l z)zMP-FeVm5W}}1ZFW~XMM9OuYp87ImPCAh%<&$V_Ifn*X)LLEB&XoTv$}fV~7#mMp zsm0_#CUDem1i27t?i-M>i5NZLZ*RrXduWe2Tb_uVoM*9`R zK<(=(pLZ&J#@@!F!^usJGg%YcRqxnLtS{h!>dLJw-d{6WuhIMC?0B}%?NNW(#F#u4 z>#tt`uYaJwen##vE%hQ&HVg9TxRp>Z03(ZjT=f>Dc29v^2jXM~?m~Z3f4~XvDN)mp zd>Ctzd8@mW(XHu*idMw;!6+2}S&$r9Y6Yxly2^h6?i%gVX%nEWGpaw@2rlFJcL`^r zvKVrsyEwVCQyUpxdA$P9*=O~yn&C20neM`3qrGw&$Q6ypr|CYW2ep2M)~!yVZpO0Z zP@?6^p8@5szW?#4^}(N^asFHaAU>a9qVi0F#^zgihxQg?eL7urD+Z;YMDEFF(b8>n zO&PURXM$q0Nw?X}dUvVo*l8Qa3Jj>< z)$vq$#c6e=CcWoj>^w_-JQHK*_Gs+fq3>vs$X)ph^yr=MckCQzsl{~gSIcof^Ghrt zi*SxM;|exvKi6+7v==KgJQ`44fc~y%K;5ke6cV{7pGWikeBuEWy_J$@y*t#`(h)`u zBWmO5P%l~|4$n>!oJCt7CG$i)+5?{utl&?aBGBHq#%! zZ05Vrf*-LJ@Kkzvf(iLFM&cC-x+uR43C|#5BNDj0n@WEUDW5`$Wvh=P&(D$a30(+p zeyghzHQg+<{Q}{RV}SZvZ7e)s#=@`kSb!tp`dC10vax_esR#GM7z+=@#=>{rYb<;m zelv*=-=ep%A5EEjERaXuhAiX80&208W7Wq3`QS}pEW88GN?2+xQU=JAI^^&QZnJAc z4jU760JiRcbl7O zze7ZOjJw}9kGqwVkGq5(cMrwJ-FMz?+}U*Q(e#duJG7upr(dB&D4>;G%a*TZ-i6J@ zSUb^*2IxuJnSQjszeKzNJTt3@qxJm}%^M)ma^vZGg!LJ%pi^rqEuaLEtdL-{*8gu3!I^ZRD)tciEMepT1k`3AvW6l7MC ziuPI3$DxHCGW)c?{_#k5Z;<)%#QObdwqQlHCfn?=1Y^Gj?+w3aKK+}r>gH+baK9`k z+q4X~lk#=rlA*~sa^*Z9oG|>5!9Wiu6TjpMlcW`A0$V+oCe2`&mGyl^s|D9>$^`kX zX%k_X738?4x2}-g=9)TOts+4_9N|+SE3`+OA8u~RinvD`H>&LfQya8CV*28EZxYJj zl}B;n^K)&fa8?jOnha8;J|1iATR7sS@`F$*F+~S{5_o~%0LCYq!HD$GI@l4F=pBuV z*`i0I@Xy&9dn9V>aVux+H#BBRf0>9}`8Imr5FAf};F=fuEGwrPMHuLFyKo7WU~IVw zU!17_!psz5m9U9egx#WZ5{&6M)CqVdxfmKlu z*M>*fslp^Js%@z-ZxVdPb%LmT2TIjJigeR-lXtROleiTi#m91(DIm*8@%5O|xqx?CeD~BCGo4L{rtS;0j zC%%a>_S>4DBeJ6f4!-DH7>HqL8bC_oYR3lKyV|Y64&mv67{;=cN%rj=h+%B*-DDqYgLmh~{=wL{#1}2DzFS{Uu}U6?%Kh z9{GyXV9JzQ^c6Zkz?vy7s`yGDwO3b9E0d9HSC2H+vP}Lt6FGSGNK-9JA~&xdX{tp@ zWq9$$Ms(zc3{;HS0MBc-iUTZAiOM3OMkP99&?cWVc_CHs(?X`R!p@e}Rr zp`XmB*~6Y)URkJR^Mym|UNT6?}_6NB^CE^M7WArC}8C7ZG|@j$v3TRlkJ z3Mi`!DIYx017KXRga0YEwc9q_HW~)z8DdvJOL#4Mjc5shvwDJPd3u(~9~eIIkD8QI z8qN%eO-4#w5P0f`?~jzWCgqPn%1bmUAD_S{VVa$LQJt1}sQY!AtDZhMJh*srelk5S z%cafyBx885lII?%b1h^v|9yV&dqZ89gQ4bb1qu`9!Vm%^2}mU%G*>X z>8k53CMui2L;Z)n0vzB~R6~{2B2Ssy zLdvl~CtB)hq&{n6`Y}5s^`**B)nzcHmtDk|Pc>cZ1ec^vB_r?FQ+@}Ar-1Yxsxf>=f*Uja#+TjMu!xz`KCd$qOs)mq3HESRdkYitISgPtA7!b9 z5D7ns;-T%TLJO7|w5KZg(88EFAZlfQbpyjYwdu`U8J`BVcGNFGl8ZK5voA@PndYOq33EIXeLNeLImtZV*B$EtIkknnE5jg*9`0yG{FjsEa^%; zp90-nPQeGra@V_asL?8fjtI?Ln>3G9ib1Gwx!(w>!t5bwE_ zUy_<`uJSw?EkLR31sL8p++cWWDTIQX+v}I`^@|j@SZ1e3wgcu&GmrT@M)yiZCJ&76 zEVgEgkBoRBPEKw9KzelqQmVXbtsX8h2M9QyAWgY>0x>NNfN`e@F2Scq6NUxP%gz=i zHn+zFkK^or`dD&71XnMce|Quwu$3q1@O>JvzEt_VI$(y7 zW2=*MH3MoZ?=@^D@=HLwN&N|;?KCVQU?@+N%5B+aW<)6(?-c0f)xIc!0^PjYA0<$r zn^$K?2^6@5S3_I7AjdCPJGQ_D`S-L7awNep^F>njM4JP=vKf&Dh%C?%OD;5@UW$Ku zss736vO+iYcp4{5v1*H{Yv!fOx1dXud6`D%hMqg_N_Jy7SMc1L99P>Xn18yQ50CxK zk}pA=BNu5ZR8Fb2;m}xoE5U*uxVgLxmH>WkO)5NyyCNqxu2wH&>~&|t`FVPNicW!A>kLj&J-y)}3uji6e1!_+kz zX6VoTkRvOH>o)MpBnCcr)vv&ASX0*&UAl*x9?@g8Xfsrl`zGr<&>e!_iOS{FZ?*EqE9l@?9r(#pzr)Y$!ZGSKx}A@6i-BX_ z7M;PiS{BVm!9MtrNXCX64F-DA+P0IPoY}RJD;A@l{8Pr*pS2$9AsVcP4S3qTrSf$; zxDkRG&U|@Hij@6kdg{28xmeEywyLDyvZRzsTJoQ`bL8fkpg6MEaVcB09eln&9XK8R zJ%Ka5fBSH6t9*^a)^+A9e*?q*2gbw)r!t1;n(0GVZjukvC-^Wn6@eV>Kj{B`AtROh zkkzQL<-@kF*!%Y!g(8?<7Kza#wD(!#`>WGg8W`59UV?jmm4eRg3==+H_ncWc3!_XSeWmb>c( z9cuu5F#Jug0ei>SfL_CcyOymiL2ij^HRyj&uh@drij@Nt^k*TMKW^rK$+Y!-V~b(w zl|WlGa#@UpYlYNdu#1)@4Eq+&<+2tgdVSiMEuE~M0Wwrm$HLQ0+0k30BknD9#0{f~ z*}DN>XnM*$btRPlw#NLWdj8v*^Y^1sjC5|vJ@mT3>5baIYx*75u!dSLmD2!DUpT{Z z6){OxE=;*ZE#(T#Fq%7h#Wkjz`q)GGjicqY#b>S3R@~Axc%Zxni5BUNtJhGp$a6Xn z{T7ca^w4hI`{d8COn3?AZ@_{9?PTIVY`G6`dX-x=*tjhZ(Javre%j>``012K5l&dX z%Ezx`>uBMUhoR(XRFNZZ!2!)`4}~L>g86cPd_NQ?8E97vNBXtY!=qH!$;nhL8N=7< zvpSJ^hQ<7-;Jy~~9X+8p0M^13V$5-MqSe#HCT7n^)&Y2^8q&)df)k1-f~4 zVU$3DZeCp!B~YN7R~JVK6d=9yW6<77n!~e5w_Mno-8&XvD9>@x9)nwme?Wd{@_|Yu zm!cf{+>ZJBHt|4XQ?WzAv<74V*9JOd*+eL3nI=z`MRgM)kBE{wkpwyu^@uAXh%I@d z0n>ztFM@~ef6`j5m+4HV{OUTkE?G$nY&PvGBbYgj2)&M(!Ivi3tem9=63q8} z=xerEzmJ|}yb2&HCt(T7irQOxXnEje@ZL5(iU9ePt-5fFk|>g(%F-H+LXCjPr(&#O zfzzpK10MK(9{49opWr_<#?D16vs;-cq}BHs3vE&pEcMu=m{{u2zdDu6DsulvFfl7$ za*4XK6>_YEuv8oNRh`|I>xe#-F>J+RLY3)iY&E5o3E3`~Z)X!~s_7etUBMXpuhv1z z6}2%0d}_fMBJJhpC%w|{UcSE&?(G$(^>61Dww|{$w}#WOeQ;WA`CtnkVqVNGva7cp z`4vi#+#23H%JX$fnwra3%1{gqQ|0blBtv!mIBL5PYJ$)*eRs~g!q?xWeXw`+&kW~q ziv;_&{3*&vhtGwy` zJAhcbYq0ij0&DLku$DG~1+XD>x8)r*u!)rNAp`cRGbRRm$2i#0+tHK4JRNlN`?GM$ zHm*V3AKLOB(;#_SpP<3z6VU1ZR-0=lL2|{oHj|{k@(%4ac=5cRH>DnDER3ftj^>U{ zTBXqpYM3-vG*6nGnKXG#SC8!K$0lfRx_Y9iy`AdqL9ey3y;3WhfYz(JP&W-_Sl!Ps zNLoQISxw`eF&aZxYD+9qWYq(S2@pgtYxogV@=$I33#XWh04(51L z-lT)6a=A7g1G|CPJ2%fqkL>v1=+{9sJPzeW9ko^6dK?LbvvvE4^&QF>~cfZskl zNy?G#_%2A2x}@%cr0#+)u;R(At)@pSw5|*SZGYe<0~`F3CJ43}<6+CFA2xN)(&mM< zVHlu_hG8(n^hCE=J1urY&DQN5$n%!Hi#^_QGFqe5i_WN}?nK7Z&5x4rj8vFJr)QO~ zJ+zj*f|AEQR=x|_!SPlmMt0TL9BEve76kL1${66<&9HRK&F@a;cW3jv!u;-Hes?v$ zDlb3zXTJ}yj?A#TD`+H8AU z3>58FDfYF=-f()q>lSA9PY>sDlW99B*-}v64k=%_3y&3FkC{zV7q4Rx-OG2J0rTft z7JX~HHIHn(7r=x>L!)7CxNo!c&8|>u=|S~68$)-*Qq=D01nIhQ-pI#vs2?>=t7Y{y z4|m1frYmKekcpNxGvnlTW=<7r{K&CTI;T1O5Nx0lMQzs8u%;x{j~NTGrpytZ3iWc* znxL9_lFU|;@^lceB$)4F-RO(f4WVv;b;Dzk?j!ER?IxUb{k~BBSa#HGu;?%=N`>3F zWK@I;i8*Lij|VTQ{*3{g^yC^6snVVJPa8Btv}~@I%`tc4jeKRHk*|cMfmMrxUlswS|G^T!a~T1 zG%B{MQE_cTMUP#)59CcycdL4e>ee<}+GNYLBNq{=nOa=i-_x+SVaX<1_o%0-4ogjE z5Kb+H{Y56h{HIK~B^G`;7XFnE??_xtZG*E^FFv+nud9y%eJ8ECk$CDpQL0>`DY3Lp z2`i$6MU=qr`q~|f$Iin&a~&7d7md1pFa|LkbLttO$Sw6(=s{cRPnK!cwbb7cmOA`) zEc~x1EO!8CTVz*hwmhKnjCfdKfWS=^F0>Vxq0 z_z@bvXx)GvZkj`vW*H8P-n*T?bu=bD246)Ps_Wy>2Dj8_ zITNKrEp;8Z=VYmvlsgGb4!f^a?|Xr*oK_~nOf4!v*l?!V5#BhXrad&J9N!0W4GXDM zKLy3X0A0?T`ux*1zSL6JK!IIWFco$Nl@n@(AP?h2OHU5~PZP+4T*amHQcL3)?odC& za$yaBj_CtkS56>mQr)M zoZJ;FQr1>0K{rPOnhYF*)K~^y7r#z6>+=G3@^#T&wa0FyH_#}ivOOKH^2oAX~xjv1^SCcq3P4{^+L zaxa70-V>mfRKs|@4iC)jBbZ;Bt~s$-^1g!XcoW#VO6jDLmVBHvXGuA8UsBGDQzoLz zgMX^qM?s%3XrxQGdQQ{jGtG3_n#SpVL>E{Pq{BTtvLK*7h7m@R%rYA20SgVicz(bj zy#M5VdB(Zr`yZGZmxb)K>mytQgOOod6E5xQml~IcnsK?Fa5ifG+-aMon)jj;P>l}qK)xlE4K26Am8 z-{$7qynI_S-{y->^*m#tSxE>lbr=BZQZFD~&q|#>i1ZoiMWpLdsnds$UdXA<_+YwT ze>t4Kw5gZqizcN0as;mB+ER;N!{AAG1Stt=oJKxKtN%b4ka2^QI+Bh`%m%(!nXU8m27YzJ2A;KuZ(OTqM1F8hzh&nSjFKy>h)>Ac z>R8-HM4UEHdfH0w3=<(6zOv!Pzi56pncrcYVYE4Tt-F@*j7O6Up3s5JHR7r12fdQ@ z+8vCm9*B^i3--b_-)HKjEIoxjctN4uGTb;WQJe zd^(>AhqLzdWPD=S>c6DtA(H9@Ehab{)Cn9l3Qu7t;#et9qS@eNxg|eKL(vW#GmH_^ z!`OxoW-B5)eZaUSO(BEmnz9Nn_S^ z8Jv&y=E{Q=wr*2zUK4v$Z%6qyO6b%);-=nd)B&61+lk40z=`$1$x#oS(mc$_=2R_KxKmJecmE;16RO(S@8D zT|b^KS@o-Cy6E)@bb&)tT{NlI*64EgNiG?wOn{=xKWOk zwaBfEEfwgOwaD#^j>$74vDR62vDS?c41JBp%Z`k(8PqoX*)`EIW<{?en+>P>Vm~Kh>3TVn(CaQUNLn>negUPRPDK@+G=z!)hK^dGhy7L&VxJ< zw-d9Bt4AcOk-GdbNxlpUO}yf^>Qy5bnN3zPa~pX;OU-LKXzA1iwNy(jOVm!cqguRi z{-oj!H@bsq!l8$7*Z>@Eis67{^})Cd6*@LOO*qU_zcV=8-37I%%qiA4*GHO&ucG%}V=N3?c~TAH4fmWaPa`pTySbi--%R+RfT(;8umLG1Et6J*prUS7~Jts<)7h<_9 z8Bc^{M2%_F2kmiNT_l(vc8I2r`0^2mVDw#bCW%*~=or&1jCuDCeCs{Bj9cVxBXVZ} z=|s-5<$doxG~P$eaNZC(M}TyoR`Vuq(euv2lAExBPTEK;;}h>UBTNA&&xn~}%X<;L zDu#i2mY{r&{(T-jZaErOtV_^Q*t%&>`|1sv)Bhi9-vJ+0vHgE$?%jJgo4O&{4FR$& z5WHlQKmyWxQ9(q5h>(iqNfEdq0Y#QzP()A+L=-GwLDWaFU`Nqsp@{-w#fAk7K6}F+ zf1h(^=I-4E^?m-Ie?G8h<~wK3%$(_Sr*JLJW6g~1%n(cMiSuiZD57bGm|HKQTAilb zOR-+3dFb1CVNk?0&7*2~niyr8GS-iY_?YxfqtjoMPik&O*`hzlqa|zlu|H ze4Ip?c*p;#C`x9 z12NW#i^RiPLL{z}k+}YEBC#*-GyI;O&d2hTW1ry*=Q!vo^85D5l9Igx4_G@e8BNZ) znq3%3{aJV{{Wg3YW(iIKpO?EZN2;&oWXR*uQ3@bK9*>@?05ZsOawh}mf^COp?Zw^- z`(SGyj$2L5ZldoGru7o0%CNU%_jRqr4hU=~mF%5qV6+Dq3^jokW?v^+UKT7n(A!F3 z-i_u}!u&j%R}1qlG+!_My+R)`Z=g-k*!kjfBv}!U6~Zr1-BV0~+SeePW`}iG3^GIv zRtTr%l%0F9_0Kki!Cqy6uW72i0K3cn@$&JL6g{<-Zu&heua!9DIeH{d!&h zIZ&Hv*sF1|IpE^7O8_E4`vx3ntFuYCpRs`B*iQr*YF|^R8^KA+IvA}v%1SXtB-cUk zkqbofVn_xe9wXE~26-ivG0CSH*27|<3CCMGcpU(rF0Vr@Bi}dvww4W?ahDCFWpFVZhAf z_BQ~L!W6?kIUO~e;t0zjcg_A*x{sn2*?h-6x27;?X6p8L=$>lWr_j=yeYwMsM+_eO zS;;Vx*5d4Ya7U6@FTgog5eI*LsXWK>0G(qINohpwWoe;~MvKuMcf#L@gY)m{EBfs%{XXRK%cWnR(PH#LziZL&Ps%U8Io%EY_LY7gcKOw%U%$~}^h3Y5 zpx>WeX$B@ZB1VhRA31JCj$cr^IJ<)1yh^Za#l4Mz@7|h`E^%*T2rS#e3ZmIJV_jK; zYXJCAE=~cTz;jB$lt??BYhK#=X?N4XphNQZZMZ%_GuN5Mw32r@U6!xm&HjdcCVu#f z1d{BrbPP@R>n=a?(&Z1`XVGHo^Nr9!e)Gb5Qo;dBxwvoS5xQ@rs}GFiHrJ$ZXp z?X^j?$^2J*+v9cveCx>}Xxg_5hu`8Ia!mVn;qZIBLu=E%Lpb~q?~rTScM6A=IENG` zKRDF%$z-@dgZdUt&L)nZuoZ_6HGP)i?u#gzpJZdov2X6W8_y(8}=R+pQBwvc!_?dpP(? zZ!D-EgaMm=GtlrD27Hp&Z?+hNoq&Nsq_CYU;3U5peas2wZVW~Mt?;7&27?xf$zV1W zxD`-1-Me6mI^A+Voo*??@cxYnX+#%`F^D9pDA39qiFp0q<*0K?HC`T6nCVtZl8BHu zI03JO@-|H^-KW}lJXhMC_6#6?{1v2FE=xGWd1J={889)!3b9{(P*k55kkd>U3 zQojLJA~pfq8-+PjZgN0#2AVeua~7KK5#|7z?-k}YXueOF)6jfBj>`(<8?U?pw`Vuz;D_QqS0&G50S#{hp8Zl`usus zfv}`Z!`_IaD~(|x_IsKN&o#w{5UU9f?hEeYTDb7 zHx(b3@cC0>F-|Eo{V5{GfmUgeJkuUSN6K<^F#KuuQy7u|pr5qhgjQ+(G~r@DjSE7r z9BSJtU1-stPKy#!rT24upwL@n(5})HQ^4N(?`TXtWVCp_`EHzZ;+^L5u>!Hz31A`< z_@eE$FuP*3}biH z9Xs@ml=x*kvs3ONJXB}|F1Nj;JtRFvzDaR5zDaRGWG;`^;$(G2oi69i`%jB7Mfph-oy&xIpa!2 zqbwDTQjEEVTPX1i6V+k0^gm7uPDB2h7I>Gpof=GM?l$7<-nu^M_gPD8Jt zt*4BF{i?K4NcL;;TM4H(r^P<{*YS)QGV}U3(1_9G^#{?2o_PJ6XvA3Y`nS-CLG${z z(TEY__3xk&Lzj1DT*m9)Md%XueLxqB#%ivti|D5EaVP-v#PRhI8eeM-TWNfaJHU+= zV<-+f4&fk2XAns?i*k%8GSt6Es}aOz+VA7%8CegW0DxZDPp31h4DU?_+9hJI*YHNi z4`|~on#HeuuZ9P;?GJH$x16r!ieh9Wo2%dWJ&A^L)+m3HQw}M;4GHPJlnNNVsoI!$)T7bk^aKmhjYk_C zg)E>4NANd5XQHvlAydH^!9@uVPHW+KX$Q{n@UEfWSO$m5GWa;YfX*A9KoUiZ5NEU) z!?8e)@PlEUhpD*8C2@veidXb8u1!4ePvIYUO(HQJ^{Zp8hEP*K4NU)axA_H4&o(aagP-TKXmCU-Z5?x zan7Yf{M_>4Qh`Vj-Sryh&-+24v%5JgHKw6WNtMZ?!5 z#J9>C0O(6|0F;E2fbVgMC_59VBhiYJ>)t|%qSwk{(%mTpm)>$k-m!ySsFc; zY9E!YqVW1tob6|{b>>>Y`UOq+5LdwZRmE)--q%g{4UxQA9XYg-5iIK91b+_pyKWYP zf{ge*g=z6js_#A}POcUwH>{#IDJE8Pi_OsSOOK1ka8!IEs<>KOaO0Hljz?U@`15y} zT;2LZR?totL#@@p*<+=Nw_;;;bP?6b7R>3ze^V!~6m@cuQzx&GP5+g$9xhV#BH+|R zac;OD2Y=8P32TPkQxoqjoq*+(M;FWMA=NDrnvGYlgfc3F6M7xMn&`-!!J}8i*Tl=* zuH*WEoL>KtqDda8Q3U^tGnxrsN;VXay$Yolh$+K5r|N!+k?6Ki8Nw>pxJ7dbV= zV~QHmC0(7@$1kGv4fuSeSG_1$*X?5G6YJoMH%eo(yijQb@PU!1U; z2z|+|5^{d9M0B}JrQVlFO7>D-_i`50S|_>kxx&e3FCJ~Xn)3NlG%h3K1~gtn#xNS2 z$hZuR%h4EYSdmyB)?DnAhpS}pS4+A~(rYAbl61MGE7bg(3>ZJWuC_n6(T0iJ!lEeA zHpy(^imJUFf4nik@-W(=Gdi7~EH$h$u_TM@o|b{5iF|J18mDD4h;+{97Ot4$$%rHok|dB6%yv1~zihNVOm}f=m+R9Q<--ovYTyH5ds1gD~Eq_V!Zz$QDeFbTdpl z)x-nH+-p6Ki$iHx)6z}wA+pUuGEDgp&P>BTLj&Kmk{zl%qTInGx1+t-CD*Uy9(1H+ zO1W=3NGq4z0r7J29kC%KH_J}O5)w>~gI6ar?|cTDzp?nsIT%_GcK%&Bd>^PSn~cv0pes+PZ|aQ&Dr$H=N?u*`5Y5 zA$dy3c1K9aC1fXer4MC{AQ=UCI@^)aUdi~;kr8&uXwVX5oJ2C(+N)hMIw%>xIWjuB zWL%Xf<7ARy;U{ZHMkgiXPe(?+OGaliLB=U0!)H%-#p47e!=ot~mPYHCZ=UH%E+_*E)m_8h=&{zJzOGAJSHBeQ9Ocnrb|dqCFBuDNH3R=-tKtdm}d;i z$id@zP8I5-L_Fn)=qszxlMd3)CA5#M-b&`_C^Kl!aLMeiWWMASmjP1deg_%ok~v6L zOPm|vHu5v*`^;eM{FCb*!(L53sWrr;TUh88mt=A4S+LmDOqzBCQ|Qidyfh^>6Ebz{ zY1|cgzJ!t{&S>Sy1aYZ~-!pR}U8>^O13go9uMlOQ!*seoi>e1Ye6W z2Dwg0tPx$TR-!AU?~FJWDWf?6MKg*4+h>^I3(_hXf<|znU1owWI@YA2KPJNDQH-yB zCc29cVB?5&g3p-girlo4Cve1%UNrkGsUy5A6~Fb3r8WNX6fQ|Sn~dan0%mulHBPKV zsK{VO%h9dEq}wDbv5*`R7nPsLkGb=`9dsTh?kTU9GOOZ45~J~IG!5ih!}Vemw@MC+ zu9uYTaJ~KEm*$9;C zW}xfadehX@OigQ=YMm#?;j}_kT;GrmMkB?!l-GIZLao(^5%@2!KOf8aS{mB^3yrsk zq3x}IGu63WYM1Ch9z`$q1tzX}GI{=gIslBLa&=ic-KLp4S6y`*^oeOqa%7}t*iYaM zQQRA}7Nz5-pD4z}>k=89m{dO#wK|zbp_Em@2@`NV=r+98c0vOZ!|e57!Q(tiu_n)& zUOx-HO~I@c4w~{zi4$!yE(V{4x)>j>yx( zbq2N_fq*>|v1dJ?2k=@O5w>aDSSm7cspBd<$VQi^l8n-KP#VU@%4QI0*g&oagG9!k#Ctva;giAWpyF(BF|dJ$&r^rWH`p6QXnMyirpnGwTp z1}D&t`SA`i2`jzQLU8QMiliW@*L;fx8mAE(^Rc#YFC`@Qca`!mK@3CMzdx% z7xO@3_4*`8Zw=1|fU|L4hRXv!gJ%25rf3;tL70ts5y{Bp)*KW2Ld~@1rqfGA7h%!) zRJ=sg&uJ}9wcxanF2kXj^u?41arfBj^6r85&~AWw&&PmsCdck>7ISU!*4zYKBO}#l zvj*U!M^qO?u@o)UTx{KSQjIL3I;o6Gm`<7$oI;E&--0JrD|G>A*2U>yOrWAnaaf1n z3OV>alwUe!zt5vW!!m_?3o%st5>ZE}EMO(oX7%LIhITITPd~243-hpo-4Sa6Lq*q|$bjbuc)$uUh^)&G@}LzYQx4Cu za>&%0x3*f7DVOJ3xnyd?+gNSL)Rwok+LEaqZ)de5Qy$N=^2iiQ;-RE~6%rO75BaS2 z!ko-Q$vIY7SWC17tMCN#~*T94lW~GI%H>Xmu7AKM(n> z6NEXFhcW{}%M!L$Jk-kSBFtGllw}nNb2blU2ZB~a*aAEhunL7a$U{M^NSJeYC@04% z7M9jL)H+~w6_#8c%C)))a~mFNW0eSVTOMi~v`U4g9S^k&THS>ukB9QC9>N^rp%7kM zjpp_|)ZXeT%wZl1TfKz20}pkudJA($9_ncI5#~-j)XC~A%=tW&Z}k)A&OFrF>MzVE z@X!g?0AaRx$g&0sa~B@!60imdO92lR_Hsb)X&E%*pqA4Fb0wO2DF_U$Cb>Wjb6V19WFT5!Bc-Jx>UEj2ETCTzjVO4uoabo zpOzZ2IxnQ*|7B>r{BMT;i{ywO+3hRDpp-0HaH72!?F$^cZeK|WM0r|pV!&R4?lYz5 zrHFP-k=*uG39_!Hh6mfp*>VjwlE!&-84XbykO<36A>Y0Rhw)uo37c@Tn{c^ykHhE0 zd0O{GQPPSt0|E@qT8}DlLG}$X`;9c&$;t%lCNbGTUemr= zbRKIk>4(KUXT-7doW2mQ5Ph;LJ!_rG4XL`6*TE%FYi(5D0?&y5&ajcj;J07_-TXI$ z?|Z~|hKVq7%NY6XZ>RY7w~NMwXc6L!7UT3|zWq58 zX9yQ~kA$YY_?8$FF241e!HJr+ zB^``4+-u#7A2FPAQ>eaUh;M?MSx0(t1CRFM zlOUzKnO71vLusWRGZZQbnxPYlGtE#@ajF@*wAgFX?&?zK)_#g*>C*vR9KfWg!Q3XA z29P3XM7K`P$sUO9vko&=2KL~0g$Jk%+<|4_K{Bq#A%9N&omigkLUXRm+}35zbD2Xy zw~Ah5ykE@Ax6rkLuDg)x&TJ=d zn6L9*B2D#P1B`};MfpY&sDIfDM#CeneN06FFp2i1*FQuX$Z>G|C=Hso((mKAT94q9 zXpKJL@_QcuoI~qxvF$Aj#2xEIOC`lOR`a@5XiGmb^ttU;Ki}sDKRL6pe$>ovt}jU$vcZZT#{-3co%DQzGYNYMNK}Dd;OHwqY9w>Ma4>r@626K{nx z3cKL7&D@kPed9$ML3|M-LekCHg%zc5oA`*kQFuWhH8Rem>micpP6|5y9<)wl5b^7F z1wJd>4fn;g;qq}{zlSbX2wG#1Med1F4+rCY1*NaiRM(8%IZ=9xiO<3tg~fqZMkJS; zbcWw=&^-h}>vR-k1SjC53TEthf9V_az&rUe$rm1Y_nUM|KWLqSzRV!rlwz8(v+$*F zh^xi=zTJWs9Jy{68$$C>06(7nU;qOcz41kV^mZZxIG^;5twN5y8C!{as9(kX4EIvM zYSK=z0O>pvtoyK${!nzRqnwUaNXR{&gcRV{ zgrJl_Sxnp30+fkL#{0*Y5egnxh==<yGEO_5teK@d&Ew(0iFR8&7iRiRaxE;B2ElwhFSL87 z{zw`a^Zr7lX5Wu7JD#Jx#l#(!Ui$%D-z#bB4cNOe{3LI{eh>$?{v_qUKrhS@Ma-ZQ zCdIAA(WI$S;^kWPBso`b1&Cu~y8UA0aln~eD5_f_KaQ(Zi7Wl$S*ak+OL{Ya3viq| zj>f6HVGj*Wu>{!<;rEs2)p3p8bX?<19IQS}-&fGZ8jFWSSb>4_N?aMO>y0CgM{vmU zf+H!1$|C8gs8IA0MiiZM0`xtl{tQ1}AsHJ2nD$17?<>2#3(cnTtQ@1oI18ikC@yH8 zN&TneGbuRHeoQogxxB^rPddI!i+tmd&uq(EjHeNM9I>+q3k7+L@hpN*AUIxtS-iz~ z4#8~*o=spLZ!w-h>`BBXU^k6A#Qj^7sXfNn>J(ca*G0XIbTLHCQw&igGBsUv&Zajw z(W52VPoZwGgcza}b=xP)!7u=r!ogqH{}S4CjJb(Bu9|3h4BiQT1PKTeP9 z;t1NX|0Box;!de)qziPhpT;at=Quw@wTrHRM<1f;l*3WwI|iyyqMyOc%ny{bH|cT# zJI$+xEwL2av+;m=7DYc$ze1;%gRz_U+WEL%ETjh~G$8TPyzma(*N(ba9Mc8v%Z=AX zATcDXr&m36TzCH^P;1aVd)8t{d9SC&D~~+=qLi1cF4aQii3@@Zdi~k3_v>JoIk6Xc zxha$-zh^K8FdgIjbS%Eq3LO(+9MsF_gA+0_=bD>=7FZMFhbyg7oB*t|el@{JuKS2m zhPUn|q|BsLq}2WJ_YA2bnO>PpF=i8H9R3*eO0!{SGcde)?nj6bNeX4SwBVIRCP`@{ zk4K-8pT?e*^f^hlOS(hSovwKa?yV7Xz`Joik0N;ev-FK8HXTGky#6`*K19atXhdYOU$G7+PHaU*vc@!S_`3J&$V*?^2%Z#5_4f z#$9N9PrVhR!#NCK2HjVHPgdI*2F~To&U9TiwqZ#I7=?ZVx6Yyf!`@AcYFP!!{ya6v z)Y+SvrZaBpv!6yr!G7?$_hI zd`(`A*`ok2Z#YJlF&nR?vA#?f2l`Qh-y=D`MUE`Bx5CE&W|F?e^(=T9lTTUj?k1^h z%p$|y<0xC$Ux+CizkrgmJ$_H9w8r z4<)XS5-Bz8Q5+0<3#@3OX7|PWmr%(P$7am>4z~q+NrE>{0;$B9jgNDr@+9l4^q5*v ztHeV}sc%ou>5^XXJE$$zzS?Vc2aKpaTg-=b4~bNp48&zwSeEK>Ci-#~^-~uBK&|lK zPP5z5^;Cmt0ZsQMhHA(kaKO(zVM!kS$+V1PQ z_Gh`iLCg|ihPi#GCl)4M*{q4ju2r~#Lxg)kmIooD&XM6tw@D{GkC72e_<^i~*EQXG zMr{>{ZPloIT!UKa5G>H$*6f0SYa_-F*mk@xTA`oN{6RuHG9_8;6Tlaa0qSXQ!soQf zU9(!~I&-7pFfFGwe1ycbU&Qw1W7+T>mh>Y@2^Z`jR3U{JJlgES|R_ ziFn<*uk?2D#udILFbBu;`*0AdS+Aka11XUaruU}QBuXTP87jgzb*PdBN|WhQ%RSO5 z)39DZou=cIH>n;nz_9nLbsw{7YU#sbU6s>a>|)RGujzCXJ`yM61R9@bk|#0e%DL9? ziRhU=6$ej`NX>pC>8Fwsj$x~PXF^Fi-A~l$_MxIoPI?t+i;fc=;S%ibc4{r0g7^-e zJWTc(_3bZVK7TIq6TfwIRNn`q=5sinDCz(gT&_Pcz3%zErxFX;?)WEYv$lmB603Baq@HKpmK~}_Z%KYHIhQRC-Z=i>Wj1r z)O#{$s!6J7z#oH0zd|#nj2`1o$q-Kz%B=Y&mNnnWFyr&)n`86Fukz-bzs#HaoV-Dv zWFGxqZk!7Ir?5?|Fc^31SkZ0e<=h?TeJi|9M<~r#YdG=jgo_*uw zS(?lF+0hRfWZ zp1T&T=W6ueOn#Bp8NW8QV8j+`BsYX||NQIGW31#bk5h6q+%nS{O9X;yeH}*;myiw+ zrB&Eq@!5d_o-OPYO;T>B8;`LA*M+jP@6x09XEz)s=tqoVbhp!&Judk3#cB%s_ou z=AQ>QYhgeJkD6%4NEc2`m>Q?UWJiZxqz=*zZlww)U_PEMwsk-7$3|3A=hJc%4j3yrOq3bYkPVDPk z&{ww}pj!(w03z~S*$VtV0N_vORyodUGJ!62Ej&A&TNB+tXU)YzSGvX+&}zNH!HBd^ z2RlJ9q@kUtA#GI+ zq43DbUXQh?zqHUuT?Csgg2fG}C=)Najs<+!{cM6tvIzTCT%sIM_vfE~()({>ujGl) z;cW4Vio<3I?^SyxqwV5r9$Bg0;aQIcM`xnm<;B%IN(1#hBW1lqwKjxQ{W@LLud$eU z9pdX(oo^o0+OOe?x=$-8t{;y)`JQ9@p_~?`jKRO|dMuCXdK?BG?cxDFdL9E^LwwG;)6;aWpCDo(gLprJ&hD%wrb z5?9|=qV^nF=V!!jo`~7Rw*%{6I4FJ{xA;t$LCf(4#r0EAW2J75wGal@L3OYrkFdMZvXn%Ftiz4SRIR$@M$D1U%U{v4P50mqRqN<%-2eg9)p zc6|ARUGnF;zL-8(4fJ z-pR!wZ~KmG(WhWI2|W1G;5pSd(Bd<4#P;|ikdo<#X%be9qJU-5nm65QPJcNIU{ z-NK_MACnR13VjCb`BV}{V%6~^k+v42wnn+N)zi&fP+Y$hIYzrV`lhp%z+JeuggfjC zitCpl%b57qL{Hc1xBKwoVKQ!r8TZ7Dhm#y*Rt#MkGp5CicgBo2#f-1VB;?1?mt&|E zL%)xqzs8KqV+>!#P;U%Px68dvvDXo)D$i}idJg$GPzx<(}2_4FjKJ3P~GVahgA$XXFK+pJc@E@3(mBX z4K&)E8Qo8k0%n`NJeNo>F$gGcMojM1jD{($KJ9!8DNNoX8? z5V`Rfx?LRCIAA=2avxthvmlx3Ryrq>^%iDJas3nMZ(f4Twzy_is@d(5!LUz6ODz8y zV)=Ke=z!wq=jbly=Q(nI{*0QRqucT3=bY}Nzl`*SJjp!T=)w8<2o0}B!n<2>tqtF> z@x8lJOx2OkYt2cfW%JfNsbPW0NyLM`L$wae#mQprt#B;v7J8fZE>n5?gBT(fpO1Fy z9kqc(NXk)?XtGYJ7JTpGcg`0?s>EddKk@4;4?Y;1tH=Dc1S-|e5iB_i;!Dl~*E|Ks zOP4#k5M}4mxU$pWl%1z!*+F$RG^+Bl1ItSRk6wmnnp(7inLOBlW9?U5VT6^S> zCy0VdCHMmR%j2__FyJxSug1xKRmom_JlTugvIA1~Vz=z2DEkqoqFhNW2mWSR&nDwn z5iYFuZOLFXEWtoZ`TQkHZox?|%d&G(|H_#{7|XMSOu|~Suf*U=g~}2j}9B zRq3U%Dt)yx=SNVqA=ysdM$Bi>if=YCP$PI%d{HJV`orC2oGr7FztS|1^A<{ZiC4vMLCQCuoYSpQPh;Y%7!dFe& zHSy(utg1a*?C4IEgQmE0u*_-Cj>z^5)!TqSR3^TOFB8|s%EWc9G7;;8kRf4>_(#S7#$*=`TSL?QB37Y z4J$>yUF+&dNPytN=C5e4h?>Zj?^U)q`7(EItS^aqZ3$rW>?nR^yy90T^d(E2Gzr~b z7pMDc9o>H?b&ttzxL)c0mkjrmE}c=k%A>1k(iJ~6xFMD=Hyt-$Za6kyz%TRVhQG`g zTo_qOX+WN29=$o1FQWp!L(_S-2-+LH4fACn=F1wHFX#{{EN4CFjgx}=T^{&{)v&H$9@GkWnT{9P;$EbJ;`%mzFdA=5 z7`~z)y2W4}2B>`3!23i>!n|JG(T0EN;sVrAZE-suy^G?0H^%*nRE*pE zxRtLmm9}9V9SU#r2&q?0>t%#zFfDh;B?=tim^+@k2>{0XlC^a1Nw_ z(ORr!p)uFBmQ}6US$K8V{QEG&#^Ht5v@B9w-wh?ba9nnJk0e&MDVm&a zHFu?Wl)~{EarT5>7GPG zw-2LcoNK=uKBDVP8a{U2i-M2!T}|sR^YI=J9z~v(0N!U}e}Y>YGg&%6z7x&Dd`3|p z$M45~?f2v3`)!rZ=6^~#`KiiD9~Q17CioihrW99kr@KyE#of_Ei1dJG)ik@iru2X( z*yxOxq1&Iy^Oa)HGR`Tv#~P0dU%J>#Q;z1kf%BkN-Z+f|<@ha&Ru%Hl>l)TMXw+OA zJ9pqtqZH669rpw40NDmW8z$VUxS-hSAjLii^J1dBLmb;( zfKN9GSA0`t#!7&Xeniu{>y&Bg^~!W>GXVP*4+Na?vq*sf`PTw8tVPoq*8^O;4NdQ@ zRHnDDQ>GRNYjjL&9n-T8-;QQwx_N~%4ci7VBZ+|l&#hFZ+-86~hob4-6)JukuLpSR zJ~TCFGij=8R@k>8fH#Wqw9MQN0KGoO0}e^|11$O#UCZwSm}}vs)h|y0*fs_Ex=sbS z%tBM<9%0IaPbUf2G+Cqo$gcq^Lj8^YkBTuk|=nlNR;oAX4uGT^gXAukiY{YGG! zFuGWop14n$zLKs6^zNhLH*GM32Y;eEc%i60K8RMU^0Lh-vD@wk_`<*#oV!n@=)c{S z{`%i5^4AA9e<=JK@UG1LG??L}2)OcbSCgu%Pi$AFi93{Oi-WDLRi+1}YZ}#VA#c;Y z01FDxbm%;#-2+aZr_TdemxehLTnVs)%H+!HR32R^^T>c(o>QiAvQ8VY(kbKrnxtG` z{7|_z%krEC{%$HoM!pJnZk~$Y80ne@_m?P>;h5$)rX`N)S;ti4_}%1~c&ReI>F|B- zV9z>yUpuC;j%!<)TWQeGaV>C6S30Jf9aDzmW29s1;K(a?Vi1ugAG}iKw3B@qEZl^q zkM2=<po@5a7<4)d`}Nz(zNP%g*}|fWJ&zO3GMC+GUu@+aYR2tMd z>Bw_TeVugRbs!i0gr>pgirjj_<#&yfwqZ_sdr6Z4BV=pggX?4r(%@A~)llYyG1QUr zo)d!?hcGxj2l*}>!l-nn!Sc-@vEY-U#QI>;WYsdXYOh-K3%96}ctS{-N}YbNUnhkb zGJgA=6dAC3xpHNBs!!>6hiZqm%XAo!e!XgUHKz~oJN@9fn^pd{-3ss^wZ%8?0xG66 z!D=n0)24d?K6;dN)mikvY4G`efc-CGYddk9YHe#|`{IKuGDZK91{XNx>LSNl)SB|vO;F?{kyc}_I=KA)Ga>fV0$gzMA`WG@Xz2}O352w78H3J;g zG534O@tOhFD&^YEDRm>)srGMFDZm4i(~F$3%F9ETbp84n#aA0velNULnRb04{2Gux z8DQ%!)Jqrabq$O`N?V~x1D5A2)BaA%RNh{h-h4*MTjglCha=_0sVWBhWUFkzFYT4- zv1gT!)C$#G-Iu50GD%AD!2n0zj`pI?oTt+=Eg;`W$MwoxVq9Xt;aw_>wY^m>tC-^S zI0KlhtCO7`XY!NE?=4Nr^qY*I0h9bpj-^hKQhe}@SB*jXNWL@}@v|!TN92gYfIA)6 zOS-DIbC*}NQU<)XMvYp=IK8g10{{=M8*s~4DhAu{7ComA?@Ez1>JzVO3D<8E<24_w zlCko^Z%z!pa7Nc(Il1t-Q|fMVO!qh%Ds@Z~4y*Kzd0)xv;OM2v>52C}t4tnejM-_s z^82dOAMKDmi4Q8AG2FX%JGpS1Fzx62o)r1JpPSNTz|!B8UdF%Zw4xD}zh^n6>2w*I z4=#97g?lL;YonI%<4aYGIn1ltoTN>vr8(^6)?laSe2pp7D^80v%NgD5bg;Xex{xW` zL<4@DB-$n)+$~3QKDbhvG9l}EH74w(F=={WkT9jefBTA-CJpkOUh*y3JNw|{CY81p zr^mi|r*i%2M&)|Y>Fr;0dcPc}HGADLwRBacryT4{i5YO6GYU84NP+Gp?JOt6K(lX_ z0WYio2zVHjIY@sw3q=H{wHa_#a}3c4dC-OEgdCM^YZ=|shsa0=(QsbOGGLN}=#WIn zE!T@wmw^X-fcs67o&ngC%b*YpNQH%v0kvNVB!eYGPvYrct$4Dbf_PqP5=b+&g<9gd ztB6ToVOU7W=#WBAfMtZ3E0yG8*hEO1kCdFA@Dw2nKNd(2=nK!&?ETCa6i+`mKs*mR zJVW6FNMafA(RP7sg%R*2@$7h7g;fT4j5Gs=T%?dnC?e$QrxY>;&L(7Xr9$Sz7KV~P z9VAk*6|R82#Pj6`iswo=K*)6`sFbdNpV>l|0iC@9X@=F1#BYZT7$)Nkuo}_`sffmq zR)qZgMGVOzWXKCKB!`gxQmcrxCPb53MWhWOm){*j+7aT-jQI)?;(I*C6Bd%ci6I>c zv0Xf!2$?@Q5$S5hBzG36_zxr?8Own6GOP@C zH&k*wtNi~EaD3pK)bJ1E!YBA za4ch6epAQ`u#6CYk}7q3VHw4_v_{476+~DDEc#sedJnb`&m&ce=L2|^W9>Gjo}1yn z@H6rJ=+u-i;CBk^TT_Llu^Tn4DUZt1vK2ITlZL*2lX1>q9(Fq+r#_~T6m~x$C&<#0 z!CJ8w33kP5We!+gKPy!o_3Ak z+0C}I73AyeF9njrce3?_vhIvgy{#YuICG0!a#>7;lOZ_Zi-?MJS zbCyKf@*miVg!sNvl7C=B2wCrhb(Ebd5E<5V{u3)RF)v;#R(bI=t0A67DR~Y1mCYh# zv!jjQSOXysNv%%jE$mtowUKeT(gx$}i06jSRi5g6ok@Mcivj`g@JCG6J_BYxE08al zhd)X@OB^19ZzJTbk7CG^gghLIA^#yHyJHM_kwSk}#&8WY_?r~N9hxc|CjW?dzIF8D z;~$ZpyGkAIW@-FeLe@KS()bVL>piKTPCT9eOg!6~R9lddS z4wFdo$4WoLc^)BGIOTZ+??}jIr)-Si#e{U1VRhmo_=$vccJh4$A4;KTJ1ISdk0a#f zD^z?=Lj*El+4U;)DDh-Kno~9!cm?tNS0azG1$+u2Y40mvm+|Q&dAJkSV!nfT<~Ult zS|YDGZNf6Xn~*+E`MHK4@S+aiacatP{xKn^J3K3RYLb^_K>Z$oMYm#EZRSBj?)^|9 zD|x#l9E;}L0%?ZpcqihS^n~JB%?pxn@b|-|0?B7L@@~ZQ#0BIG|GDdcs2Z4#F7HIfJ5AYVnu=8s~?YVvhYS0(u%zk!fA%XOVANYPkp1M}}8VpDI%Y@u1B~O5# z`RjyaTqKZ6_?5p)$c0Y{WD@+&4-+!799;)AT+jE{1&c`Zwn`F$s1d>HgdjvGdR>AL zy)3Kq)q9C95#5U3m*|8QL@yD`>a4!X?%(f!&O7(cotZOlX5P%5nKv_^tIL5>qQJ!_ zJFhVed*K}f_Iidr=%R1E;OCsx>OJM#UM!60ylRzS#aEh!#N6^tmx{r*o__nG&hh+L zSt-p)WxO`M#_#$EZI{lWjbNt_vYb4iXUSRu;TGYecpGxM(E@t>Ty`0n#Io<&^Bxw? zym2!uuX*zAK#X7q5!;meV4>ZBW5Za&m5npBs+7v4!iR@b%+5H?nA(+)(`~r!dtJez zkD|jEqZYWUNhad#mdNUlcJ@(FQ&0pb=s$U_-`7sCtGZj{TlNtAMFn8uV+-s%40BP} zX5XXWlT(5Pa;2sgH7yN6?k5QeGgOaHP!B_2_EjuO$O@chNp+7l(gSBV-R!r=-M@e5 zvF*@#^_By(uQ{&yMiQtta@TYZrl+IMUOhe|`oMqXtx8R1e!R?ZJs97R((d?aZYlA9 zWndBRZ%htOZM3s`nj9FH=FF^k@8ds+dov2zWt%B5JyuJ~4=r{xgDE_82u0Ti=-WAT z(|Z=J;q4*()c~sLCbg<(-~I$dniZSN59d~Bav6OTOZ$=G*rt51CY~LLDuPhFc1=De z9^4nokDQ`%lS%hHggC)DK5-WWoTR4w$X5sEim9Jyx-*^j8jl|Md$lMYPGUKb+YXV{ zbE3Kroff_&OE-ZO2ibInS3X#iCu+HB41mL5#rk#sB5dU#Zu+4hEgtDn7A~*8Kz{&G zoRxW=-0c$Q^NbS}Tn>>bGN74y;{Rn`$2EXF^Oun<{%~ouYKs$vZryGC?$`sTDA&bO zWL0OCV!;$W&D5#hdKFW_@63lMfo{DGrh!_{Tq7nz1Og@rsfE*-j}gt|_k9V~m=SSy z(Dz`W=3w#@$G7paSa4QzX7WCR?eIawH&(o>xo653A9MBUX1W4$j0_t9mVf&l+YrAljnIqNP@ zzS|D&$dC#HKh+kyRWwqwlOIqe#~7F<$KIWMWGbY+qdxHXhnA^A3I{Q98UgW)yXcE_ z=gu}G^rd5JEj83utV00~QQfi0VwUu3V%gWsv7W`sc$#_}>+J%Y_R$<2*alHOTvI)uV>}kUz*J2T7B5Iu6=Lb&ZeIk$W8EfDQ z!;@WqIY`O;ty}^AX~_5t(75mis5Ir6b`px{GO`hjOMI|$zsbq+ckPT} z>H$94>2sk6Z$1BklyI5J2cl;V<$s{hkbi?i{GH~T&8k}-mUI!leI-g!<0D<3V$ z)VfKQpY0}}&g{Nhv7Bp6{U~ULT8@}}wNy+6dMjF1S*j1aQq^l&z8S7a96X$wdvsFN z*PyeOoXEY~eu?j(bL0=hzL(m%8Dr*tQt0}pB@HIxf8UtFX9u)}W3_aas8OpdECJ_S zM=}GMlczagK|?Jz4h-ZZWV36*KH|JH984yRFxx%`!QhI+#?jy;tQ2PhYXQS~%Y)~h zoNkXrd(lls(5&P_BVM~gEW1;1y;+}IRMuJF&WG28T0*+REJGY_yM<3U-1Z7fc{WCI zp7*NlXxH+avjAamkT$v#1}*%%M|j7J*=BjD&5-zPKpAE{?Z1#IPWh%b8&lNZw6-#8-2!2Z-I_T+5AiCgL#;c>XkiEW;*CRG` z`kuh!2TJEe+~|7{zu7;3P$-z4s~nX`?sN>wShr=G=!Vz0=YSbFFIjbl=?WOga<+~I zE;{piISC8WLF4YFD2}%ldukTD{)Nc}TCN|qBo&7vwc2aXA*#dvQHq9v!QB0*MivP_ z?C`x=p$m^tJ7;H^C}0YuU_UA;t6?^6D@l9qMFHSno5R?3Vc&!c_)f0BW47hX>aKqdt(KS{JvL$9Jw;)!*em~B=2KqsZ z+c9vASCp@0`18$t>qF*{oy~mwmaj{j`A(Kc+oB}4gZSx80A?s*xe&FIjvUG97*l*8 zqm_CaNfQemeU_MC!0VoqllsB2xneoCiB-J1`aFde8a#=W3cMR~>4i;jFktW+3NgGn7t zbqL%Fs3zM=h!p#Tp?!59Y2_7E7}?X#s7p3j$_^AS(Fqt0Vz$>hlVRs!vs!OH5O4>I zJTOd`7kVr9a$6Vgj(pHTKz+7d5a zyK9(T$W56zTrx-mifg}pG77CRn}?bR?Sn!1!j&b}=!%VDxjSf#&=#tdJfrYGw7t7w zOmBZ|{UD1ymKsA|bRXwOTBFbcnfoUdlMR}Yqvu4~W;(NeIq{aQFT!{3-(!`;-nr zsI?EK`=x%8l`o`$ch;!RZ9C0pVNm!|eM_u)10G3~SQEy7AZ<1rYB zzV;%KZ%kDflMfkGCs6?t2vpj++eI*vS_Nbk!4>f4^hFsVk9wMT-JbO9s*@;P<5ySM zxue`emiU<DtOpMRd>lwKQmUl?Fvcb{A!20Z7@Kj{Bv zsEbeVg^G0GQ5j~+8@;_uBK2#jCnD#0u$%HS?O3=|byEOfIsCbdxrOqO;@J8r;oXxE zZN8*6EX0Dz8OpaPeD%G)`gh{k{WVnU0I1g#OO*ElAh>3q%3Dks8o9w-ZgV^mna$4j z0ZW;zTdK565>m~_*6xbuXjIx4&4MRo#J2RQX$kMWS!tg*3toUhh2OTYOL%#UP3JiR zGbP1${bvH2B1BCq!f0kNJ6r*Y^P~3td9yYoHh)y8bB?%^-94zT%wCu7g_3#91$dwB9@aPR9iw{Qd zG^&2qZQF%>uN1zWrS7AihWx9p6lP@%^AQQowvg>cH~0Vy4HMiz=R_Oc9+T|cL&iyxB4X4Tj(&unZ#Qma*9Qco#}j{m8UyOdPE1@PAfHG ztC)znmikAnN)S@jNKzBG)Qp!l^vrWpVVDR zuIDFb7-fy9;I%x4o5*(mXr%pnQ;AO43*bl?@T?X9mgNaD7B!vq%u9YBuw34K zv#3iwA%(1AKh3N&sS&90=-Tt0=~Ra`ZpV(kFdrcAm0ZS2Z6>yc@TUwerbs1?*uLe# zbxgXivoDfbF9`LA3zt^K)W}SbtTX<1;WysGAArk8n9Zic5h^Chtv^jkEf7X?vF^*A zfWCW~uJGpkYOr#gR~re`{Uvdq?O05BqbY-e4o>a<(QtcAJ00G5AQNUI%VpkfJ}-hx zwrNw?%{n7zI@}Wl3Gk>zzYS>@t)^)n7&~_y8N^%5bI~()Wk4b3+D>z_t0ix*~~72jw~S z*gH#qZ^qgkp6f^r?SXT+a3`8m{++k)qnBOc+>473U+Qxlq-J{4KoI#) zx2Ika6bkKRNlgiZBmpaZbvJ+%!5Ltp9D4_Ym9=>U(xJ)V5qId5YKV@AA_TewiMXZ}R$+^9j4}QlO|Kn%yf_q5V7hzhp8w1fi1|$Tv zs)yG;E9`gsB!pcYV3lXo*jbuS1N{@Nv|%5G~q! zqwLANwnkmjySl=BE0|_oVVV`3qc_+(tm{>;EKjejP;anPSddOwkW*OK^WJXJ-e9GK zB4=lt4b(pc!0ew0YXz$C4(Ffl8Q-cr8C@#K`hp|@1?6OYY?1(<8R6D5){r}Kn*zVO zsNVn9@L6l`5n!sAbu6eyJtAqx_;m$fYSB=k+nTIHW5M6ROpR|jmW02xK|irtiA4)e zfZ(@kNg~fz3-YWGYIR_qdC$lD#YBN+M1fgvOA2VUAMK^B@I+dxKtp=d1|_Iw`>z+j zrZTq|$!-1%IJ_F9nX<5m-*c5&=c2Ug|5otu5AofXed}y~c#<+{Y`_(*4hchH#37)T zk#jeJ*>~g0Yv!9R%B18@&^fO`w!pIba$r%LmB;E9P4r5am6U6sVN?zMO+pt`6Gw~K zD>+J9aTlHVu>@X)2cS7dnq?E$%(0gWOl zz7sx;f{u=Yyp(P!AKR+4!(m|My#^dQhl@U3@Gj|(QD z2)1)OTG1&tx^w6sz=vqb!?M82_D+y97Kp)m01F|BLsF$cnT6vz_tBVz`-|Vx1s0;f zSnIgmBm1i#RDI2OQP6+hfxKT?`(*pVKkjU#On=1>;LAcVlibq&ja;b5{vDVEJ$y~m zQ`b=18ElqqMmn#Dh|J~eIXXAvWvwg=`a*~~H_N6YVyyyOlIkRdPMa z>^&we7`X@cZDP4{W>gv@YwyyZv=}R@*f)};`>VV0L=Z}X5zMWYkl7(OBe&w6d#6q9 z^=GH_8gGuk?DaHpE8E|n4T3#)Az{G}q8wI2Rq+8zfAlYJWT9+HfSse!i*kyAKth4Z z(!o#}0`oc$qdIZ>~$fUkXcugYuD`^U4iW2Gzs7%_$m~C?)phXm4C6&~QC}iS9lu1R+ z{Bo^bIFwFp02_o%HqyJBq!&}?b15>RJE4H8RYC{SMf8V(1Z$taJ^R#|V{4=i5-Xf# zf?hYwSFe+c|8-oq@2E_t)|_9@j_?Q)n(>~*9ZE>aC=TgFFemYsL)(u@-+99;H4+Gi zH+WmM&4PeePbMRCRzjYJ3f@-yQz~?mECj8Ic9UPrbO@0e620od8upNUK+MWZcM-q4 zYsZ%+G>_S*+^6(f-z(_(A7hmfgoIY7fN(d)$7=u|1JH@>U_)riG^^n(| zUO^rOFNDvWQc=2tzZ|ygoB|wa`X{>@5-)|n96AR z(wiRzes_!v$LpBi*No)n zgl|Yil+OrD!WnB26PEs+y$Xa4gzVc+kKWw@cX#-{)P7%+##WWlzw2^hR-1-=feE~j zNC~#ontq-h{NI#N%4O<{FSTB_AKSN74^%97c3Py*&$fJIWGqazlb&}7w-F~lME=zM zQlPg1^^raN>Z2a&-Q6}>@lYu?enS!PINZ$fLyWagG6iQOqb#L|wF76AP`H_zV1M0< z;mF`fMk=DVx~J5y*^!F*U#~kyPSK{x6f1LtP?DXC>U}qCC2e0DE_aP(CG>iwbd;?F zd;XVfvZ#ZC5K8dD7blmM8$jL4ZikSokCR;u3*P@xX%1U5rw+e|!F`+j$*3!RXo2iu zZ+re0z`=1*$ui5MgDs$4xci~r5{!oAY_DR-S~0Nwq5Nf6*!Az>%y#pqu&+OReW2E; z!70K9-|~uDY6fbDR-QsRGKbyPDq=CDq1A3cr_x-cYyrvD$@60^cZH6H$czF&LO2s& zQ{(07R|~;g26BPJd%Q#wp(yPydw17sw~y$R#{Pz#-`HPT0jQ%f_H(wHGD5oVKglrw z*uu>pRP;~2n2(IOk%w7D8D^b*zQhtS((eD9AGpRM+exoTJL>9qmGqBu=M0AahAx~2 z&rBPCNi5qD{DM`VuKKw9v$LS5)rx^tCggdqp#DB*3v3DQ8D{Q&PDD@KajXKgD$Rq1 zPb4`XoHr`CSq)9%rZvigO^VgKYrW6xCV!7yui%*} z)iSk@HPYj+zq=lins8b1?A9&Puh>$gN31{5jZ}s~yoKStY=5MN8>mG;d1BK3RRZ`= z@}+GQ|M|=vQI-RUw2}W%D371=B{}-%v$BFu7wLKud4*gV(fD_9;V0uUqywTG^lFeE?zc^uc0FEp*x zISGFvEZtAR!bd>i)KM7iSM1D`U+C76i>a7nID&Oa^*753eR5=?g4o|;eZN7dMk00d z&x1---K=C>!KyI~TJ3~uQLWY=t0&gNAVR}OVG#adSQutPpD6eNMh+zbmB^Zg?;T*- zOJG^kNM}xq2YxKAZ2uy?NI+ERpB;3_MyoE1s}nK#c#l5Bxm}=+(B%cM&`es=a?(`o z%TbDP3c^*S?;HQ(=y-&V#qp+mmX3v*Ct+0l$CMiStmr7kous=@IzwUyzVDkIKUA?r_6_}e(HV*=3PN!u@7tCK=RYL| zLJNv5u9}|+bInjFSjd}|%fcQjb|G=c>l^QdK%WT>qjyg`xo0SDRtXZL0@goR|Gkfr zcvEaKum8Z0b~kdJ?(8LkxK3Ee<(OoZ{Pbu3NBN36i49@S?eW)V%p=K@+3S}9pJEL4 zqF4lrJQ(||-By6yEk-Xihk|N^4+fF+)}Q<;cKxp}6Rj;6by>IRw<0HFJ+8Md?4?(h zyIt1CJib{n>Y=4U@$0%KQANU0(&LOJeT-Wcgu3xg3O6l1Ti2UM#B(>eyQ6>(9>hoVviRZ53*rl18?1T1ONJoj((n{uL?K5&9!k3NA}Fjc$EX zP15`-Kb)f>Zso7KXudNs1VqBPi?U^!1+w{Blk)w;(rI-S6N)myrC}Fv0`Nb_Szno`n0-RlBbplmRHHnoqQsC6 zY@1820Hal7;E?O4zvB?o$s13Qc&BTlJk5DjFG#?Gbf4uw-oGzewWFB5?fvWHF-IOL zMp?;4HbP2E#yk2jN=ufzSGs^TpgBG4z>ipBE@inLUG&Vkr&;EDjt)t9)p-zGJ6jNn_zV}pwBjQ}4&(=4S zzd?HMvfK|ePh8Xs)lR7)B#q=k(c+`g%MyOVP2Jq1Mk^fw!d_HD(MvCJNiUc><1+fh zPI58<-kqbpdRRgI~8{7G5?{$BG0n+wPS z8u`NG6JzDtAFPaw7WjL^Y4mv9KlyDMUO6`tJfMO-fSyOa7nCh{vDO2NFz%yS&q#57 z&AA*|vdi|>>{o5yL!)oZmCWoc7HpINRMebu*6tRjD#1PfFprj4lh1%uZy2bIKdten z_w6RdA*YVKWVBH2b4zbI;Me?`)fXe)nM>s9f?ILVk<6x{)O|rB(X98=e#25h`qT{z ze2Tukf1W2}nJ2eh;pfPb8pf&I?(jY&Y1}5JB3jQ5p&fem{rorrwCR>(0c1ZBaX?BL z;a*JdeSOuY)~V?h(rkC+m$Qn;J@TT@Ei+qy6uW7W`3p87 z164ffKGULH52|hrMRz0AeM7lx=6-<+9_uhUnbo%zvQ|A^!g}%%=_0~n^+0N8+)m@cNA}YqTbX!p>`||yPROun8mCS zPmf4JJO14#U;WNGO0KWNrpAM6H!~fxKv&>>aC=rcMdwOcNO2V9zG;R!D3-5LTuD)o zHOpK7=;y1+K`Mj)DbjZcuvw+~31{u+%lga+KvEES8F3`N2n)lb`AuOylFO`iIfz%U zUAGLEeV{V|K80uQ?S-2o!C&z&AIRnD7sTjeGIjcIXe}{`-$(CsiVG6uW)q9``{nm_ zu@9(Q@Rmi0s54OM3to@QABq6zc5~60OcMWJ2r1g@|;gC$8Rn(Ynp-WKGQa*?<4gKlT5Bki&noz~@^ z)k1cfuam9CT1k1NS1J)DLX!UM!n|Cx8Z3)9OV7CgVjpIc#fW#l;v(AsxW%gtn$@E|SOxYoR&!`q57$ZC z0PJ8^q111_{jKw$a>zs?se+=f{{5>6^QNz7*hM3Hq{t_ISp{a+C(w-9rrGoz&!b1T8Qw|BA=CeB(nE z!LLaRT=F;>^l+_d2|nXZM*ctuGi(qZk?tpt8=w0eT_4`(i; z$^2@kP1-bz^pQk%l~Arxh2K^?|IKUA_l2sbw0&$;Y~NxmazNU7am(dU&BWz}jgK1T z9B~x+BlWMEr?lMg8ql5#3+ED3Cfn1z_9V-bc{HK&h3F0Pr;aWspA!*T*|O02a72FpfI^ZNKj?Nowe z>5w1CLPie@ZE6aDcg+Nkx#d(W&r?4Ows#RYs0b=#udAeJh(v;FIo-$aJk7*9pLs}< zRKc6ad%M6MH~cAOAkv0E7l!x;Oil} zw=gMG_v(?ebPme-g`$*RCml(ma;;LqiK^cR+SfE~>=IH-H-RD^gri(i^RsYve$W-OoAQlsI10KRrtm;BqJ!X;t5lYr?)SPj{~-WNWTodyuYv_1XzO zf`t2>%;C$o+VVK~ifsh))Ow4JP3QZqj!(d|Qab+bern;N@3<&3^zD#A8_ux5e3BHO zG6ZK}8$pZI%fcCYk?njzz|>)L{cG7@2cMdwFSubH3+#*3cXLt12gQWE%Y81?Z1T@H z&A?W*0+!T8B9gBdc-g)29y?)qDuZ&9FfzX`TxB!gUl;w5Pl$o+>rQ-*N+O1EpP+rT zWuW(P)C=X|yl=ZNqw{bae93WzDW^bX@B6{S#5ZJACwlUof)aCYF!Ggs#``dtE{VwX4 z3D@jH-coVH1X6FRA;vBo85vHS1cR?_g{ZG z6J639`ke-NbF!;Kv2kN>Wd(DE~b{*V1 z@|$0ZpIiG~k;DXp_GnAaNjjA0;i&slc&IW?aiLu=q3~i4Y})JDq4=Y;tAy&KA)e38 zNedRz|5XM3x>$UEJ3ZtA%m2x}XfZ1x?=@4%yI^$F=N8gmE=Qx7i7sUKW=02%`^hD| zysy)^$060_)7kTL@h#KFfBr|W3Ltz+7Dr{Dzr$A8D{1c0Iw`edr6{T8#IO@3t?S)7 zV4YP&JlxZLtyVt%uekOY{Y{9Eaeh+V?#>Ho z3n3fUwppzlONNvU@ZhD2=DFC$Dc2Ss>Tk>~KdD*2?MdmQMQr8U%y7EOE)SZ(HHrIR ztxmTg)W32KKbNbx@~Zo5f~c~@mc8v6M10F$_R>9NF@AG8cD+4Y&N9D*s7*SIZO2Do z9Uv>pIoOMH_fhZ12G>h-eWB&Kfu_@3^(*FaT$KI{q~}QXBuerpzWw=U4;@i+$yv;YVn6(+s6EAq!dB7BGcvm84ca?~g| zYtn?qrx1?c>^<(Me0c=Z7S09v-uuPcBgL!iul4r1aUXQp*Y1xDb{qeXFY^>ua%VxQ zMAU8q*xGJA{isEcU_z5#$^Ouw&{yHVZuTgy{@<(4C)r;v=)duMuN{qjcDqbfn7d!H z3AXcRf`S9)lsuBu*|MhJ{mE`dz>U$h!qce25(~~WGt{eNQ`&hOkDH~Q9o{-qB%GRn z;VWaHDc0b5Z9Dw+1G6|Af%`VwRzc-nCkno=yFI*wC3i!6-T_2Z&9U!`dH7;*&ve*bm@x69+SJWqd46j5 zP$j1Cf=f=u!fT!~z&K8a!ViEBnPu$z<@aMWecEOzY1I+zF6SdIu&xd1Yk(^!4b-eH z9yO)jk~1^K26dcSctm}q4y@baZrq!?W=NZ>fOel-O{6$r_Ivr_8^<<8f4lw2l3lx=*d6vzpmF`}~RT`#BAoSIHnXb0HPuhi%`~g+@z8y!8WgGBsv5yLe1RrUZ^; z7Wz^g{4L{u;5C4R2GvQk4=!!0Jwi+@c?Tf`zpfQO8_M-m3(f=KFavsn*CD zpFk1>Q>_X6=|hx@S{l$I`5%QS{yH_!5GVVw0W}ecw zvmK1TNdp_CqF~G#Pk>L7xP_i-KNYGfmPzVtiRzTow`CbY=?8c>NAH(EpRQ_{MxKk? z2T=Ot-&U!^dY9igfl0?s_0lq)ASg2Dl-|vTbmHpv-7c(y-sD`e0O?$)}79Q0maLA>b3?`RnwAt@e2A$6ciu)-cc?Z?3~ zYJTRAgK5;KIupIj2C|qsrecSn-_a|S;v3`9Ce}U>x}?g1(dY%V`0Msp&g>3DbI03@ zF0m)XzNqIp7SZF+=A`(i_v#`WZzGri2rkHi&q=g6W2`Dh6y@leU+8Uu`Rx!qYyam> zF2*3so5^*{$ednwaiXyIx3>u`M(15ySGzznNB)+!!9-};N5mg{@bC)*oW1$1pjiNW zbyCRR?RJ;g_44iOea1F%#;FpPAG!B)SPA<>n#DU7!nhJsVb9J3YrnC%*$t7S?q>ER zr_;p%(Dwu9J#@AYv7o@77=p`XZ*Q)-7TOYMdjqe1GxC!gS8kW$!x9HAq&f zyIB_4?0q-bT)Q@3t8XT_w$lWrx4i-_HnXp_)C^s^?qL(=zN6nS)pz2!I!{?J2HYiM zdtT8181 z#C1jPZWWhzWZ{-~1S<*b`T=h$Sv@0>?yBpSyR_cq{}aJ{2}5QpSF9mBVJz2AwuhY`|2A0{-+3kC{xd(%9qQm&}yS^ zCWVZ{&-hVN_BS}MGx_GQPCoqBhcd}B@h`{56!vM_OiS{h4+GT?OE26Wra=|CV3&L! zA>fA;oZ!mq6%L>5{o+9|X;;eHCTn=MTVsx8xN(PLLnrGm5+~`FtmKVoCF4|&L{BK0 zWcTZckrwE|#o&9Nmy0fL0DJiFlU^m{xlFvwlOgJqJ>a0!faBiP2{_#5*U3~Naeww^ zsT>Q?HZu$BQtBZ)#{c{hQCPz%q_cDiwjB6h;8Jy7cvNNQ8Q9*Y$-Y5HO3#!H*W?KG z(UhF?$GQF0I&;6sJt4l#+`Xj+gKfu8W%5=yjP&j}`xXw28{|H|?$B7R_-Fk(kky&} z$o(Qd@B4LhR6`cBrVFidn4O$Cljhv^1Wg4sVi+|#ESC{7q_AN+Tp zN!ja$V97A=KYO-`n>}OODb>DtCUAfi;lE!O`W!4St#0@9&8L#HU);2ThQ)UF%y-(T<7(kbXgLjT&E|Jhly_^kGE*0yHk@W@Pxz z*09}t)hFhyZRGSg_M4aCvz2{qb+GuoPzRV5UfHJ}vpN{LhJfosyc$3gcK$nC=aQKR zS`s&IL-k@i4!8s3t=!_f^6ur;*R%1rhICQk8ACs?c1L`Ggr#^}e$ijop7f?wc^d+( zoJ&v>7mXY?yGmE=!RR^~ZyoGEl`}frrdu1zj55 zY=p`~npSZmLbf5_vj6+VGv@Q=*OTUjTpQ@A(gwoWRWwRSh8s(2bV|Ei+X#EQx>OQ-muI&>mp(^18 z0qTqT@(~CBx};K__G-6amX4$s*)EqysG&pKIY+E{rEGiQokqd;MeZ9WBTFWP_h|5y z^*kar4M;szD>YD!I{*`ow0E}`s-&FXIm(%e4|Kwh!2Wu_PORi^>|^g2ec+_B`3*Ul zl8XjWJ=e>GMZ}_(_|XwYz@~BqdZx6m@XE8&A1c>RuPJ_`2e)0KWQfHk=5tCL1lR;e5ChT`TyP6F*s_=xn&@QoY0k>eQ@vb8eI z&DJ33k~C^(u|@v|M@lcc5?=^3E;%vqU9pfH6;0=P4R%Z3QC;ZYaR9N~0^vVyMJ@)s zq_JB4*x_cD?uR#Gr(diGm)xq+0I}6z+sEK+^o4Y;(&^sC_>q8H6q}o`4HTU1G&lzQ zB{|&<#p;|>Z~ec6;f9o716x-d-;!2Mf~kr+!TP0o7dWlTY5%yqZ}$j20f+CB1!p{C z*-nW(cOzSfmqTeot7k*Pt&A#_xUQukh_hI?gbKox5NDW| zKt2>-^-5uT##4^|9=A`9Q98f9czOZEoDPWIJv!CJ*`@5A)xNkq60LgsN#FEv&3-m= zS*O&}x^Fm`?~Z(o11G9mi{r~x=obGUH`Ty?uQtQFHRs+2nY7)m?e7bCcJ49XfHNgm zy%XqiTob|O;0$o?hRK}C(!!SCJmsu!vn0Du_&`dFSMm~k#ABFLdiY??Zfv+SPZK`y zDpeyZZKq{k2&o4PX)thWkQ-^aw=iPk)}Ubr1-9Km=gm)e|HqAgODD5w||GvW2W zGx`WhD^0$HmJW(7K<@SNy;$=^^`+iMj*FZ>vg}^GQ%=-50)nh1M!XHK^)B+3RZ8XT z>13x0$}ezCRT>uG@7bP;;P>m6f(9BpOKN{?VZ&#nns-pQRm1Sc%sD2k#z3nt$Cq_d{vSLTdftkb%V%>dK2_{#n=B1vzx=dA{?7cBc*YeotVD zHuHvBR!eg3IqvIZETQEacBA-WHC8`ae<+0vP6%1}MJzp@5UzpMz&6;jUhOCCTz~BS z8*6*3ck+!MAilw9sp8XQ<;RnF`VRU11tas&=k7gQ&s+UtF}oEx9={7vL$DPZfyy~2 zj01l6TERJ@S<-87fG-X!u+AsR9f$BnKc)M%dpmCGAL0G)=z3_qLEb>xu-Fma!G=JA zwic@Z0v2A#Bg82uWE@=NlSAy)Rdt;bF2Na?dXvVmI%T0jCH=rF&hz*yQiL=#_%oG- zb@3VKYMJ28U>o}N3DUG@yH)Y_M6sd!-vsVeC+A}Rnz7>r*zb5L>gJ9AaR=2ESp96HuV<2hp`SmiT>aJlh5lF4InULA-5Ajj6deke@!wJL3EHl5 z)V4?j=?{G6fvE@2Dh}rIz`BQ7ffq>GxL?;{rd$~~IrNb{4SS%I7O zs&mTSt#Q?B?bWQ#_nUAYYJ&-tk8CDuC&;%^{Dx>Gm_GNs#pbr&gGyF!V`#un;vASI z`TFZ1OB}ihIkJmsuvyll(&Q$U^Eh~pG}sNww_(+2?3?lP7GYPcRo#=TzNPsi&<0g5m)mw_1< zfbX{#_Nq{9W2YPGtNx_PK%>yLk~4hi5bn=(R~?0WCxZLwny_K%l(M{x9XfxZO-SY| z<68KmBMtV_S)>0hZ|ouq<0( z@L5ncOCSm5l^17e@u|6`@&hlufJ}@1x}IrUG`12E#9FuG%4A z)*qpdjuD0nM<~Ns7(#-vmVGS*_|i9dY zZ<0+^oH$Qz4<+#dJrMWs0R}q=?aAAT004a85(Q4@ks6z`ea*I3Xjy#5j_w(_!oPi0 z8r@F_9isk=)447%1aSW(J;`e9`dY*IX^0vbIRia3cRvc!?Z!#OFgzV9z`ytXXW#vD z&pF&5BQ%bk@9o57IRyF%7oL>*2T`p9^{%DYhEX(8$J!$`@RTDs<$BdH+Xp=Af6*rp z4+MQr+EsDqw`&y23{@E*8pH_Go~YuPjz+uGmkUkbVQf5pT#K$0u3e7`Lg8rk31F~& z7F>Ig3MDj5X@{RB9A;#qv6=GmDDkiN2(J8M|G{dcz#2D8R7xkX&4t@!jcw%XaPTf> z2(RD`l|Dj_1Kr=xtL`Y)4Mxglu#v}9$nc#W(2-6jDW=8C%4klf6Np*lv}*9>1-=Omc<_2b zJUu!&YmJybFdEH!GSShKQfm#$f!N?3w_Q5QWWT(Zy{!InW1QZ9MAdDJfsIX5iUJi7NhRl>6DU?I`k z=bTqIdw(ghx8ohHrP2h1bI7&?Lrr`tN3i^;1S5y!#Nn$vF7f|yzW4`AIQmTabP!nx z#5Z_cg5fw>MTNL2zSQOcy7o;z;rxE9tAb`?2eRTG~P>QWS z$N_EUAx4*Ea!YvPde0#27>55z4G9i+oN=&4$L^#2fjOOPL2ns*^b6u8J#^YYOIk<8 z-t)MlCs`HuXXXUD3vVN1%LIb;{kaA!ZuID(KYmxk?9igQXBR;Rcf4^%i*|EK_sA`$ zB)Pw?b*S9hMsBhFeyd~dW+`N#9(Uy{Wp-yj7e7J7tGtJ@kw>C=EVB7PaftXjVQ}cJ zEypp3H5J_U>hn+5`$Hg){@N=hk0vJyf?a*Xw${u8ePU z@;*fALsoq2x~;u)jKAX)Ts_@bB;5pG-%P$qObtf3OT$3r{)3HmxIRL^Xk#WmzInkl z+lqFQ(YWZcRE6=Pd2CRE1%NHXtw0n;wILIaAwtj(a6x~F0QDyEV#^PosP2-Jw)R-T4zhy3R$~dKLCaT26f;7XG0{yOiA!%d1gpXi zAJWr&CI3ei^wEEezQTHZ#3_$dGpRiACtG{-gGZudEq%d1_FrZG#-w2KbLXfgh}?x3 zNGzZ64_d7ME#isN2C2#*Xxylt7FhEVih9M9KG36U=BXN57&S<2&UI0%Tw(LiI;hKP z!Pe}hHigeDI`_<2klXBBAR*n{Sh-bqDcZ+z=J*7BM?HioX!3%UpzR=X)_G>8Ve&K! zi>4FXC?HO-ZwBW?ap4E(A>d*=^#4T5yQ1m~5}&g-ZG=n3m*QJ*2jOluLj zb!x<}Y*nL&+PnB!*w0wq|5IMa6QP|C@>+W#$k)8ai;nj3^Ct`H8>EZ#Cyw4ZcTo*q zw6-~mN!+{n6Z=ES9RDjC|jf^EF+Fm5Tb@_=UqiX zRv2q(5CGh(eHVa60lwn5Fcxh4d(1;8s_itjS3PJ(>PjdNj5#n2B2T@tFHWy&xW#Eq zgeV84e6QNT#jdg3rQAXqz!_=8hwf+2A08X7vc0p+yEKSPX zBcZP4fk#`>jU*N+Y z(|Xxk*EZ7wzgQ?&EWQ<6hMx!n^K($%*79HU$x$;j`xfhmS5pIX1kE+N2A-UWiURo1 zgoBCkJ|Bjiz`Mj>&Sfrym!JJb&3!|!dN z^FLJ%L;yc>Vy4|oj1O?Sdqf;NZ4J@8OAHXn+n5DxlQWg9PFVqZ8d)MCX(Vbfzxs<) znSVaEGPHO1e*zBpj7>*$X6wf>#OuP8h^|ZI=0vL2iVpP{r9zohSk_(|KQuB>RZxP`cxI>K){v&_Q_J}8CHW|sDQ*=?$Cr{X0lQZSng`>Q36odIPbz2 zz0-7nuxlJsO%iogrr$=4$uvpez(K6Ojf>GINdPyucEZvwre_?ulT2^zU>rQK$_mRo z`XXa7lI|Fk@pAhdT5}pL2JV2Vzk#ytoTE{SiRu;Uq;j~gp-~5GThjkTZ|QG5NzQ4(1V07E_C?$7o(TA z@}r@)`7M%@JHDT7iqIN;DQ9dN%Y2>N$lDOOjM`uMZ5nii3&^;T6cf`ufV;mc-!9)= z9Eh#h8}BdaVy(f*E}OB)9Q0e^pui^;s_s}|*jmlXw}y$ibGDUc_Bpq>7tc*Tm6jRF ztA~q~XWKqj1GvLb-(Gm1J6HmqLIZXK@;$wE#^7WW{87MSXfPJZ37xQ5QH z-C=4=wp%$=ZqXQ3oE5zZSHkw1>6GF&C{5olsDdz?=u5L9^O26r zQ}*BQ3fwi$CmERi4S8IePCff@X39oEOzdR}P^|J!VIQ-mQ?Oz+P?@{!!5K<&c}((J z5ATP+{fgS8m^ooVCL4~o(Nu19AkqNxZ6Z}G$vq0i+ABti{;w<#k3-|T`(ri;x3^=! z+`||gZ)Cax@&{b;DyMWNzV`g>?IbIJyZHEjO89L|5>fq2xWl$64t(Qscf?xBc2MaP z2Nq>xb?oHR-Bu?Jwkbz?G@*_`MXsuhL3f0$4K925t|}U{h$9Zav}<>-uJym>Wi5qJZg!&bPiX(ioN?UIr~3|rc{P}{rn7j=WPNuC1Gn(o7-Ed z9ugq?Ma@zQZReCwYZlBVo89`Tgm5)z?fdtTN%{n}lEq$8-pUywG2WYo<{QfeH?Um+DLv)V+r|=;?5%5(pBf8B3y3*?X!zVl#K4-jO zqOmptbmMM*OsYEWHCAYPjSyTDvtGE5WLp*M86~63scZn*bT#L+V-|1gU&ohfvTw#8 zayWxbf=ZSIk2!`@*_yFUrRpi;u4pEWr^g(#DH?c$5iMuD?H>~^nbP%IAA9(+6rF#B zF%EU^hyKQ;8DR^SWR5v5(+jvR8^}Xacg4=5<*-2LZAw}Y?U2I&l!;OzMo}7vFxlj; zdYcWLA@uiZ0}+x8|5gR*Ynx+ggJJ3OQvplN55!1OFZJVt3}k35CF!u3zw=Ij>TI&T zU3h0dbcJ;mDcxD^1o%seN5G^8nV@Aj#Eo|+Giy+X@V}=0OkMWgsxslgZC3V1R$7*t z<;!MA(#yl=V?bqQuMh1dPOyV5ejMfdUk5sX)jPXYI>Q{oV}RJ!18Vw7Oa(gPrUVs% z>mdPG{_*38UmqQi<7N?8*umKCBe~8Gs{I|mf0YujCDAjlUaT+iVk1~U|IPU5#DZN& zYTA=>3rcsjPwrw04<81p6*$N@=iY>~aDvb?$9F95w+gAeWEC02YE4I9nCtY(HseVWQCFB(pD{lT$Xy;hFV*d{JNCHt%@@;}a*+I7D!3a|=?3TI0Em-M~ zQp>TAt&i*g)rIOo`=&u{UD5x}na}7}?1NuT&p31^FmK@Ot8edrAVMrEE{G|G+*(yN z?@BH$4~*k1TU)=I=cv=eR7>TSyrXu1p1a!NhxZlzZ`26OgNy}Dbu_2s8q!XWR(gta z=(az^z9kErza&`%3v9#Ayu867sgeMmV)!1fca<@mH%^ahwXggRlSfZ+^wwHQ^P?Hr zHx31_k+M7via~#Fyz0uRc90JO-@W?4CFhSWH0x|vY(G07sl}LKyvHeKVy#=b{X|xI z<$RFN#ry6<+6P_xE3(bMiST27*z%yPsg7BvP0!HP*vpvsOwpRlw1+Nn54EXI5BD({ z@m=(Gp91p6IV4mCP5L^gVA;t_DriWo5dJ#xu&giGY1%bCsylw@bXU%6C(#N{u~iFl znob9BhY4ik*HJpDy)Gd^&EH6W=tpxK_MKAt)(dHlB+3T6#9()H?*`><*q`G+?*LU- z!6{RTglvCTQ!tUu7gT5bc%s9-L z%I6`tdf{^irqa&2C#14PL=Jboy(Mb**tt0cUr3XeM;vx#xVosVN3o!W0tYx^;F$-> zVu(PO9^Hdc3OY|J7 z2d7fr{2ngo#eDZ3W`npWO%3tPqwSrM89FBVUpxG*0soP*cYFSQWeTXvq4Tndf7IUG z2zU91GzoB*eGS#VUBD}2;Zbb--nI0;7i+LS$0-sQxXb6zW~;r%8vGeQ9@pqC0oH!K zu4NR0IF9~EfTQlEqfm&3)d)>ie$(TN#)>cv#0lSKZjHrm$QvrMAL=!>znsFff@tue zo>?}Ue0-2X4h(I;>%-ZIZAW4U}Z=WcY0!^rofl&ZRvX!yqA(9 z?C|qY2=hbl5WJ-u2C%|lUdWoGunT69uOS03WTVFM2ZEe}Z?yzzFRCqx!g%uEFDXU8 zO*PV^`0A{0&k%|(JkMiVVCMQt7MDx)xmLJM>+Kd@*IE|k+0fzV&eJ5VNWIb<@Om-5 zKbHd6QCx>yml*!3x^6MHSQC$ZKfof}w{I+gjJn~0nG9wvHKAU0>?24sVd!MF&MQFp zz34hybt2?#?U$M4m8NQhjf9wvVT3(&NN2hHtE+a~8NqXTUvsVv_Ynb10%jwT4QpD$ zYlOe3);K|8YtO$F`NDD|T}Pq-aNBB3*Q!^z(@OSfsbot@9%p8q2C?lR=mn-ryM_!6 z5g&=-8Bo4!A6=Zsk)3*J1=D(xT(OGz_;Vq{i(-Fuif-Ea7}z`fB|WCFAe%>qTSut| zVJ{t>=ea>_5kKX%&-T{)66i>=(Eh`_gS>kiZAmkerNrWaoV9O40IvP3$h*PZa*Y9I zta7an)%>C`Uq@icaRi9^2cG-Lt1!r`59Gae~6ozNNa1|*C$p}ZkNlJqSvW!ecn^q?+l$4P*R_KlH zJ?)XGE-l9pQIouw6@g%?njb7gXdsbaDm1Z7rb~5ApSMmmfp%g9zD1mguNS80P*(ep zW;4`;)~MEK*7RegwJEbY*ln5euOXQ^I#5mO`#S%`#yY|9H{fIg=-0&@az9|MtxquF zQy+|N!bk{xcQD0O9HnK?8~%d+1{ZVVWUU@O+cxkDhxoE5BwZ|j_Ii)a@x$&*m$1OM z@Zt1T(ci(+G&ct23%OUG>2anRdmaA*gxG)2POY-thcW+~9$VSKZ+qt6+QB9_*|r4!h-<>%5v#BHrCz5o3ry1+E`x$BUEQFRHg>ihIigF_JvOz9Z%Ho1 z<=5(BU{^Ob8Rs5&1gFKoJ-|}UIBS(*(0rEVKpf4vOs68ZWazLw!*BMd&0DYXl=QvJ zWulc0;YdS#&F}i$%RcMWltBm(%Yj(cuHt@oApic`)W zqR3>X1${p&a9@mrGo|@Eo}V~qIV({9mS4t00N&_VQx^nTnm%`kGP@yAY+m~#nfCqn zzDRu*{%2h-`f0i6)ICQ5W5aR}9v_;!wZxn+@~FComD>nc?Q&B()qK#}=})Tp{9M^y z&QABJ$neZ*-aVATyaI5?i3be(RZ6t(9{f%U^GP^e0;vyL@cuPiJKnLnl!D9T1KrFq zM_JoWFVN0UchqPL3wH8&)Iu998d3g=-9Z+6h|B3Iu zQ66%78tRRG)ePa?xe)a9r!6AZudp=;;vt2+cua++Opn~O>u5!&T1%)bHa}ITp~nVp z!^-k#ST{qV9SmBGg)u+8MO|Av{m{KoYJDq zOYsdFRTYg@JMfSSDY^wRU4pNVyRnqC;kqc>D!CVO$?Tor(;Zh%C5T8Ra%yNF^@iyl ztW#{_ytO+k+<@0fT>+x#@iTR=CPdXE67O#|&o&Wm9eWvI!0U2ZerSSHDeFXpY5&{m ztYjHz9(kAw?5x~c^SX-+xA94(R~dczd2w6o6!N96!CY}?m2tYUj(Ug>E!<+94p>7) z=rLOZEb39f%%fX7`er!jHHSr6TlJ6>{Fmci8J!-B4AItl-LHMb&WgtRv3=#|G9h(l zBlBO<-y6`UxVG1~P6{iX!}%i{8gBec z6mX<$c%&(LO-sxH7xQ9kr4!Pn83c5@t(5wd#2B%qy*7mYzH{MpM4=z%os}%kYJ>gV ziZb_C^noQ+SWOM>iP;F6)iw(Ld)mN7N9?1T`ogScs_ZnQV@QC5X~m_fp+oVhUz&qE z*n(o~#{N^Vc-b_JZWI~zHdoqFeKTz{HGHa{Wmf&mYN^=D?0YsoSE1b9uU748?RiP= zBe7x|(YF~S_#4HF*J(12mFD3yU%%9<%>0a(5mKMO`MmQ-mGjs5#_;5I9AdnvFMaZ4 zJy6n)*RGdwb)whCw6B&~&-f=-!cOxI&7?clBUz`A{?m=4BR%s=ojwyjl}6zJL-;#G z5t*B3EA-(G{0~|+LcXsbjm7ZExQ$r>}i98X@H_D-92-geA0 zYi*blBzV}fcFS{Fa=y*RfIg(YoL56%s`RVh+iBG75u0w9+rYQ<%^Bb(NQ~3#bH`K* zl;yk2KO8~G*FCQ08GqAw3@WcXQ`%~!c?K)P{DuO7&LQ710nj9oRj<_gB(if-E9^(j z@uY0LYrS7n91pEriVY}FwXorY44(i!rf%<0C#K9zfw*8xkNN|}s)#9bC7?Xa+{Pzl zn6!7Z9BP+E)3|@%F*em|rqzDhl8DV9Qc9*u@`BnR&Uo3@2hNp>#d*2F4?H^+mYsij zf~LO&yFfi^{I`8El?RVZdhYBn5Z425sXHe7rL?vtlJh!TcFq!ap0Hkd*Dx|8j|0U_ z6(cfXHWeHAVe-5~eK}}jhO69>zLdDZvTY}u*f@1q4icc5Oa4Fvk>@KEI^9hhY&cRd zWmxpO6U5Pwp7%?B)4uivRKe+lVhfFz^S2V~Gx*AAAcv@5x%utncQ{*UmE5tlYf4p3 zfAWVt9_D-K@Hu{yQ1X{g)2|LK-n1Wl!B#}AQT5t0zhEo-*Q{1Qxbpxk-TMqoK1A^J zR$$iL51T87;P+gG_YC^m=Q9B|vHg#eL*+7&){JW%R*&k3&=pq?b0i*7h5!|_YowUa z0C{68pMHwinMFBc8!&Gsp|^JW_4>zyp4ykdhc7LGg07VuJ!HFNlr_CC6pB1}+WQl> z5mf%(alRmvyHbDdh`f;c5wA*pGjtXH226Q!OA3dbS<1Mw=3)=?$fTLh4$q_!0TGMA zbmh|Q9$i6xq2T7^XvkLq_BI=|^ZtZ-XDap;;Xg&gpGH?;D1p|7AsT$PlO7F=rjY2- zGCyg%l*|jZuJKC`^V9>$JqM8ZQV<`iDv;W9kkdV=6o&y>iQbRH2sCx7<+=Xk+c*#NtrT$7OqeGKun+ zv!eB6*8jXadGRZK6&#)ZRwi_Xr-WbZX7;+)8{Nb^$CUNjA-TA5!y%G^m2T=Cm!KCT ziQ{XrRHojKcaEt)M($z7rzaQC7vpNeBT6mapC~BOM`C`2;qWO?M|btB3#F>CNO4vR z&u#AQd&lA>B`lS%tv{^IDcHI9k++#lrv0pJ(!E~JL%H&Yzx$eVUc0(*>e`y(PHf}e z4)3lKrEA}I5G5)IjajQN{C>2FT zc9&X0#vR^?>4FXuSRO@G=kkBD$snRTYX}H7YFnO{<*fgx$wL=@EW};_Q+RFw9yIVEnT>x9$8scs&;en_)|S1&-RJAgro6yv^C&{ge;AA8BXKtot6tF`JXl_Lq+`3tEy~6ChXjoCwHI>uPn1o+BWxdq~Zh zS2#x%r&J2NaLxk?b5o}dV?{oVAuR1x@j2$p%U1@$ggt1SwoshAvNMH(zp_|W}J@Tqoj<;vtg@S?Q} zBL`SqQo0{|m0;l-XKvR{ggCL6Zi`Qvxev>tspv0F>a{{6lNx>5duzpm+{LOj3fF~m zyC}j>G$aETb1aG8VqR)d=~%y-opQ@pt+ufgb|(Nyz_Gp6DG25&OM6MY>?7SGsNrXy zwc7(_NgDitbwBIUH#j0F>w!KKA$vxFj)V! zxO>vx?tJ*u(;xD3nQ#aZ(vW>0)@l@qVkJM;5b5GROBGMw^X%Qxj|K>t$P?nz7g;vuMQH!v|Du*RWj zd+F44gk#0?F?ih$;lCj7hTNUb#w$r=6GPPmC@&huF$1)thV&{v->1-}d3%J>=(XmJ zM*r3qqc58OHu^7!Z8k0FM+RI_5em4zqW;OKUp*`koj-AST}VS=vw}~ktM_bfa{9#s zy~nV@^01RH`8e+%SJ^K7x&^ zzC~bPQTim%&-D3(lHYsyREVnf?Ge=|5_viJHi~`4dtYApc_eQKYF$)wLHH!B)jHDR z0#pLYwW9b5IJiXF3v63f@_qzUUAlde+iD-#co`}KWM5hQ2pC`^&0|+j&VMq2GBk1u zUbodQi}RP`HiMl1LyW!P{{nJ;ju^sSAwEdo4r;4IdseKA`+T`R;%dNocl}R~{ne!Y zJb%JkvpV?E(7E6$n%(x9@`I;*$i)=3jq49R&g`=In?7M{=q&|dxGY>)x#DS}Dd#?Q zAGCgEmnkOXn40w8xF>*GNX_Y3IT*5=Rknnn>7f2UP`+=e2K2SayTTQ(`)|OeV`n49 z#0lU9)|V8`08os-)tOkmFFNO(vH@KoUCz_DIjZ9^4>m5Tf?HC(2gK<+tFB)!g$Jrh zmlhMFdRM&%2+$SEsQ17Vz`=>F(d@RE0Blf#ncPL-ACT;&bglXYiLx!Gs8>l<-O%d7 zVGpATSE~^NlJXIe1mi(TaCju%!e03v{bam2Fe~H$HaF3*lfluqb9#qpvmXGbQ71^X z{y89|2coSZC?r_o@Oc9if}S>z=o~^AR(1wC}>k}Wby~Y*8)bk&anG?@g zcs+oy(4uY(z3Q;OBuQ=#vGkn3GDWATo6v6!GeVKFjfJYRC!P>r*9B@Um@6w} z_jBwjmsTFK0R>42=|yDjq0HPVV#LQtb$!as3+7(c=h#K5Z8JGDOmYR6jNkC0y{!r? zA|BUUSjS!?L{z1G7%JHJLilN*AM>Dct|YTF=s#4sE1!8ltBH%#-@I`Ul4qp9`yY(x z->qdrW40>pt$JP1Xa2UkS(w8k(~4GUY)L|0_K>u}q9B!lhReOlWPSF@fTQOwMKk6} zk22>CqI5u&A|Eh0OZuwEF=EVo4hNvrFzQ0-L?t2f^scJd><-S0Y)p*Fjt$`FqofXQ zB7IqYDW6INRej-ItDzUG z8JDp$z9S8|edEWjlTkz)l>wmgL>v>yXKuOrHzzTgpr8S@B$UsnK9MM(zaXBg_lD?s zkHxB`tsCVPPizR~1eG31*U@Rpc4u6azQ)`yV93BhuAH+x$W4nnAHr1 zQJ=^2A&7!i5qZA#;mUV0Ss4fa_FO5363sKICAH1*9)Gv03b+egNB+iXBej5}bM2BM z&&w3k=LwmLh_ezc+udHl(9^$BN`ye8@*}kqiYrEtXRY1qa)O=5iFK0GXQM^?!kn58GIOM>2h z@}wdm&>4A^?V{fEZ(0>J?e4|b1o%mUcF9epSewfU0!bBtMI1X;xG_;lL%x**VXLjZ zpJ<@Ef|wP{h6@fn0?#X4dq_N$q%r;!7crb2+Wl;;#QPvg}Y1U1tnQDaKfbI)_ol4ie~ z1x1;z`}}wg8Ov?3TDK`XTPS1CB)&&}Y!wk#CGl7k6F*>fgn}~E=agTU{|Npp_(%)$ zwnT9beB+S;=BD$RU-);SRXOgdtiFr-?7jiiV!lUWQ>n`~7?pLNT8{IWGpyb}Ebi(a zHX%4(rTBarzhw85za8EDg2lyOlc2>=n*)%HE!rhH>y1c7v-$iu`WAMUXGChCMf`v| z@St1NdKWl)ifWkcsRlZybYoLFx5?BZM%+lTC?|Gr@bWLz4ppKR1jq0Fb7%ViGjqTW8_U@|_E1DhKFBpQ+1BC_OS<+dyO2C00ktq&+ofAs=Z4Aj|y$@0uon z-E%I{`E`$L!H*M7x(+ua{pfOC5?vg*K4|f^fb_094nWj@8*P~farTSOg<(b^>zqB6 z#4m53wSH$cs9z1ys=Aw5=HI&7#T#vNGhm)Ibcq8bzBsXaB&-Dz_n{+^10*>!I!!nx z+*vXYF7~lO12pmiZD!LwWdMpt4TpE^7M5D{>sh_P*t>RPjDM%`-FD7^EEo)CanQfE zfx|7D897JK!z`Lp-&D!mKl(8)ZeWXM>X!LWie4B-&V=MwXya%e8qe>##1H|`Z@SDA zd&4!J_qftE^Q78tvY_OA&EDzBFs*w0mhM7c?*N;d27K9ZJwO2t!c`|lY|vtYDpIVv zD8H_>f6$Ll)!AfUuf)*eQ%hsI4mHfzy8VM{wFD(KV!97CUi3(F4U)8@G-(N*_$sl1 zU)>H+;}pPWvU!BYizTiBY_mxS3nAnIpwFip6qP~q5xtS?!?=pAsG8ZCu-w>?x(hLh&JAmgsT@4sTOJ|ZWJOdr^B_J@+R zfOnqWU}g|Oy>an?B3H^C%|`)_(u0e8aI3W&6f z>xZ7P|JhcphvM5{`3aeB8iEm;3$U7A__Qgf+}UATV{w8NU>08xCgH^2Lxi;4m8gx& zrd8>R_V0Wq3BJBd49T^G(OV2}5owOBa=~qOf=XX+uB`-DYp|O9TpFo*lJR&!>41Df z)3w!$HC!PeeiSd=@e29Tm7YCJ13-UCvS{LVJ~)V68^(}ijE?h(5SnP8qyij^&fP@5 z#}UZ}iFc8adpnO+3W(%WfiA&e%nad{gqsEu!8{wbf!hHm2xK=CA*TXPm$Fc$*vH>J zI*uK2Ti}G33UFierzY~iFlK^qOMVQ3fUvPgZQ$0y39$buw<$ftm_foV=M4V)&9QhG z7n|HoN;F00_b2T_r9c}3Of3>+{;y}3Bu-yZ5XaB>95D2W&(U8qSBycbQ>{hd{Ls$Bo<}JdU3fU-wi|(q%v~6H$+-tKw@jBey z&=srHAsvFuPutkF`>M-U^6tcKC*D&}36|5(W@3r~`$vzZ7^3SdjUc1b2lXwr=ZRO^ zlGcIAXJu8FWy}%QPS=9fp5DJ#!8H~TO|p<$8QFFEcf&kpQWMnTuueF4@ECNK1#zj| zFuVwhJ(D3|9--#s-)>PsUh4YO?GfUtdT%KE-Yfs#m($=%dXLc4A{SW84WLhEDEznX zRR|mqb7f%B>lW~n0@U@!!*b&|In~z;^Gl~|cjs=RRKgW+q3*Xa+aQFz=7;&M=ByDy znZvuEfwPB~^%Uk#uhckN3fG*S0J-94GIlkQ;EVLnDO*NnHl}^>Fm@cjuD%~NwPg5I ziGEArx&zYhDMF4_;*~^uWkYz=5E9LCUf2!uQ6z%G*%(T02}q}>D4_Dp(<|q%R%+<(#AMr_uq2~D$Kj9ozp*M`umtxPSAN>0#|!6 z$4;w^)wD)2QrJ4%=Xxgr@sA0wF-ABsWT9vn1M?yx1cq4S%tB!?2EPO_8#p2{NkIV) zM5Xg-;fP0&s6iG_4RnDFapQ9!WzFDg?$!Cu^x{Gy`7L5iC<|4!VQ||xdT-U>a1;Vn z1UlrbY~ad?N&fV&k|mQ#?0-i$)@02x`aqdSJ45t{6AqN)J9y(5OiY_z@5Bnx4&;4l z7-O?zsEMKv64SxSWv=u=z`d=XTKk)569_syuJ~ggqKEf!pd5+Xp`!2;=_f@*^7D{> z0H*8kSQIqEg7NuF4?uR2NgQb8E;Z#J8lFQoutflx57I+uReFK2!vq-_y;+9J-=yT5 zFrS@ShTPxGiNUZK0z5pyg$^>|32{SP`?aNK+kQtR{6y}NXiUE&l3pUz_8#)RvOKw8 z6Nxb<;{Lr}>j;uXzFwQ4JhB&3XHqtd`;WOjNOS zFzvc~2#20&UGK|({z1n$FWP>TlN64>OvHw&dMG&;OuIjdS2alxOPhgDw%nSI2OL~e zlU#uNa<$%vupg{`HDhD?9BwRBG-k+VvsId)=Y}!g35hFn;!^P_$A@&jWDo3+2Kk;S zunRvFg{buZdWDn`?|7Ev(*cFajr!fO5(|8Da?d0I)xeCfI!WAf zk^LK!-2bsLLPkXSBO#nQF=XKcs(V2>coqs+a|^tn44$P5fbV@y_&9h>`12-K>uHN* z1=-}&ocV&DitRPH@x%W2E@G0jt@mPj&i;>*52uf{6aFeSSy(ji>9B*mES^`KMhc-o zv+Heq0$R47G3&9y=T#bW&PKcQc_BS!u*3TgS3+!d1rLh(vt~f#QtI17YVM=(MXo{k zqn<@BI7%#`3=%TL*2&Q~s0yJq*$w_$dUjis_IlpHqMDkZJy1{?)Iz;veA{g7ls`1! zuU;2Pu$q$m^ivCjB#2@XXHV3=Q&gVPnDflMXhPD)NQkynKUnmnn7t9K23QivES9Nd z8c4zXJB?IrkRz1Aq)OYvR~Sl!<@Bo0r~;JwL^v;XQ2;TMS6A4b5_`hNU9SmtRO!1W zk9=&q&6T*d;Ok@%dOJBSiFnR|Bqb`e3E0+uuIeBW#gzuSTAf1f+Rab40!xd{U);rv zN!?CMRzG))+ibXoPS*O5j9ioKD?399@bM4A-zuZ11v_=! zH6i^+G`rr8ulz^ecHJER_a8CpvL0Wh6P5cbV&jDW$UO&s9^xcafSoPFT27$PHk=jq~mF+zNEES z2G!e8QA%d6@7$Bnwrj}5YSbQfm~+O%Nv@|7JB(=t3H#*Dqd|PJe~dof_Z$}VQ*ls@ z>N)}J!opOa-aO?$1=qnu|HW=b$W-7G`JK3xRAGHFe;&uls@}q|w8uvBcaZ=BaC`fY zy4O^nip-tHAH_u4brH^?X_8aP`EF~TN#cLLEYK_1ZB@KwX7Rs&R1t@dlP`Vb)#kgk zpF~hXs53_q$N#}N);@LNN)~B%bp=GsfOC{8|23f%3f&Pa%$Ce0vq!~n1bx2VT0;Vs z_7$DApQm86`y7kc-CJn*cr~LZhz1gRzmH?BWyYVDeKja)2;xCNZY7pJ-KR%LMXDJV zj+)ah#;R$_(GQi8KD=jjJogW_m57NPNnaz0`i`#a-0k4obFN`wBK>k=9wdtuzYDWY39d)e(6Ai3D|rv>4_U5^1a;j+0v( zu!!d!CCgXJP9CcXEQ%_QPD83Z-=|ONf@?@G2v2={FCPOY!NvSO7kzIszE?lg;2}I# zqpIC12%Dybc3ngW5!3fnBoQ4Q3hRSAG7W=zt^ zYCTB*M?obmwB?DqaKPB1J_F&K>l({k)=ZV{q|f0^^_OY+PbOi-eY!bS8=Q}dzHw`7 z^|qdI$9DYi5qA7oV9BhS6PmX@dZb3kU|eh1Hs&tLO7 z8n9b`DY<{H&o4Ymabp&x8`B6Yx>aCI5>vmDpzPX7{d5_(9DvuBH*~Y->XX*~b z4GlQgH4xXA4QIzEdE5LY+AkHc?nO{NB-*73a=vHgPX8V2y|hxfyf|h0G5v+}mfD7| zzU0$=jqmqjxpvw2^tdlg7Y&XHo=uOGHYQw<=C%7Ez_rV1muSynEB}kVO{~ejjypuG z>5Z?m^;maEz z>9OnmnXkr~fMNKGA&~xvAmJy`mu9;8VU?j%Y^KDysbJoBhnC~F6&Mz;6eFp3U-g%O z)T<5bU|am#M{P&9f8#Z^tYe51cvr3M&Kv_GbuPhQ8%L=Z+Kbv)H51;B*&xA$=OTCm zVIb|^S2^*nv2ORW=yL0tkO0qAO5U{xmC;8UiPy2mh02JO_H21TTJF579ZH?r6zp0{GE$WAJCy(7CbUuahV#kgeT3SE~?xT#$Se{%iK**i)!evQO6$+6yE~Fm|6Tao~hU? z+-fr4Rqdn#^(JQ~7Y!?_1nRP~ErIH8x3l8o)i>o71O>t4>YffE$AzT}U8lP`#+=+c zwQOxxwiQ(xM^~~0VW*=J-QB0#S}`fhUQo!6mZAaxfiDov1lVEmC1(1O>sZ~L^s9pt z@yAj^|3FMz@uFQ6kE@HenicVneT`YFTR&}TC;*^W3M1&T{MuF5?_zIRKmXSH$t^Q! z0Rj`_p;A1-#Uo8QmJpbCqX5d0C5+4WjY#Inv@1 z)2R1Z1371VcNot;iT{e#kH#y8pZ*yA)bf@It2-M+u|HG0$cVocqkZoyCthRMUN>fPkn>Le` zW5a?Fn@Z+c|DK)VNvNQ;}D#_2v;*AoxKrukw0t$V{E#4LJu`A_)yZd2iNS> zkD3RBJxQUv@5V}tdeUCrw9#Kcyiq6_J$_asdC3Fi`|Ep36J=tns-$bR<6ZF2=S0^D zNBZP)_L8{s4Cu+^yHox5FOT@Pw>!3(Hqw%BNdyb*{S%6F#ceeTtOPcM#$8)Ue8K2j zbJaNVVzH3G@k!b>&Sw*6#k7?-Z?ch2vN-pOZPa#G-OYV(EUpSo>z-BLrb`j^4?C+E z(?t583pdso=XQf?aOxPOY;0gmU)1Tp@q)#D1*UQlFm7`#bxnZY(b4f0DxX$k3f*LR zYW$zS#CafcPf*uA_1Zij?tv~2R~a~WZ5Y+K@&+sC0PdZ5?c9a?poZmu7`Xa;D{pBZYsDyX{8cQ6U2|XX5Mb%O82TabQ8EI zI3Yn?069OF#e0E4M<&1ErKPIgm3u&=_yS{$^*l0XLss=wj|F=j{8YWteB=(Aui<=#HIUpqt>v`%{snIT*A+OV)D_=~@UYpx%)2XIj=ga#7(}yxEp8`xv~$ z&~&pc^U2}E-N=T4max24uW!?z!Oo)p<^-Fv1HCK)tnrOuo{s*%$?6ONg^iOw8>;7CIjA7}ul1is;Z@y*YMDeEC@p2cxmLsjfYM z&Kti68c!`=vbc2R6hJpccNji8QVXl6y78E?mm8|=RdBUmv9pY|(~Kfrw)uAM?~SuA zCh+(qo|hNvi1K4FaFiai7zz-vhg)ohW-YX8RE;-(B=y)931)P@Ub6|s%PM`dk0C|f z?P$p0tz!+2zTJVy;GHaKuw2Tn_nh1a|4+v zD~_5bMGKYIQT=nswsqH+-G(Ye;W7MJNjN7k=CN7RF-`D`--P-K9s^Llbvky{0QIk! zooGBR!+zZbBZzVonCSh8?j;)lE|@#QpU(t0s5`*NqCdL#P<3}$Ja3Sapb|}fyY8p4 z%SP3pt>+zW4(N&gkIX&L;twBtyY%0-F2AYPnG*fT)pp~WvJ(WoQEjo$a zp6^X(surCLa@!&ATfWm%WS?NJsz240z>5jE5;#!ZX(o-hhY)%8Ws{1C;HwnO(@>a$ zK{?DZ7zzcNw?)}M6A2WsF%S7@-y*o|QBivFa#Q8%4(`w4cS)azLv@E1lHcqL9VN?5 zWkUwvF{D3~QM^j|+HkhlL9*Oy)8Dxg7xDe=6={%FeEHWO9JaTAgx{P?m{-v-i!3kc zkprf0FEM|+X<;z@R$QR!ZCSajI{EB#nICDvmvU_eRFFi?y+4sH(UJre7iPYPucpx4 zZ=aY7RH0Vtr>2IkoP3}NHZ-YJR9ql-j#CktnrShuWiP?OA(7%4#zNhJCC{{FeU$Gz{5v^7EUD)=7=bxzG40W>&P%vtM+Dd$GZ4~C4*V(;)4&)T5SowL_vM8ABPq$C( z-0Sy5cUBUkp{dy7%+u+U)pmOii+)jb?@LCe&5>i%wu`;IN{_#Jd&3e0Yg)N&K(PWJeXu;x{glL7d5&g&1TkAy_-9# z)gnwVtwpE8xMZSs*g$60YG#kQsR#L`&FV|o~uBUTx zzT6qc)~~;esyyIqVaG!r4b}m)X3YZUbfT0GZdTcZ>|GA-xl(nngxgY0KXTPhZS_e7 zj%hw$pjbdXG*ZOCX};cLz7QPYq^EcJ8laMMYe+p&S+8JwtjF=e<0s(ePJse5mK(Af zhLH({wnot}JW8xRkPOm=%ms@W;F zC31U*KCs;@m6w0VKQPT}yzPB4Ms~C6>@=@3n36d{12Z-}(l9-KiVOPW@DyC8a;>K- zfeDM2``x&ON_y6ytYKPk=pJ@LE0+wqsRFAH>dF3ARt)+c+DcOIv;;~N*6zJFWI>o; z{g;3+;gW@tP(S}`d0up@h^F*L5>kmwQ5Nd9jF7fiVl6~@b*Rt_BX%|;g38uj9H6R` z$qBnXS}UUl5$sKc7Int7$42}v>h-q8B3hzNr+UZNNW46tO8dt+R~n6~*^@T$|2lF( zSFMO*vOAU4Mrg>qYMTtI4gNP`_lB`=;Iw5&yE*x$=7)F39{sfT+Dd|{-3^DER{FCU0ciKB5 zM}J*1xz6$KwF4S&)YiM(JBml$59nA~b$|0{hY=;qMTJa(b}-u|rUv;xr>^zO><#z+ z{B^~>)}Sn=Dl&Q*J|Zu!qNHy`Vx%@arKnQGorSI~JcBjmuvsl}HH@tGBteTi!a8>FpUA^*;nN~r&1bIoS zBU$e+vJzgZFdKA^*06{!%;#y#uq6{%F8>h7h!B$tLos+;J3IhUp|Z9Iu&R1k|f zKK_qG3r`BK)hP~(?0IQA+1HGq9Ue=;5RU^+*Jdg?))&KtS z(tSw$t$_1ir<$0On`!&)-uA1~8stUEsKG&%qJm>!l#kpFq!s!i>fa!#h(eWM?KSc` zT@?0w)*#i`IQPVow#aJG!igRvd6z|w=d*z$$D4n}PPa{BF0FoC_^&H!tgXP|0m`~7 zO(cN(hEiioFxN^$Hq*#e8&9Lj6%@U#zE^FJR0h;@+_^g&mTs=UzcMJ@k`#@)(>l|A zq3cfMH*a*AhAZ9Zy`brnFA)Xw6&bV7RpA_HUAoz4J2eH|w!q3{46r|V?5mG;*&65b zl@^=m2+_#e>lZ~bX0_R<6DM0=eVZ-&KYa>!4~yo#NAw7}5P$c0&Ym^x1<71u-B(q; zGTq#M|NB>nHAjE-Cu(_%Q}AOV1C~ml^X4D6DP&&AkT+hs73w>E!yAYUUk#9VZ#d!1 zDmtof0kY36i~4iDI++=k6V;6Aq$~dv=cX%TZ>%4EA#tFY;RpbzIT~oG{M4cJguzx% zpm?R=M;5f3aV>aStQKyz=c^el`rN6xa;y7w6xVZf3*3EKUE;;#Pof^U$;8ih{ESZK zs|+^xve}F^!oIiC*;RgIYWZ01QJo{)(A3U!{HfesCT4^HV%&dTSULX0sP4Yz=HEr6 zUf}X=I@I3|WNprJfCB5;$4yU-rgnrcOZI!AFC34Rq(%E{{@(EBRk_8{_u=0pGaQ5U z9oj)ut~eS-I|k9Of7EU{2nanp;+7DpTX|Z)#rFP#P;qME7)8NGLr=oNFj_B&KJ%`w zXCuDdbC=#SOQ_n;DY!4G+?*8ZeG2wbx$I!`Cb|_sae_wm{CaLpyZ|PqKDhfRC%@`K zX)P@f^vrNxq@+#os_nChX$-Sp)&hs0XxN8LfkBEkFpBmu%fl|FF>InC-@OTgjA$lUwKMpm`LTJsO+#6EvF$MSh7Um$Tu ziQmVzOckF{K|tr2V98k#-(LO23cJTeI_LY0VJusj-%TRm9KX5fj|>(odbHbAnBHHb zG|h>n06@gh?&qG-81%MFEq`u04Ce9Ryl7voYB?{_L%T3B7&O*!cTDC0Lc>{GU{u?C zQ7sPgMzr`=j&VHARN^uI7&qe)8-PC+o{TFE3*++G{*U|#L=l(k`C zVH$rbT%PQoR8n{x{6_t~f#(g5A7W8$+=d>TlK@?YYXza!5%H_blZX7{av!_q#5^Ni zTXa6G6H!*R@oQ7}H|2r-pG+q_?=J^F7iPtLvz>jDEgWbn%u3A*GqL#vwyO=h;$S~| z*IT#~dp(NgVm}QT+DMmds;kGhpr*1it+0ssH&b(DwC4S+7H&7s-pPiHF=W`0(+PAy z0{QYt_y4us5eJtiB&Is|#Wey<+*pe7xi2C2c? zFH#$^5rt$9@7lG$8F@%eAJWiyw}!Zcd>&X`Yff6S771rcMorZfmp7dX?~16jl)Ws3 zHj8d;C@d0TC(PdChB+Ch$0Ux$;6?U3|DKB zfJ^NLbfKAtwphWDk@AyocS5rcwb+~u)0e37P^ej<8{~!)~ExyMGuc_TVMEf%o33I`+u7PJvcsw z5_|RNDvHFni+`yVTIxn)DxV&m=g6V1woGBFV%|Xj*j6)tAP0UK-+-n~cSEphfXes6Yo!@2vFD}#>cqoMk><+!ITscnaz6-7Mx z`AACyZ};WWBxnaQC!#)|@1gf~PK7$4jmPdmGa3Rn{H{ zZxwx{pD1gc!cQ5v()}%O{mtuwN7K^)<}53B<42UtGS>_}L?Z>mbrk#1vr?UeTYHsk zboO6^XR?ZmKQiG=VRI`y0a@@1#?#?DiCchRy7q6!)2Vlt2NYOGzaGz7l)Bb1RD7J% zm2R3{)xb4FNX6gQkKs2c*GM7}h3d}`HsTu6fUh8J8+=cTf#6q^owx=N@Re7xg{)iN ztVxnM5;#%wc+WZ39@0p&l)lQ$V%5U!;IaXG!>;|*B30|THAj0 zN|iOPoa6VH^ya~`_0@OgfT|*`%`-L`MehnjSev?ncl%yCWdE821o=!IV3$8$u51lx zh>m#b&#E<}gU#7Go~K)%8QzhS=m0d4chSdATPg~7e?AkepC`Rtg4HpNP2bxw%^B$b zbGyIPo!W^=U)JQV!NQ)CAc2zj*DI0NLq1#3c}|yY4NQHDqd&`3H1PeN+l7vZQg@d- zve*hTqD%WWK1-b zu^o{pn`2o}eai=5Bk*Oq=SB5h6!=QhSEf6W#l0Nrhxemw!oO$)aTAKEbRyfDq*G9X zMhu;JdHhy?UPp**JhsuM9Vq|ti%)p->R*dxXdo-5d}s$)nm$kOmMc;Z)2=6EesK>k z*&_>v%s#26ems6A4`)7osQxqrHL4yvWO^*J#RH)$n3*~1u^A$pZj(Ug%A230fbsiz z%y@cF{ROT4WjC$$83B236>3w0at`imdKUF|d+bPkX^8AEaV+sa1>7lA9VGi#AM^ZA zc^>D$|M>aK^$i{cksO<`JW>4H*kjwHz2}JXJ2>5jcg}@3QQJ+v`F0(CF1BzBSYlp5%G6hiUWp7Ic9sP3g9e$>Y2)-rsek)*}+$dJHT&H;91s z(-2MHWU0gSV-3WGp7W-gCu`Dc+u6ve9-9}B<)n5X@Q&%+ldIRp+P`*DeqqVvM7ry8 zbz6yB;^lN6F1EkhzVsL|$d9!byY6jFcwU@TM3?RLrjtW3OP$il(hnQZBg-SGB*X~% zGoQ(3l`e2B^o#FOI=TK<|76MtsoHkz{+De!wA`g5Bf?l{nD@*FI=P31k>A~WfbR?U z&jQYfJW;XywC&$qNyn7`eD9pXj*iVANev_?1$HqS%o2LtyIa*QHdhS$yjc6%Bo^ zbEWk7N+C2-I;!a5>r%#WvzL4IQ*ui5X4T??k!cCoPpOU3O3puZD%LL9CmpCup`5Pt zwX!>UsonAc$hKbE4spGVu3Wp#pKC%8GonR}BRm8Qrc4E`=0RmsdllI*_ailNjIRJC zEs31&&JOf|Nq;PK5HLtPApPCl^)OSNARu7A3}*#@Tmm2U+x@Zd?EQ09$G7>QS^?zu z-076HQDZq(J-XhxU2xuLfgDgTt(Fphj;9>l=%cW64Q$>)_&yP4Ep*OMOe(yJm++L| z9LD912^6#!2Y5#j_?=Ttw(f!~Pvmb+5jXCH7MV;eKu0x-PHXkT(Ek;mjR}Ta=+X=; z#kn6uPG5=x$>~+7@j$7i}91meP*oQ^tll& z-9<&H+6cN*uVP^>2kzizjNA#2*SOL0vjzo#KU@FY*)Zr*E9S+Xn@uLx5`TZmzc;d# zc(S-HOsiMxrtChdhc0zfcaOTE3mD44W z*@79^a!H}u`~$9}!~@QzRza&wH2_U+CvgLuFHrX^*wL?&WhiGYb|j2P#Dr zi4Ru1F#v;92P+IMY0->u`7UODOY?wsa1YXeF|ji`ZV0n=~1UQcQr6h@Y5u) zcvB9VYb3h-Obbs8{E<2i2lrM19^Ot7=7R~d?wh*2^EcVA&oLQR?H84EO3|DjqDW8h zh1qpOd0h3W(1Q!nD>o)icRL^TjtF*5fzOha4DH%R2>6@>oq0hp)V-T&P0uA-HcMHk z*G;^?0DcF3vr|v8uN3}zfX0I}ih!|%=Hz(QbUCLJvI;77=9kt(i#$#rsyZ~%@5{>l zeiXlFPyZO9C#Qu=So-uzs7E3bIM5z!AqkE914(ZiIYydCyyl~6`#$A*3aIu@1@I3) z^4U`4>64I{$xih-hd=DiTd%}HW1+i8^7 zl0Ci%gkSfG(Xfs@kXj>WxXOz#Qsm>BJUeZg3LTkgB78>MLKJy4E}y&kk8VmA*>Gd2 zXD+2!ZGY&BV}7{UUcF7q7}pR!{lPGhYv|-`JXC6d10n|oB<^V06;Kex;TdPPH_o-tn17A@%ss%y;1u-Pt$N)&oIx?;uPJB?Rr^>*N$|MEk(h5*6KR42lLco$yzK&WpaweQ`C6abyV*X}e)P=p^4h@?@eWW*D)h z97#XnA}qVcK^UjfS$t39FP(bnOSge(ef$>tdDd41q@$k9x;s)U))T=x?!Q0gPIWhgh`w|WAjf9S2ax~@dujm=RsZP zi!>g&9zj40jI<)%GoaC08E(3kLe7^pNI>`oPh6DBpi1AqIsrStW&*}wc zG!oF`3b(%)6YKb2!3`f-Ty?NKQMg@aOcK;FFFsOA*~o@Gv5IP3R7y#WokqzA^XNfm z7UH+lE9g+pDEYKY2iuEnsqxFkffpYS;r93-#u*(Luc%xU#R;Avsk+7jI6o_cQfU1W zZ^(2J0dboZcT{GLj;5Rb&c+T6EJzQWpf2J&X^md z$*KOy%B((ZpVpq7s1D4>W(g~I#9*`T3VHNjH8`Wk5OC7_^@T%gOx^k|@jleCJ%h$;QF2HDig8N&YY8j$5{_iZtJ8MIZp%qDc|&8^3H=s1Lud1BeG5Z=NMP+v;;ZpmrKoA z@XMVKJD2LwxhXrt;ab~cxe{#Oq&@2fjwF)l+cz>42w(6?j2YwiT?@4cvvgG$GZ{*1 zNfCmE#h5*GJwI<8V=lGxJzH$&8dOk%KhJUk@EB!xXBAVFd-p*s# z2Xl;;MI*%~$MFAUBVT$C*S@ErC;n^o0#ySOX3?nT4bQ7{jA)ew$f&-mQ0>c6@rGM$ z6 zkOYI;=(c+?ceANn3ZMfITrh)>Ioj zQ(4;tv~UybNwpJ;1mSgxk0Qt8vmqt)`x@o-IhN5&r17;=WePlKYhvR84WVYHW!no2 zyM(T_3?t_$u??|gb7?#ParQY+(an7OYFYTU%;vc)<#7{x6SsAz)iz76!liG3*NWm9 zkoSw1W}wat*~!c~E9XA16NwB+gEG?$;Q_~5uUmt%h<7B$Drib3fEigyCvPo3%Kk(* zmu{CgRjzch(ub~xC(^a7L{5*6-A;bUHpza6Q^tLYJ>WrSElb~zrvY%#lri%Smnnt- zE+k1(G{WS4*tJL#ddr@l?$lHbBl=dUB$@istSvego}p~6sXYCtfaI(2tS#6IosXoR zY;t=5PMY4hL2N*NDt?(>LuyoZ>V~i56}n1bcJ^RR3Nk` zk0jd$fbT=pepX2J8}WBwX8&71xHX?|s;6fEH@BY;76iEAe4vSceg7cT|EC&AZ+Ue9 zV}Lm?5^}k+3)aM#8(ts0!dn#|>B7@vrvb8+EJj6^>}&Bux^NW{N0O6E0GBL9w&_7x zkqLtXdfmx|_h|M~W_n&^?9sz~ney<2~nqyICSMlr-r&`rkGO}4l(399v+=8 zt>JpocjUssDkVi7ga2i=UxmRLV>4-EYUg(%Y#bYnlhp9lxOQlB{j;lp^U&EL{JR@A z;+VxG%w^-dBQ?w=vVVjZB(rp}x9PNGv8AQ}Ru29p1QMgYz)0StEtzD&>0GFgK%LpU zcu|b1IBFFfU}Mz?!H?foOWXbITc``|N6MkV39X0F=0_-S>goVe0(EqBZRs|uX8Zn0 zI=HE)d;utxr#=ae%o=~byt7hePO(#T4NIc@`Q9;UDv5KJyM`XwV*p!%7niNvC|To? zv2)PHZgV$^g+A)dI;lQeV`GRKHY5~PX!<$AJggCH0gpO#B-UjYZwwvU$vTZCZ*(~p zN=j$qlEQksEqf&}`2StbH4snO=<+D6zLANmBDP0c7FJ`DFFA(Q<>Dj~Haa|}lm2Ie zEUt|O{QhTZvP`SbjmatS`^Ej(?#=J~TZ7K+F2m;6#v)eK%uOV5K>;3j8@uPnxAYvt z>hsPCtq<*$n#2<~dVK0??qm`=iAMjsY=GT%G?7eCKl{}&Jz+w(%Hab!@hzoO%Uq`Z z3o50nlIkGPGGQP$;=oG)k9F_KN8Q*=-_EanXKf!XK}PT_xS`(zmi>*7qW2EIqYbVW z_+jsYX2zaLb3Dv4@Tdt`o%MyTq`*sgf4sw*+jj0HDY`Ypw8HIF!T@oq|+~nCX?b*&lwi<+YLIBWSI9o-cej?$5&&L5+*$xV)h(M{-}i6(b`7k z*j)Msb=|A-D&h*9;=T8&8r%w2tG6H^rGBLO2E=yBnEpPFdwG*lks1gtS5u+?bhq{ChC-sYI5El=)?OM-#`zs-a8w>b#o& zI12g%%laqdyk_3m$S*@70#cETa*hzMgBsjj(s~ zpTBN=8le;B(hVKCb753{_4O!9kRt<52#q0E&kHVqUSM>?7|B>8K-vlO$vg-sgne+a zfo4N^=4aAKzsm$qfsZZ>6^1=XA4tIGo3KC!J7skA)SI-H(mEPM6c5mIZ zXZ?sa{}`%UZ_`oENY+eWilD*w8TS8zi8K_5v82SS{mti0r=T088D3yD5!PdmCjiUc z(Xf6LsQTbI=D!zTyp@=b+x{N+yQ#K*09gB!0`$qpspo@tU~_g!-5ZAE?++SY*|Hf5 zWH}%w0y#7O%^s#g&%7Y;Ufa?-UVKqs&t?K$m!hOM*Mu%Qk@Nx}oB|a`#!;;L&vuY& zp=aBgyCZ4dqS8fE&`r)-TG(&YW-7NY*U=1)kQ~oA6(=PU;Cl9!AN5d^GtAA0Q;6FGLB7v*;ltsL-HH- z#JBR~=6ed`I5Z2Qa5)}owZwKpc+%TgN@@6ml{6CL(VyDxm37^RTtV7{aDZ+UsSaOy zw3Gsp4kF6Fr(HPQoMmZaa|&rUz<7?>wb4A08|qm*;NCYmb!~%V(M#j2?v9iGgQq#P z5<{rk|4xojYpAE_YCOlccoB>Eq&)Y{;xlRJE{GdJyC#32@^V>gq;&03Bo&YPbl5d! zqD^RW0BQiCak)yit_90J!Q*SYWc&v7By3?8SGf*Qk$zVuw|gU0vIAjdHven5$uelNP?HC0EE5tDUM8t`@kad*Ems_WLiQ zoTAt4PZ73+mK9_ZSDTBninz)+q&h6ft}un{bEvf#lzd(En*p`_MLd%08|v(XkHrfc z1<;sx1`Stkpqw>6yX$xu620`&wdCRsNEF0dxKIf6gtD%%L?Xjp;)O$>?OISbH~VSY zOf4*_ip^(~nObgm=qu6zKkQ=ReE1`gluZqXk;V0|SQ~XjKDApU*Jq#Vw{xOyMx=^P z7~ScKA^WEmg9GXu0c-JdrGH?d&il$KU$tlA3+MU58^DG*p%D}<V@4C#El3~#wlQ-ATnR{{O@^2I$R6rUirj0|@FatVMdJHzoH ztw|~+aQ>Sph(IKUQ?TVPP*c0-H#$vSdrNu36;0k4x_>KyYG-*PQoOnNKyk$ol+6!U zA_rA_)@L9uBT()r1stoRd;Z^t%OehlXswttM>9La$GxmT*~ok$Cfro$$DNt zh1z5HMi!jRDEjq&McnsdBnl}DC6H{jnKE!Anr;I(C#3Hcx~N+%RgNj?k8^SUgFRd@ z-0E31-r$E9&3s_VloKCK3(Ly?nRQfd2Pi!2UhxgFj0M^`bS(K?Y)1P6%TRgb!AXC7 zw*s;^lT0?*fslv zbr$Gy#-XZJ!#1^ObWbX}hmwK)Sd4_aQ3Qe7h)U-!)>(2$owR=MTVftrHKzF3>$DSv zG;GJQweTX1QVS;ig9dL98e)bJU)er$wfJxdA&h%VL`F$3b*bSput|Nv&T}{8 zs5=S$hhz&hbRu#V_#*zqLXHzoWzu|P2?cna33=tKFSZM+AaW1N!YFHK?+O^q3Xnjh z6c$6v=YE}VpO%*sh)W6^GjD*jz|FR`)BJ3z*}0N%iL^TKP8ks)%|Iqs z4j)ejyl5)qD)2Q7>Xkq3U7ZXbG~mEs&;Bh0M1CpYp5dYzN@ZyH+dm|Ko!Kbzpq;O| z^{=4BYDP}16C(EUmsP1+5%&s|Ey6O6OLrIiSC`iUgaQn?!>QbG!c6gI3#Nw@ujVVa zndSm>nP{QzxAjlF9VZq;)rGBn`?3FwsoOgRb!UOQ1ScJzeT3nace%dWApzT(4pi6|-um9Q|UDgP!@W@IKi15ku8`p1MzOfLM@E-S(TdM;a zy%LjYG%}FXZTBS&Cn=BAI;E0rW6HZkIBaQTO7PmcXAEp!p?#HhKr2Vf%y!iyNRaJC z8VsBL#kpDF8P!Pt|>U5@X7D1FkE)#O!@Q3%| zZgkKh82iITAw_+E%PB2#J+}v?m)f+6*@MYy$N;`W9L6o@*-3ym10Qcs%VaaaY;mAaHOo?bRZw`oB#n>mM1+Y$Q& zZF$7Xbtlvfnf7@{sP8SK1aOJ6clOAe@Mcwebs+q#dyB1%X+|Q)_iK2)kKp_GX-vLT z-VV^Ez)AagCk7N^?x(EbtsXn~!c9HjCqMo>^bORjs6#lv4#ZWKZ?CMMZ}i(RrDvwa zbDG1MHiHx#P5V6nFA=<)|xZK-fdwF^1rg7%U_; z7tULKT*rN4W>t1yX;gbeH|m|3<%ACZfm`F zs9C@<>9P1=nVnkrz}@Jl=}}Bc$8y$Rqb>aM9#E7>z%yM+MaSC#0Dh35qwG zKAQ}TA^5(v{|)v2sTQZ{K2ztqUqIY0`DHY$*~=Sw@R6G+I(_o??%zOSnebT` zv8*TdFlIynh-!v$uQ!1Gdfk1C!81}u7O3VK<6igrP7!Z#`d)c}G8dO&d-KG_dkmVB(LA0mpyYa%% z2$^i!&%gb|;=t>h2=ZrI8a=UGU1@H2Fl_|ElaTagt<3(xJi3zu5AFoRM6RTiyUrWs zkz|j=pDrCJE(M9fw2IM89gR*Ts0>PjiDuR;95QqPf2^ER@P_5;i5@c1rm}p~ilz*a z@dkvlxDMAAPaf4z5(3IgoMAxz2WaR+YmDHtF{;qBv52Nm8vqaKK&r@2_pjxFoi|00 zFj`v%YPl#E5_EsbW{Wasj5Y0<_fYIp(D2FZ8xU~@ea)~w$ zFlcd7)&t-ik1y{(D?vn-36IC50%Q zH4}UPt!-S)*1ZH21_nalq>1fq4)}al;RM)Imj(4HKdIiG+}HVYUwgZMhP+W08Fy$v z7OKF#Kg|oWnvkgtVOy&0haR6E)R)D;Ms@*!aq_jqLlH+ogt`&6>or6Cc?JnJ`uy;} z{0y2El;Cj*h714u`*HIhw0pTL;>;(mLw9DYC`$`U8`|fEUZD?NkIebw`k844Fr1&g z%Ck>7L(Tp&qG@PRMRad??_t@6M`?fZ9gxeVOekH_1|~!Z(id*^ll<`mfqAC?jjE3? zzdj(f8EFhc;4TsSzujWI7!4iWEb+vP|Ct%E9=<p9B+JN3%E3fR1> zK`^gP<+7N~!HKt#diyHp$t3tYcus_04OM(J4)$`B0|Ocm9%^S+=llihUh2{C2z}%CfCH7d+2|>H{&ZBy0W1g0C+tI ziUVle~dbhqNzulF*DL?-wwEgV4mkGM&(*$v~qdDjKFD^ zsiM5PDxddA%wN!_o6NPa$?%ntVAm7jPT<165Wzi85#=j~rJ6<}#4|X(+d2zQ@v^&e zG6fFQtEFaLE%cg%ZEA2C+D{zWElO?P)(6M0lhHnhM77~WqZyj^2(jutacCAKvny@1 zh11TZQh0D!sVW2pXN9o-fk9rc* z3khc%^ScvkiC}z}c0ZK%quzDSs2`@fk(JlZb(e5JsMjJ6*HscGAog3)!wd*+%cvVSo^G^Ib!eC(Y!Q9!tB59BAFp=eqzXO z6uF365Q8Ytc$%-@VXz!u#Y>s+bOZm}{&bvIeDLf28B4hls;+Y0s(VC+4+#~VB^J1| z1h;0xaCkD)8@=87D6vw~(H3m6A` z5BFG{u8xeuToo1m;?%Rl<&Hg0#)^*}WSV>R$*fQ1fbXV@@g15@sBXckbK-L_X5vyN z%Z&DjRehH?BX(~xXo}y~H68=#>y~ls#pas~ zpA(D&8P!6$M4J0!L_#Moq_20ZX+BV^c*pWGVQ@S_>k)XX?_$bRNsr6PE`uf48{M=e z!!IZE43=DN+|rU1xST9L=%}Vs!gJds@xxaCGqYEBkTO>}_!4jPCGw;^;nn#OYvML6 z*6er#j~QTj^w!iDkM;#SgLNw5N@Sa%b1#aU+)~z2*@(#DGWv_T=Cy3-Hy5uLC=O-%WS8nm-C+g)VO64aq=f~>h z$BN{~is#2F=EpMYYYE;fX8AOg^viAF^)Ze|U+3<skB%rR~~ z=oPbo45N4UTXFakHkXpCa#JxmTR-&W9qu7z*gj{+ioi8_F%D{&FP8!)<2oLtbUcdg zaO<}Ckc~AU9qR8tKwj8%UWjAe}s`uX-*sR>1@`S&?z{~SW@%w@JcayhXfwu%7 z{M5PoeJaK)@TMTgPaVPUCb3>Mx}3-czH!SGb4hdBUcY0rgRFQXpPgk2v*d87}-aNzLn-eX(pNv20Uu zZ2l}#Pb8$Ca7hjJv|qMszx=}giih8oT>mT9{#THGSHk_SSo>WO_rKEVcSXSON|oQ0 zy}qY>>B;{052s>ZG%#e9T*@lp81v&B^J5yDjCQUVe0b!*-`T*V$cEd%M#YcMmZ{Rk^q_&??ffHyDni`X+nHZR)e6iP z3n+aNP%0K^`y$X*BG7hjlnR8D2%xkSdQ&})X(7g`@g}LdCaJh$0hF#np_W3Sjshgg z)&=Ub|_we)DMUX5gQj@?S(0r{1|N5vCj?t`gT3^xJ6c(AM3dB5#;xb0~M? zy3si?51d!G;h9jsiBLc9plsj*ElEBCsPYd`IznZYx>n>+dCIhzY~**XXzjlPR`ZMH zTMb=&+o^_|+nu?bgbhEvgwtneKvcw>{UF+yo zOZMB}>tg1~m6i`yM?9wD6&#u85=t#^mpP_}0z97p>UdmBpV%f`^}lv^9h{LsFu6r) z(0rH7u`Z@~F>B$*>Im~x{L+x6f7hd=r6D)}u24W*vc~fL;bheza=R%dqNLqaXvog7 zs{O^EJN{XrfRIEDv&700J)x(cv+q<5k}Ix8w%h1m4@oXPS64%l3I1(c1M27TxcK`B;-|UHC1` zRea)DVs*l|!Ir_GOS%qCKV%nvfLqV0QT5-kv{9I|Ik|Su$@p)^1+s4oYwbMfx2JwS zi9GYfRlbM*j#N20sPDmXf9w_579%k>@UXlatS>eC#{08)MF=^5yFj<|ec;XQTS)1$ zp8xWsEckng25Zh4ucqds)Wrt4u~ACKaX;Ry8D#t^T#7=KvZpVz3q9puRi!)QaDH_9 zvhV(ALpZmLe#WasD82vrGWtEbk5BkDLN8|D8a1|LgJ1H9c5LEzg`y42-tsYEOc$0UcG+agr>IwwU{6gG;y$`&yZM);$a$#;P zONi{PQbbWKK(xyIqDjYRvK=8c@;R4xw^}0Ao;&2B8%k`1$Qgxw^p3(yu0S3n6Lo7# zcigm3H;vk`S3XqQMQ#_x z!5uoDQ~GaQOQi9Aa8g3zIt$keV3Iq+yAk|qVCQuhcv67Up=FnFHde1mN=@6LckBVA zMdhK=C97NIJfTZ3HOZe-eD9*of3?4qwYT{Uer?II^ePEYx&Byh!JPbIyYF8^?FpPE ziulTA7xnW!>Hb{qqqhMAb5&~7@K#m3iRJ#`z7`u7{uYTh0oUN6sE zJxtp?UjpY}$B{x-M0%D1$KkM?lf2!Bbw;tQQ$5TJ!mXczSJvyEGWf=|OhZ_R1z)i> zC1dnsJ0b~W1m@yb>-(AdA&D=YV15dI9WS*hVdj5q-#+w=ud27H5$O5{3b7&YcG7m+ zu>MLzxdh=g6N#Ztg65a0@P5567PnSp?b1$^Ypu#}jgrZ+!=vemS4d?YVB{@NmPuD( zS8BXF%C%>fE5(n1bf)FU+IK~KClx42+%$AZiKu_6tc#xMit^u6kuBzV_6FC4bbL(; z#(t}Q3FPS7PDaV!pukAL`Fq`^I@9JPo5ZaE-o($a30ArE6#Dwl5+EExH+`yHm54I!Sno-?5Ar_%B5!H#y4L?2!cdp!>DGHmb zAu`DQ2!dUH7VOsspdc*(B#JHgCSh$3AADT$c1 zeH${6d|mxvjpcR^MzR#NaY?!jyk3iX0gYT&k9GH1tkco?+5to|e$_RbOm7j&nn%7? zet3HhX%Azvd;(9c_=zJ-OZ|NBTyE;kwn0wPW-!v?QZZgTs+ zPWp7Rn^(?yM^`EYwZol#Eyof1rn%4VL-jH&jkh8#zZ`kY_5oibc#o}{CoE+{b>dVg za#rn60mWw```K!^2kU7*hp-_WU1D z?*Z4u@`VjwQRFYED2j**C{+PLK&2A}q!($@6Qwul9TG%Er1vHz(mP16AzY+`L^{$U z1c(WQNC_bcfp@v@_kR3#nQS{bGv_(aIXju1ZQ-#N=+<3^ww{20zw$^S#C*3u8TI|* ze{{^%O<&GA6Ks0vZ;ojT5fY-kG&#l)AFRD(s>EBa@$ORj9DjU&OX zY|_<Mjw0l;r;aDj1u6*2eI142t%5RIPU01Dg*0_YCm+l3Q0Z$Z~x4C%7V5= znt+u*s; zEcA{T7f*urfMcmOx%Ot0UT#F7jif|crrxOW{lvmYlUb#nQBtU4`VSxHFqU%2pIfwd zRY>D_?eh3iR3fEB?lDpWCmWSvnI@{w155p9pVh+~*};4`@E3TwxrG%^yKm6&Ao2Tq z)z_*zoRmH>MOf<95u67jPAg*>V*DVKv&~=q$xH4O*AsgNVC1L>$%xDQI6K~oRvaY> zzm<;cFUDA6EUZQLD!t;MF9-ov!AQemPgY^;V6dsV9iUEHcC;X?)f^+*zAL=8^iBA4hsoJSMkBuFK`Kg2HajJ?5P~-uT{a&DWmg z4lzRO2fmD zKS2)OZam;G{}lBdto)#bo7X`DLl_>3H?yupN$NoZ<(`jSg9h?)$_O>H1_*8j;elsA zUb*oc?+Cp1aw`wP37-V!-p+OfjR+P&$&{zzJ#$I{KmV_hJ4S;f4I3n!3(i1rJv1A?&lcl7jL(3?bvCN3|{)nUggk9jDgxl>J zeQ)H!rJAnbLgu02mW|TZWeKXbBOMS^3Dl0T5w2Ks<#V92`PsRAa zpP;_%LVvfl0>B}fKG0zU8kJaUDDz@JU$-gc(l!(Kxns7L=W!T4rz07$AKV#Yzi%y} z{7gmYF5c)=v4KJ>8qbtWlIsbb-rYamcF=X9o2$@=Fk9gT7}c%5F~t?=Wp;`iY65{7 z78T`YxxoyfA-*YQ2sS6~rM0fsvw{A;+!(9DLk@}KzM>J0O~2slsxq^i{%znW-aLtt z9RFc+___{cY+qhf7MMtLl0$G{Jf`3H8x&YfzU8($Lu6ppGiR8pGNsPB^BDc>FK|bs zL>m_EryH}FaOZbxXm@astS2q=`sREaLqhD%19^k{MWEoE40#t_(=P{4e)7~@cd!2nCTghH*hWqwB~xXT?*?0p=nS%t+Ol>;ul6#JFU2w|$cjlQRB;A6 zES^#S*Vo*G>5eyi_4B6BpdXn9b6Lj9`O8V*`|fQossgq!$!qLv3O{HpC|mohhZQFw zLkbCV8)Wn3FOrczU|6}}*W632+)=PPK$d;HjujGlH+K$E1LC{)qYcmV%v<@3)Vi!J zcd4xDRY;f#Cc9T8?;p&Kif-&$Cn;kAp;87L$Q0iKm6{amW!3qBO454vnVTxW%>+)uKT=6H={N5m~E^#mJ^}RbxPqp2itOqD%F!aU~ z?;?gD%nFEk(|*(0f~oJ&19YgR4Ghp zB&m4oC^$6T77cnQJy7e%ZNkv1fID3d+zHWaDN?7hsT+U6zUF%@0qS4s9pRFw=Q0N(*O5P z5v4^Z6KWhLW-(jp9JjWZ@21QRBd}Ufom>F5q9JzIdv`?BG_mHcwPt&= zFFQ_n!fR~Z1?%XM6xA9!b61i3#A#$^1h; EpsYOD84 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

7rz{nHq<{jYEV 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 literal 0 HcmV?d00001 diff --git a/src/BenchmarkDotNet.Weaver/src/WeaveAssemblyTask.cs b/src/BenchmarkDotNet.Weaver/src/WeaveAssemblyTask.cs index 9fdff81cac..f19c70d3f2 100644 --- a/src/BenchmarkDotNet.Weaver/src/WeaveAssemblyTask.cs +++ b/src/BenchmarkDotNet.Weaver/src/WeaveAssemblyTask.cs @@ -24,45 +24,76 @@ public sealed class WeaveAssemblyTask : Task [Required] public string TargetAssembly { get; set; } + ///

+ /// Whether to treat warnings as errors. + /// + public bool TreatWarningsAsErrors { get; set; } + /// /// Runs the weave assembly task. /// /// if successful; otherwise. public override bool Execute() - { + { if (!File.Exists(TargetAssembly)) { Log.LogError($"Assembly not found: {TargetAssembly}"); return false; } - bool benchmarkMethodsImplAdjusted = false; try { var module = ModuleDefinition.FromFile(TargetAssembly); + bool anyAdjustments = false; foreach (var type in module.GetAllTypes()) { - // We can skip non-public types as they are not valid for benchmarks. - if (type.IsNotPublic) + if (type.CustomAttributes.Any(attr => attr.Constructor.DeclaringType.FullName == "BenchmarkDotNet.Attributes.CompilerServices.AggressivelyOptimizeMethodsAttribute")) { - continue; + ApplyAggressiveOptimizationToMethods(type); + + void ApplyAggressiveOptimizationToMethods(TypeDefinition type) + { + // Apply AggressiveOptimization to all methods in the type and nested types that + // aren't annotated with NoOptimization (this includes compiler-generated state machines). + foreach (var method in type.Methods) + { + if ((method.ImplAttributes & MethodImplAttributes.NoOptimization) == 0) + { + var oldImpl = method.ImplAttributes; + method.ImplAttributes |= MethodImplAttributes.AggressiveOptimization; + anyAdjustments |= (oldImpl & MethodImplAttributes.AggressiveOptimization) == 0; + } + } + + // Recurse into nested types + foreach (var nested in type.NestedTypes) + { + ApplyAggressiveOptimizationToMethods(nested); + } + } } - foreach (var method in type.Methods) + // We can skip non-public types as they are not valid for benchmarks. + // !type.IsNotPublic handles nested types, while type.IsPublic does not. + if (!type.IsNotPublic) { - if (method.CustomAttributes.Any(IsBenchmarkAttribute)) + foreach (var method in type.Methods) { - var oldImpl = method.ImplAttributes; - // Remove AggressiveInlining and add NoInlining. - method.ImplAttributes = (oldImpl & ~MethodImplAttributes.AggressiveInlining) | MethodImplAttributes.NoInlining; - benchmarkMethodsImplAdjusted |= (oldImpl & MethodImplAttributes.NoInlining) == 0; + if (method.CustomAttributes.Any(IsBenchmarkAttribute)) + { + var oldImpl = method.ImplAttributes; + // Remove AggressiveInlining and add NoInlining. + method.ImplAttributes = (oldImpl & ~MethodImplAttributes.AggressiveInlining) | MethodImplAttributes.NoInlining; + benchmarkMethodsImplAdjusted |= (oldImpl & MethodImplAttributes.NoInlining) == 0; + anyAdjustments |= benchmarkMethodsImplAdjusted; + } } } } - if (benchmarkMethodsImplAdjusted) + if (anyAdjustments) { // Write to a memory stream before overwriting the original file in case an exception occurs during the write (like unsupported platform). // https://github.com/Washi1337/AsmResolver/issues/640 @@ -90,9 +121,17 @@ public override bool Execute() } catch (Exception e) { - Log.LogWarning($"Assembly weaving failed. Benchmark methods found requiring NoInlining: {benchmarkMethodsImplAdjusted}. Error:{Environment.NewLine}{e}"); + if (TreatWarningsAsErrors) + { + Log.LogError($"Assembly weaving failed. Benchmark methods found requiring NoInlining: {benchmarkMethodsImplAdjusted}."); + Log.LogErrorFromException(e, true, true, null); + } + else + { + Log.LogWarning($"Assembly weaving failed. Benchmark methods found requiring NoInlining: {benchmarkMethodsImplAdjusted}. Error:{Environment.NewLine}{e}"); + } } - return true; + return !Log.HasLoggedErrors; } private static bool IsBenchmarkAttribute(CustomAttribute attribute) diff --git a/src/BenchmarkDotNet/BenchmarkDotNet.csproj b/src/BenchmarkDotNet/BenchmarkDotNet.csproj index fbf324e611..e55863fa45 100644 --- a/src/BenchmarkDotNet/BenchmarkDotNet.csproj +++ b/src/BenchmarkDotNet/BenchmarkDotNet.csproj @@ -1,5 +1,6 @@  + BenchmarkDotNet netstandard2.0;net6.0;net8.0;net9.0;net10.0 @@ -49,5 +50,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive
+ + + + + diff --git a/src/BenchmarkDotNet/Code/CodeGenerator.cs b/src/BenchmarkDotNet/Code/CodeGenerator.cs index b75f7641c9..02d0aaf7e6 100644 --- a/src/BenchmarkDotNet/Code/CodeGenerator.cs +++ b/src/BenchmarkDotNet/Code/CodeGenerator.cs @@ -5,7 +5,6 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text; -using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Disassemblers; @@ -33,29 +32,17 @@ internal static string Generate(BuildPartition buildPartition) { var benchmark = buildInfo.BenchmarkCase; - var provider = GetDeclarationsProvider(benchmark.Descriptor); - - string passArguments = GetPassArguments(benchmark); - - string benchmarkTypeCode = new SmartStringBuilder(ResourceHelper.LoadTemplate("BenchmarkType.txt")) + string benchmarkTypeCode = GetDeclarationsProvider(benchmark) + .ReplaceTemplate(new SmartStringBuilder(ResourceHelper.LoadTemplate("BenchmarkType.txt"))) .Replace("$ID$", buildInfo.Id.ToString()) - .Replace("$OperationsPerInvoke$", provider.OperationsPerInvoke) - .Replace("$WorkloadTypeName$", provider.WorkloadTypeName) - .Replace("$GlobalSetupMethodName$", provider.GlobalSetupMethodName) - .Replace("$GlobalCleanupMethodName$", provider.GlobalCleanupMethodName) - .Replace("$IterationSetupMethodName$", provider.IterationSetupMethodName) - .Replace("$IterationCleanupMethodName$", provider.IterationCleanupMethodName) .Replace("$JobSetDefinition$", GetJobsSetDefinition(benchmark)) .Replace("$ParamsContent$", GetParamsContent(benchmark)) .Replace("$ArgumentsDefinition$", GetArgumentsDefinition(benchmark)) .Replace("$DeclareArgumentFields$", GetDeclareArgumentFields(benchmark)) .Replace("$InitializeArgumentFields$", GetInitializeArgumentFields(benchmark)) - .Replace("$LoadArguments$", GetLoadArguments(benchmark)) - .Replace("$PassArguments$", passArguments) .Replace("$EngineFactoryType$", GetEngineFactoryTypeName(benchmark)) .Replace("$RunExtraIteration$", buildInfo.Config.HasExtraIterationDiagnoser(benchmark) ? "true" : "false") .Replace("$DisassemblerEntryMethodName$", DisassemblerConstants.DisassemblerEntryMethodName) - .Replace("$WorkloadMethodCall$", provider.GetWorkloadMethodCall(passArguments)) .Replace("$InProcessDiagnoserRouters$", GetInProcessDiagnoserRouters(buildInfo)) .ToString(); @@ -132,19 +119,13 @@ private static string GetJobsSetDefinition(BenchmarkCase benchmarkCase) Replace("; ", ";\n "); } - private static DeclarationsProvider GetDeclarationsProvider(Descriptor descriptor) + private static DeclarationsProvider GetDeclarationsProvider(BenchmarkCase benchmark) { - var method = descriptor.WorkloadMethod; + var method = benchmark.Descriptor.WorkloadMethod; - if (method.ReturnType == typeof(Task) || method.ReturnType == typeof(ValueTask)) - { - return new AsyncDeclarationsProvider(descriptor); - } - if (method.ReturnType.GetTypeInfo().IsGenericType - && (method.ReturnType.GetTypeInfo().GetGenericTypeDefinition() == typeof(Task<>) - || method.ReturnType.GetTypeInfo().GetGenericTypeDefinition() == typeof(ValueTask<>))) + if (method.ReturnType.IsAwaitable()) { - return new AsyncDeclarationsProvider(descriptor); + return new AsyncDeclarationsProvider(benchmark); } if (method.ReturnType == typeof(void) && method.HasAttribute()) @@ -152,7 +133,7 @@ private static DeclarationsProvider GetDeclarationsProvider(Descriptor descripto throw new NotSupportedException("async void is not supported by design"); } - return new SyncDeclarationsProvider(descriptor); + return new SyncDeclarationsProvider(benchmark); } // internal for tests @@ -168,31 +149,19 @@ private static string GetArgumentsDefinition(BenchmarkCase benchmarkCase) => string.Join( ", ", benchmarkCase.Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"{GetParameterModifier(parameter)} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index}")); + .Select((parameter, index) => $"{GetParameterModifier(parameter)} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index}")); private static string GetDeclareArgumentFields(BenchmarkCase benchmarkCase) => string.Join( Environment.NewLine, benchmarkCase.Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"private {GetFieldType(parameter.ParameterType, benchmarkCase.Parameters.GetArgument(parameter.Name)).GetCorrectCSharpTypeName()} __argField{index};")); + .Select((parameter, index) => $"public {GetFieldType(parameter.ParameterType, benchmarkCase.Parameters.GetArgument(parameter.Name)).GetCorrectCSharpTypeName()} __argField{index};")); private static string GetInitializeArgumentFields(BenchmarkCase benchmarkCase) => string.Join( Environment.NewLine, benchmarkCase.Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"this.__argField{index} = {benchmarkCase.Parameters.GetArgument(parameter.Name).ToSourceCode()};")); // we init the fields in ctor to provoke all possible allocations and overhead of other type - - private static string GetLoadArguments(BenchmarkCase benchmarkCase) - => string.Join( - Environment.NewLine, - benchmarkCase.Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"{(parameter.ParameterType.IsByRef ? "ref" : string.Empty)} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index} = {(parameter.ParameterType.IsByRef ? "ref" : string.Empty)} this.__argField{index};")); - - private static string GetPassArguments(BenchmarkCase benchmarkCase) - => string.Join( - ", ", - benchmarkCase.Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"{GetParameterModifier(parameter)} arg{index}")); + .Select((parameter, index) => $"this.__fieldsContainer.__argField{index} = {benchmarkCase.Parameters.GetArgument(parameter.Name).ToSourceCode()};")); // we init the fields in ctor to provoke all possible allocations and overhead of other type private static string GetExtraAttributes(Descriptor descriptor) => descriptor.WorkloadMethod.GetCustomAttributes(false).OfType().Any() ? "[System.STAThreadAttribute]" : string.Empty; @@ -235,7 +204,7 @@ private static string GetInProcessDiagnoserRouters(BenchmarkBuildInfo buildInfo) } } - private static string GetParameterModifier(ParameterInfo parameterInfo) + internal static string GetParameterModifier(ParameterInfo parameterInfo) { if (!parameterInfo.ParameterType.IsByRef) return string.Empty; @@ -263,7 +232,7 @@ private static string GetNativeAotSwitch(BuildPartition buildPartition) @switch.AppendLine("switch (id) {"); foreach (var buildInfo in buildPartition.Benchmarks) - @switch.AppendLine($"case {buildInfo.Id.Value}: BenchmarkDotNet.Autogenerated.Runnable_{buildInfo.Id.Value}.Run(host, benchmarkName, diagnoserRunMode); break;"); + @switch.AppendLine($"case {buildInfo.Id.Value}: runTask = BenchmarkDotNet.Autogenerated.Runnable_{buildInfo.Id.Value}.Run(host, benchmarkName, diagnoserRunMode); break;"); @switch.AppendLine("default: throw new System.NotSupportedException(\"invalid benchmark id\");"); @switch.AppendLine("}"); @@ -279,28 +248,21 @@ private static Type GetFieldType(Type argumentType, ParameterInstance argument) return argumentType; } + } - private class SmartStringBuilder - { - private readonly string originalText; - private readonly StringBuilder builder; - - public SmartStringBuilder(string text) - { - originalText = text; - builder = new StringBuilder(text); - } - - public SmartStringBuilder Replace(string oldValue, string? newValue) - { - if (originalText.Contains(oldValue)) - builder.Replace(oldValue, newValue); - else - builder.Append($"\n// '{oldValue}' not found"); - return this; - } + internal class SmartStringBuilder(string text) + { + private readonly StringBuilder builder = new(text); - public override string ToString() => builder.ToString(); + public SmartStringBuilder Replace(string oldValue, string? newValue) + { + if (text.Contains(oldValue)) + builder.Replace(oldValue, newValue); + else + builder.Append($"\n// '{oldValue}' not found"); + return this; } + + public override string ToString() => builder.ToString(); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Code/DeclarationsProvider.cs b/src/BenchmarkDotNet/Code/DeclarationsProvider.cs index 9581b709d1..d4043fd60e 100644 --- a/src/BenchmarkDotNet/Code/DeclarationsProvider.cs +++ b/src/BenchmarkDotNet/Code/DeclarationsProvider.cs @@ -1,65 +1,321 @@ -using System.Reflection; +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Environments; using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; +using Perfolizer.Horology; namespace BenchmarkDotNet.Code { - internal abstract class DeclarationsProvider + internal abstract class DeclarationsProvider(BenchmarkCase benchmark) { - // "GlobalSetup" or "GlobalCleanup" methods are optional, so default to an empty delegate, so there is always something that can be invoked - private const string EmptyAction = "() => { }"; + protected static readonly string CoreReturnType = typeof(ValueTask).GetCorrectCSharpTypeName(); + protected static readonly string CoreParameters = $"long invokeCount, {typeof(IClock).GetCorrectCSharpTypeName()} clock"; + protected static readonly string StartClockSyncCode = $"{typeof(StartedClock).GetCorrectCSharpTypeName()} startedClock = {typeof(ClockExtensions).GetCorrectCSharpTypeName()}.Start(clock);"; + protected static readonly string ReturnSyncCode = $"return new {CoreReturnType}(startedClock.GetElapsed());"; + private static readonly string ReturnCompletedValueTask = $"return new {typeof(ValueTask).GetCorrectCSharpTypeName()}();"; - protected readonly Descriptor Descriptor; + protected BenchmarkCase Benchmark { get; } = benchmark; + protected Descriptor Descriptor => Benchmark.Descriptor; - internal DeclarationsProvider(Descriptor descriptor) => Descriptor = descriptor; + public SmartStringBuilder ReplaceTemplate(SmartStringBuilder smartStringBuilder) + { + Replace(smartStringBuilder, Descriptor.GlobalSetupMethod, "$GlobalSetupModifiers$", "$GlobalSetupImpl$", false); + Replace(smartStringBuilder, Descriptor.GlobalCleanupMethod, "$GlobalCleanupModifiers$", "$GlobalCleanupImpl$", true); + Replace(smartStringBuilder, Descriptor.IterationSetupMethod, "$IterationSetupModifiers$", "$IterationSetupImpl$", false); + Replace(smartStringBuilder, Descriptor.IterationCleanupMethod, "$IterationCleanupModifiers$", "$IterationCleanupImpl$", false); + return ReplaceCore(smartStringBuilder) + .Replace("$DisassemblerEntryMethodImpl$", GetWorkloadMethodCall(GetPassArgumentsDirect())) + .Replace("$OperationsPerInvoke$", Descriptor.OperationsPerInvoke.ToString()) + .Replace("$WorkloadTypeName$", Descriptor.Type.GetCorrectCSharpTypeName()); + } - public string OperationsPerInvoke => Descriptor.OperationsPerInvoke.ToString(); + private void Replace(SmartStringBuilder smartStringBuilder, MethodInfo? method, string replaceModifiers, string replaceImpl, bool isGlobalCleanup) + { + string modifier; + string impl; + if (method == null) + { + modifier = string.Empty; + impl = ReturnCompletedValueTask; + if (isGlobalCleanup) + { + impl = PrependExtraGlobalCleanupImpl(impl); + } + smartStringBuilder + .Replace(replaceModifiers, modifier) + .Replace(replaceImpl, impl); + return; + } - public string WorkloadTypeName => Descriptor.Type.GetCorrectCSharpTypeName(); + if (method.ReturnType.IsAwaitable()) + { + modifier = "async"; + impl = $"await {GetMethodPrefix(method)}.{method.Name}();"; + } + else + { + modifier = string.Empty; + impl = $""" + {GetMethodPrefix(method)}.{method.Name}(); + {ReturnCompletedValueTask} + """; + } + if (isGlobalCleanup) + { + impl = PrependExtraGlobalCleanupImpl(impl); + } + smartStringBuilder + .Replace(replaceModifiers, modifier) + .Replace(replaceImpl, impl); + } - public string GlobalSetupMethodName => GetMethodName(Descriptor.GlobalSetupMethod); + protected abstract string PrependExtraGlobalCleanupImpl(string impl); - public string GlobalCleanupMethodName => GetMethodName(Descriptor.GlobalCleanupMethod); + protected abstract SmartStringBuilder ReplaceCore(SmartStringBuilder smartStringBuilder); - public string IterationSetupMethodName => Descriptor.IterationSetupMethod?.Name ?? EmptyAction; + private static string GetMethodPrefix(MethodInfo method) + => method.IsStatic ? method.DeclaringType.GetCorrectCSharpTypeName() : "base"; - public string IterationCleanupMethodName => Descriptor.IterationCleanupMethod?.Name ?? EmptyAction; + protected string GetWorkloadMethodCall(string passArguments) + => $"{GetMethodPrefix(Descriptor.WorkloadMethod)}.{Descriptor.WorkloadMethod.Name}({passArguments});"; - public abstract string GetWorkloadMethodCall(string passArguments); + protected string GetPassArgumentsDirect() + => string.Join( + ", ", + Descriptor.WorkloadMethod.GetParameters() + .Select((parameter, index) => $"{CodeGenerator.GetParameterModifier(parameter)} this.__fieldsContainer.__argField{index}") + ); + } - protected static string GetMethodPrefix(MethodInfo method) - => method.IsStatic ? method.DeclaringType.GetCorrectCSharpTypeName() : "base"; + internal class SyncDeclarationsProvider(BenchmarkCase benchmark) : DeclarationsProvider(benchmark) + { + protected override string PrependExtraGlobalCleanupImpl(string impl) => impl; - private string GetMethodName(MethodInfo method) + protected override SmartStringBuilder ReplaceCore(SmartStringBuilder smartStringBuilder) { - if (method == null) - { - return EmptyAction; - } + string loadArguments = GetLoadArguments(); + string passArguments = GetPassArguments(); + string workloadMethodCall = GetWorkloadMethodCall(passArguments); + string coreImpl = $$""" + private unsafe {{CoreReturnType}} OverheadActionUnroll({{CoreParameters}}) + { + {{loadArguments}} + {{StartClockSyncCode}} + while (--invokeCount >= 0) + { + this.__Overhead({{passArguments}});@Unroll@ + } + {{ReturnSyncCode}} + } - if (method.ReturnType == typeof(Task) || - method.ReturnType == typeof(ValueTask) || - (method.ReturnType.IsGenericType && - (method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>) || - method.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)))) - { - return $"() => global::BenchmarkDotNet.Helpers.AwaitHelper.GetResult({GetMethodPrefix(Descriptor.WorkloadMethod)}.{method.Name}())"; - } + private unsafe {{CoreReturnType}} OverheadActionNoUnroll({{CoreParameters}}) + { + {{loadArguments}} + {{StartClockSyncCode}} + while (--invokeCount >= 0) + { + this.__Overhead({{passArguments}}); + } + {{ReturnSyncCode}} + } + + private unsafe {{CoreReturnType}} WorkloadActionUnroll({{CoreParameters}}) + { + {{loadArguments}} + {{StartClockSyncCode}} + while (--invokeCount >= 0) + { + {{workloadMethodCall}}@Unroll@ + } + {{ReturnSyncCode}} + } - return $"{GetMethodPrefix(Descriptor.WorkloadMethod)}.{method.Name}"; + private unsafe {{CoreReturnType}} WorkloadActionNoUnroll({{CoreParameters}}) + { + {{loadArguments}} + {{StartClockSyncCode}} + while (--invokeCount >= 0) + { + {{workloadMethodCall}} + } + {{ReturnSyncCode}} + } + """; + + return smartStringBuilder + .Replace("$ExtraFields$", string.Empty) + .Replace("$CoreImpl$", coreImpl); } - } - internal class SyncDeclarationsProvider(Descriptor descriptor) : DeclarationsProvider(descriptor) - { - public override string GetWorkloadMethodCall(string passArguments) - => $"{GetMethodPrefix(Descriptor.WorkloadMethod)}.{Descriptor.WorkloadMethod.Name}({passArguments})"; + private string GetLoadArguments() + => string.Join( + Environment.NewLine, + Descriptor.WorkloadMethod.GetParameters() + .Select((parameter, index) => + { + var refModifier = parameter.ParameterType.IsByRef ? "ref" : string.Empty; + return $"{refModifier} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index} = {refModifier} this.__fieldsContainer.__argField{index};"; + }) + ); + + private string GetPassArguments() + => string.Join( + ", ", + Descriptor.WorkloadMethod.GetParameters() + .Select((parameter, index) => $"{CodeGenerator.GetParameterModifier(parameter)} arg{index}") + ); } - internal class AsyncDeclarationsProvider(Descriptor descriptor) : DeclarationsProvider(descriptor) + internal class AsyncDeclarationsProvider(BenchmarkCase benchmark) : DeclarationsProvider(benchmark) { - public override string GetWorkloadMethodCall(string passArguments) - => $"global::BenchmarkDotNet.Helpers.AwaitHelper.GetResult({GetMethodPrefix(Descriptor.WorkloadMethod)}.{Descriptor.WorkloadMethod.Name}({passArguments}))"; + protected override string PrependExtraGlobalCleanupImpl(string impl) + => $""" + this.__fieldsContainer.workloadContinuerAndValueTaskSource?.Complete(); + {impl} + """; + + protected override SmartStringBuilder ReplaceCore(SmartStringBuilder smartStringBuilder) + { + // Unlike sync calls, async calls suffer from unrolling, so we multiply the invokeCount by the unroll factor and delegate the implementation to *NoUnroll methods. + int unrollFactor = Benchmark.Job.ResolveValue(RunMode.UnrollFactorCharacteristic, EnvironmentResolver.Instance); + string passArguments = GetPassArgumentsDirect(); + string workloadMethodCall = GetWorkloadMethodCall(passArguments); + bool hasAsyncMethodBuilderAttribute = TryGetAsyncMethodBuilderAttribute(out var asyncMethodBuilderAttribute); + Type workloadCoreReturnType = GetWorkloadCoreReturnType(hasAsyncMethodBuilderAttribute); + string finalReturn = GetFinalReturn(workloadCoreReturnType); + string coreImpl = $$""" + private {{CoreReturnType}} OverheadActionUnroll({{CoreParameters}}) + { + return this.OverheadActionNoUnroll(invokeCount * {{unrollFactor}}, clock); + } + + private {{CoreReturnType}} OverheadActionNoUnroll({{CoreParameters}}) + { + {{StartClockSyncCode}} + while (--invokeCount >= 0) + { + this.__Overhead({{passArguments}}); + } + {{ReturnSyncCode}} + } + + private {{CoreReturnType}} WorkloadActionUnroll({{CoreParameters}}) + { + return this.WorkloadActionNoUnroll(invokeCount * {{unrollFactor}}, clock); + } + + private {{CoreReturnType}} WorkloadActionNoUnroll({{CoreParameters}}) + { + this.__fieldsContainer.invokeCount = invokeCount; + this.__fieldsContainer.clock = clock; + if (this.__fieldsContainer.workloadContinuerAndValueTaskSource == null) + { + this.__fieldsContainer.workloadContinuerAndValueTaskSource = new {{typeof(WorkloadContinuerAndValueTaskSource).GetCorrectCSharpTypeName()}}(); + this.__StartWorkload(); + } + return this.__fieldsContainer.workloadContinuerAndValueTaskSource.Continue(); + } + + private async void __StartWorkload() + { + await __WorkloadCore(); + } + + {{asyncMethodBuilderAttribute}} + private async {{workloadCoreReturnType.GetCorrectCSharpTypeName()}} __WorkloadCore() + { + try + { + while (true) + { + await this.__fieldsContainer.workloadContinuerAndValueTaskSource; + if (this.__fieldsContainer.workloadContinuerAndValueTaskSource.IsCompleted) + { + {{finalReturn}} + } + + {{typeof(StartedClock).GetCorrectCSharpTypeName()}} startedClock = {{typeof(ClockExtensions).GetCorrectCSharpTypeName()}}.Start(this.__fieldsContainer.clock); + while (--this.__fieldsContainer.invokeCount >= 0) + { + // Necessary because of error CS4004: Cannot await in an unsafe context + {{Descriptor.WorkloadMethod.ReturnType.GetCorrectCSharpTypeName()}} awaitable; + unsafe { awaitable = {{workloadMethodCall}} } + await awaitable; + } + this.__fieldsContainer.workloadContinuerAndValueTaskSource.SetResult(startedClock.GetElapsed()); + } + } + catch (global::System.Exception e) + { + __fieldsContainer.workloadContinuerAndValueTaskSource.SetException(e); + {{finalReturn}} + } + } + """; + + return smartStringBuilder + .Replace("$ExtraFields$", $""" + public {typeof(WorkloadContinuerAndValueTaskSource).GetCorrectCSharpTypeName()} workloadContinuerAndValueTaskSource; + public global::Perfolizer.Horology.IClock clock; + public long invokeCount; + """) + .Replace("$CoreImpl$", coreImpl); + } + + private bool TryGetAsyncMethodBuilderAttribute(out string asyncMethodBuilderAttribute) + { + asyncMethodBuilderAttribute = string.Empty; + if (Descriptor.WorkloadMethod.HasAttribute()) + { + return false; + } + if (Descriptor.WorkloadMethod.GetAsyncMethodBuilderAttribute() is not { } attr) + { + return false; + } + if (attr.GetType().GetProperty(nameof(AsyncMethodBuilderAttribute.BuilderType), BindingFlags.Public | BindingFlags.Instance)?.GetValue(attr) is not Type builderType) + { + return false; + } + asyncMethodBuilderAttribute = $"[{typeof(AsyncMethodBuilderAttribute).GetCorrectCSharpTypeName()}(typeof({builderType.GetCorrectCSharpTypeName()}))]"; + return true; + } + + private Type GetWorkloadCoreReturnType(bool hasAsyncMethodBuilderAttribute) + { + if (Descriptor.WorkloadMethod.ResolveAttribute() is { } asyncCallerTypeAttribute) + { + return asyncCallerTypeAttribute.AsyncCallerType; + } + if (hasAsyncMethodBuilderAttribute + || Descriptor.WorkloadMethod.ReturnType.HasAsyncMethodBuilderAttribute() + // Task and Task are not annotated with their builder type, the C# compiler special-cases them. + || (Descriptor.WorkloadMethod.ReturnType.IsGenericType && Descriptor.WorkloadMethod.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) + ) + { + return Descriptor.WorkloadMethod.ReturnType; + } + // Fallback to Task if the benchmark return type is Task or any awaitable type that is not a custom task-like type. + return typeof(Task); + } + + private static string GetFinalReturn(Type workloadCoreReturnType) + { + var finalReturnType = workloadCoreReturnType + .GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance) + .ReturnType + .GetMethod(nameof(TaskAwaiter.GetResult)) + .ReturnType; + return finalReturnType == typeof(void) + ? "return;" + : $"return default({finalReturnType.GetCorrectCSharpTypeName()});"; + } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs new file mode 100644 index 0000000000..b53b99de64 --- /dev/null +++ b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs @@ -0,0 +1,76 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; + +namespace BenchmarkDotNet.Engines; + +// Used to ensure async continuations are posted back to the same thread that the benchmark process was started on. +[UsedImplicitly] +[EditorBrowsable(EditorBrowsableState.Never)] +public sealed class BenchmarkSynchronizationContext : SynchronizationContext, IDisposable +{ + private readonly SynchronizationContext previousContext; + private readonly Queue<(SendOrPostCallback d, object? state)> queue = new(); + + private BenchmarkSynchronizationContext(SynchronizationContext previousContext) + { + this.previousContext = previousContext; + } + + public override SynchronizationContext CreateCopy() + => this; + + public override void Post(SendOrPostCallback d, object? state) + => queue.Enqueue((d ?? throw new ArgumentNullException(nameof(d)), state)); + + public static BenchmarkSynchronizationContext CreateAndSetCurrent() + { + var context = new BenchmarkSynchronizationContext(Current); + SetSynchronizationContext(context); + return context; + } + + public void Dispose() + => SetSynchronizationContext(previousContext); + + public void ExecuteUntilComplete(ValueTask valueTask) + { + var spinner = new SpinWait(); + while (!valueTask.IsCompleted) + { + DoSpin(ref spinner); + } + valueTask.GetAwaiter().GetResult(); + } + + public T ExecuteUntilComplete(ValueTask valueTask) + { + var spinner = new SpinWait(); + while (!valueTask.IsCompleted) + { + DoSpin(ref spinner); + } + return valueTask.GetAwaiter().GetResult(); + } + + private void DoSpin(ref SpinWait spinner) + { + if (queue.Count <= 0) + { + spinner.SpinOnce(); + return; + } + + do + { + var (d, state) = queue.Dequeue(); + d(state); + } + while (queue.Count > 0); + // Reset spinner after any posted callback is executed. + spinner = new(); + } +} diff --git a/src/BenchmarkDotNet/Engines/Engine.cs b/src/BenchmarkDotNet/Engines/Engine.cs index 4a9aef7792..e7e648ff31 100644 --- a/src/BenchmarkDotNet/Engines/Engine.cs +++ b/src/BenchmarkDotNet/Engines/Engine.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes.CompilerServices; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; @@ -12,6 +14,9 @@ namespace BenchmarkDotNet.Engines { + // MethodImplOptions.AggressiveOptimization is applied to all methods to force them to go straight to tier1 JIT, + // eliminating tiered JIT as a potential variable in measurements. + [AggressivelyOptimizeMethods] [UsedImplicitly] public class Engine : IEngine { @@ -59,13 +64,13 @@ internal Engine(EngineParameters engineParameters) random = new Random(12345); // we are using constant seed to try to get repeatable results } - public RunResults Run() + public async ValueTask RunAsync() { - Parameters.GlobalSetupAction.Invoke(); + await Parameters.GlobalSetupAction.Invoke(); bool didThrow = false; try { - return RunCore(); + return await RunCore(); } catch { @@ -76,7 +81,7 @@ public RunResults Run() { try { - Parameters.GlobalCleanupAction.Invoke(); + await Parameters.GlobalCleanupAction.Invoke(); } // We only catch if the benchmark threw to not overwrite the exception. #1045 catch (Exception e) when (didThrow) @@ -86,17 +91,15 @@ public RunResults Run() } } - // AggressiveOptimization forces the method to go straight to tier1 JIT, and will never be re-jitted, - // eliminating tiered JIT as a potential variable in measurements. - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private RunResults RunCore() + // This method is extra long because the helper methods were inlined in order to prevent extra async allocations on each iteration. + private async ValueTask RunCore() { var measurements = new List(); if (EngineEventSource.Log.IsEnabled()) EngineEventSource.Log.BenchmarkStart(Parameters.BenchmarkName); - IterationData extraStatsIterationData = default; + IterationData extraIterationData = default; // Enumerate the stages and run iterations in a loop to ensure each benchmark invocation is called with a constant stack size. // #1120 foreach (var stage in EngineStage.EnumerateStages(Parameters)) @@ -110,10 +113,42 @@ private RunResults RunCore() var stageMeasurements = stage.GetMeasurementList(); while (stage.GetShouldRunIteration(stageMeasurements, out var iterationData)) { - var measurement = RunIteration(iterationData); + // Initialization + long invokeCount = iterationData.invokeCount; + int unrollFactor = iterationData.unrollFactor; + if (invokeCount % unrollFactor != 0) + throw new ArgumentOutOfRangeException(nameof(iterationData), $"InvokeCount({invokeCount}) should be a multiple of UnrollFactor({unrollFactor})."); + + long totalOperations = invokeCount * Parameters.OperationsPerInvoke; + bool randomizeMemory = iterationData.mode == IterationMode.Workload && MemoryRandomization; + + await iterationData.setupAction(); + + GcCollect(); + + if (EngineEventSource.Log.IsEnabled()) + EngineEventSource.Log.IterationStart(iterationData.mode, iterationData.stage, totalOperations); + + var clockSpan = randomizeMemory + ? await MeasureWithRandomStack(iterationData.workloadAction, invokeCount / unrollFactor) + : await iterationData.workloadAction(invokeCount / unrollFactor, Clock); + + if (EngineEventSource.Log.IsEnabled()) + EngineEventSource.Log.IterationStop(iterationData.mode, iterationData.stage, totalOperations); + + await iterationData.cleanupAction(); + + if (randomizeMemory) + await RandomizeManagedHeapMemory(); + + GcCollect(); + + // Results + var measurement = new Measurement(0, iterationData.mode, iterationData.stage, iterationData.index, totalOperations, clockSpan.GetNanoseconds()); + Host.WriteLine(measurement.ToString()); stageMeasurements.Add(measurement); // Actual Workload is always the last stage, so we use the same data to run extra stats. - extraStatsIterationData = iterationData; + extraIterationData = iterationData; } measurements.AddRange(stageMeasurements); @@ -129,119 +164,65 @@ private RunResults RunCore() GcStats workGcHasDone = default; if (Parameters.RunExtraIteration) { - (workGcHasDone, var extraMeasurement) = RunExtraIteration(extraStatsIterationData); - measurements.Add(extraMeasurement); - } + // Warm up the GC measurement functions before starting the actual measurement. + DeadCodeEliminationHelper.KeepAliveWithoutBoxing(GcStats.ReadInitial()); + DeadCodeEliminationHelper.KeepAliveWithoutBoxing(GcStats.ReadFinal()); - if (EngineEventSource.Log.IsEnabled()) - EngineEventSource.Log.BenchmarkStop(Parameters.BenchmarkName); + await extraIterationData.setupAction(); // we run iteration setup first, so even if it allocates, it is not included in the results - var outlierMode = TargetJob.ResolveValue(AccuracyMode.OutlierModeCharacteristic, Resolver); + Host.SendSignal(HostSignal.BeforeExtraIteration); + Parameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.BeforeExtraIteration); - return new RunResults(measurements, outlierMode, workGcHasDone); - } + // GC collect before measuring allocations. + ForceGcCollect(); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private Measurement RunIteration(IterationData data) - { - // Initialization - long invokeCount = data.invokeCount; - int unrollFactor = data.unrollFactor; - if (invokeCount % unrollFactor != 0) - throw new ArgumentOutOfRangeException(nameof(data), $"InvokeCount({invokeCount}) should be a multiple of UnrollFactor({unrollFactor})."); + // #1542 + // If the jit is tiered, we put the current thread to sleep so it can kick in, compile its stuff, + // and NOT allocate anything on the background thread when we are measuring allocations. + SleepIfPositive(JitInfo.BackgroundCompilationDelay); - long totalOperations = invokeCount * Parameters.OperationsPerInvoke; - bool randomizeMemory = data.mode == IterationMode.Workload && MemoryRandomization; + GcStats gcStats; + ClockSpan clockSpan; + using (FinalizerBlocker.MaybeStart()) + { + (gcStats, clockSpan) = await MeasureWithGc(extraIterationData.workloadAction, extraIterationData.invokeCount / extraIterationData.unrollFactor); + } - data.setupAction(); + Parameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterExtraIteration); + Host.SendSignal(HostSignal.AfterExtraIteration); - GcCollect(); + await extraIterationData.cleanupAction(); // we run iteration cleanup after diagnosers are complete. - if (EngineEventSource.Log.IsEnabled()) - EngineEventSource.Log.IterationStart(data.mode, data.stage, totalOperations); - - var clockSpan = randomizeMemory - ? MeasureWithRandomStack(data.workloadAction, invokeCount / unrollFactor) - : Measure(data.workloadAction, invokeCount / unrollFactor); + var totalOperations = extraIterationData.invokeCount * Parameters.OperationsPerInvoke; + var measurement = new Measurement(0, IterationMode.Workload, IterationStage.Extra, 1, totalOperations, clockSpan.GetNanoseconds()); + Host.WriteLine(measurement.ToString()); + workGcHasDone = gcStats.WithTotalOperations(totalOperations); + measurements.Add(measurement); + } if (EngineEventSource.Log.IsEnabled()) - EngineEventSource.Log.IterationStop(data.mode, data.stage, totalOperations); - - data.cleanupAction(); - - if (randomizeMemory) - RandomizeManagedHeapMemory(); + EngineEventSource.Log.BenchmarkStop(Parameters.BenchmarkName); - GcCollect(); + var outlierMode = TargetJob.ResolveValue(AccuracyMode.OutlierModeCharacteristic, Resolver); - // Results - var measurement = new Measurement(0, data.mode, data.stage, data.index, totalOperations, clockSpan.GetNanoseconds()); - Host.WriteLine(measurement.ToString()); - return measurement; + return new RunResults(measurements, outlierMode, workGcHasDone); } // This is in a separate method, because stackalloc can affect code alignment, // resulting in unexpected measurements on some AMD cpus, // even if the stackalloc branch isn't executed. (#2366) - [MethodImpl(MethodImplOptions.NoInlining | CodeGenHelper.AggressiveOptimizationOption)] - private unsafe ClockSpan MeasureWithRandomStack(Action action, long invokeCount) + [MethodImpl(MethodImplOptions.NoInlining)] + private unsafe ValueTask MeasureWithRandomStack(Func> action, long invokeCount) { byte* stackMemory = stackalloc byte[random.Next(32)]; - var clockSpan = Measure(action, invokeCount); + var task = action(invokeCount, Clock); Consume(stackMemory); - return clockSpan; + return task; } - [MethodImpl(MethodImplOptions.NoInlining | CodeGenHelper.AggressiveOptimizationOption)] + [MethodImpl(MethodImplOptions.NoInlining)] private unsafe void Consume(byte* _) { } - [MethodImpl(MethodImplOptions.NoInlining | CodeGenHelper.AggressiveOptimizationOption)] - private ClockSpan Measure(Action action, long invokeCount) - { - var clock = Clock.Start(); - action(invokeCount); - return clock.GetElapsed(); - } - - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private (GcStats, Measurement) RunExtraIteration(IterationData data) - { - // Warm up the GC measurement functions before starting the actual measurement. - DeadCodeEliminationHelper.KeepAliveWithoutBoxing(GcStats.ReadInitial()); - DeadCodeEliminationHelper.KeepAliveWithoutBoxing(GcStats.ReadFinal()); - - data.setupAction(); // we run iteration setup first, so even if it allocates, it is not included in the results - - Host.SendSignal(HostSignal.BeforeExtraIteration); - Parameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.BeforeExtraIteration); - - // GC collect before measuring allocations. - ForceGcCollect(); - - // #1542 - // If the jit is tiered, we put the current thread to sleep so it can kick in, compile its stuff, - // and NOT allocate anything on the background thread when we are measuring allocations. - SleepIfPositive(JitInfo.BackgroundCompilationDelay); - - GcStats gcStats; - ClockSpan clockSpan; - using (FinalizerBlocker.MaybeStart()) - { - (gcStats, clockSpan) = MeasureWithGc(data.workloadAction, data.invokeCount / data.unrollFactor); - } - - Parameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterExtraIteration); - Host.SendSignal(HostSignal.AfterExtraIteration); - - data.cleanupAction(); // we run iteration cleanup after diagnosers are complete. - - var totalOperations = data.invokeCount * Parameters.OperationsPerInvoke; - var measurement = new Measurement(0, IterationMode.Workload, IterationStage.Extra, 1, totalOperations, clockSpan.GetNanoseconds()); - Host.WriteLine(measurement.ToString()); - return (gcStats.WithTotalOperations(totalOperations), measurement); - } - - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] internal static void SleepIfPositive(TimeSpan timeSpan) { if (timeSpan > TimeSpan.Zero) @@ -250,28 +231,27 @@ internal static void SleepIfPositive(TimeSpan timeSpan) } } - // Isolate the allocation measurement and skip tier0 jit to make sure we don't get any unexpected allocations. - [MethodImpl(MethodImplOptions.NoInlining | CodeGenHelper.AggressiveOptimizationOption)] - private (GcStats, ClockSpan) MeasureWithGc(Action action, long invokeCount) + // Isolate the allocation measurement to make sure we don't get any unexpected allocations. + [MethodImpl(MethodImplOptions.NoInlining)] + private async ValueTask<(GcStats, ClockSpan)> MeasureWithGc(Func> action, long invokeCount) { var initialGcStats = GcStats.ReadInitial(); - var clockSpan = Measure(action, invokeCount); + var clockSpan = await action(invokeCount, Clock); var finalGcStats = GcStats.ReadFinal(); return (finalGcStats - initialGcStats, clockSpan); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void RandomizeManagedHeapMemory() + private async ValueTask RandomizeManagedHeapMemory() { // invoke global cleanup before global setup - Parameters.GlobalCleanupAction.Invoke(); + await Parameters.GlobalCleanupAction.Invoke(); var gen0object = new byte[random.Next(32)]; var lohObject = new byte[85 * 1024 + random.Next(32)]; // we expect the key allocations to happen in global setup (not ctor) // so we call it while keeping the random-size objects alive - Parameters.GlobalSetupAction.Invoke(); + await Parameters.GlobalSetupAction.Invoke(); GC.KeepAlive(gen0object); GC.KeepAlive(lohObject); @@ -279,7 +259,6 @@ private void RandomizeManagedHeapMemory() // we don't enforce GC.Collects here as engine does it later anyway } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] private void GcCollect() { if (!ForceGcCleanups) @@ -288,7 +267,6 @@ private void GcCollect() ForceGcCollect(); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] internal static void ForceGcCollect() { GC.Collect(); @@ -314,10 +292,8 @@ public static class Signals private static readonly Dictionary MessagesToSignals = SignalsToMessages.ToDictionary(p => p.Value, p => p.Key); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] public static string ToMessage(HostSignal signal) => SignalsToMessages[signal]; - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] public static bool TryGetSignal(string message, out HostSignal signal) => MessagesToSignals.TryGetValue(message, out signal); } @@ -343,7 +319,6 @@ private sealed class Impl private readonly object hangLock = new(); private readonly ManualResetEventSlim enteredFinalizerEvent = new(false); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] ~Impl() { lock (hangLock) @@ -353,7 +328,7 @@ private sealed class Impl } } - [MethodImpl(MethodImplOptions.NoInlining | CodeGenHelper.AggressiveOptimizationOption)] + [MethodImpl(MethodImplOptions.NoInlining)] internal static (object hangLock, ManualResetEventSlim enteredFinalizerEvent) CreateWeakly() { var impl = new Impl(); @@ -361,7 +336,6 @@ internal static (object hangLock, ManualResetEventSlim enteredFinalizerEvent) Cr } } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] internal static FinalizerBlocker MaybeStart() { if (Environment.GetEnvironmentVariable(UnitTestBlockFinalizerEnvKey) != UnitTestBlockFinalizerEnvValue) @@ -378,7 +352,6 @@ internal static FinalizerBlocker MaybeStart() return new FinalizerBlocker(hangLock); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] public void Dispose() { if (hangLock is not null) diff --git a/src/BenchmarkDotNet/Engines/EngineJitStage.cs b/src/BenchmarkDotNet/Engines/EngineJitStage.cs index 37d519842f..358f62ddfd 100644 --- a/src/BenchmarkDotNet/Engines/EngineJitStage.cs +++ b/src/BenchmarkDotNet/Engines/EngineJitStage.cs @@ -7,15 +7,11 @@ namespace BenchmarkDotNet.Engines; -internal abstract class EngineJitStage(EngineParameters parameters) : EngineStage(IterationStage.Jitting, IterationMode.Workload, parameters) -{ -} - // We do our best to encourage the jit to fully promote methods to tier1, but tiered jit relies on heuristics, // and we purposefully don't spend too much time in this stage, so we can't guarantee it. // This should succeed for 99%+ of microbenchmarks. For any sufficiently short benchmarks where this fails, // the following stages (Pilot and Warmup) will likely take it the rest of the way. Long-running benchmarks may never fully reach tier1. -internal sealed class EngineFirstJitStage : EngineJitStage +internal sealed class EngineJitStage : EngineStage { // It is not worth spending a long time in jit stage for macro-benchmarks. private static readonly TimeInterval MaxTieringTime = TimeInterval.FromSeconds(10); @@ -29,7 +25,7 @@ internal sealed class EngineFirstJitStage : EngineJitStage private readonly IEnumerator enumerator; private readonly bool evaluateOverhead; - internal EngineFirstJitStage(bool evaluateOverhead, EngineParameters parameters) : base(parameters) + internal EngineJitStage(bool evaluateOverhead, EngineParameters parameters) : base(IterationStage.Jitting, IterationMode.Workload, parameters) { enumerator = EnumerateIterations(); this.evaluateOverhead = evaluateOverhead; @@ -44,7 +40,7 @@ private int GetMaxMeasurementCount() : 1; if (evaluateOverhead) { - count *= 2; + count += 1; } return count; } @@ -111,10 +107,7 @@ private IEnumerator EnumerateIterations() remainingCalls -= invokeCount; ++iterationIndex; - if (evaluateOverhead) - { - yield return GetOverheadIterationData(invokeCount); - } + // The generated __Overhead method is aggressively optimized, so we don't need to run it again. yield return GetWorkloadIterationData(invokeCount); if ((remainingTiers + remainingCalls) > 0 @@ -131,44 +124,12 @@ private IEnumerator EnumerateIterations() // Empirical evidence shows that the first call after the method is tiered up may take longer, // so we run an extra iteration to ensure the next stage gets a stable measurement. ++iterationIndex; - if (evaluateOverhead) - { - yield return GetOverheadIterationData(1); - } yield return GetWorkloadIterationData(1); } private IterationData GetOverheadIterationData(long invokeCount) - => new(IterationMode.Overhead, IterationStage.Jitting, iterationIndex, invokeCount, 1, () => { }, () => { }, parameters.OverheadActionNoUnroll); + => new(IterationMode.Overhead, IterationStage.Jitting, iterationIndex, invokeCount, 1, () => new(), () => new(), parameters.OverheadActionNoUnroll); private IterationData GetWorkloadIterationData(long invokeCount) => new(IterationMode.Workload, IterationStage.Jitting, iterationIndex, invokeCount, 1, parameters.IterationSetupAction, parameters.IterationCleanupAction, parameters.WorkloadActionNoUnroll); -} - -internal sealed class EngineSecondJitStage : EngineJitStage -{ - private readonly int unrollFactor; - private readonly bool evaluateOverhead; - - public EngineSecondJitStage(int unrollFactor, bool evaluateOverhead, EngineParameters parameters) : base(parameters) - { - this.unrollFactor = unrollFactor; - this.evaluateOverhead = evaluateOverhead; - iterationIndex = evaluateOverhead ? 0 : 2; - } - - internal override List GetMeasurementList() => new(evaluateOverhead ? 2 : 1); - - // The benchmark method has already been jitted via *NoUnroll, we only need to jit the *Unroll methods here, which aren't tiered. - internal override bool GetShouldRunIteration(List measurements, out IterationData iterationData) - { - iterationData = ++iterationIndex switch - { - 1 => new(IterationMode.Overhead, IterationStage.Jitting, 1, unrollFactor, unrollFactor, () => { }, () => { }, parameters.OverheadActionUnroll), - // IterationSetup/Cleanup are only used for *NoUnroll benchmarks - 2 => new(IterationMode.Workload, IterationStage.Jitting, 1, unrollFactor, unrollFactor, () => { }, () => { }, parameters.WorkloadActionUnroll), - _ => default - }; - return iterationIndex <= 2; - } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Engines/EngineParameters.cs b/src/BenchmarkDotNet/Engines/EngineParameters.cs index 878cbf1d0e..da6954f44e 100644 --- a/src/BenchmarkDotNet/Engines/EngineParameters.cs +++ b/src/BenchmarkDotNet/Engines/EngineParameters.cs @@ -1,7 +1,9 @@ using System; +using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; +using Perfolizer.Horology; namespace BenchmarkDotNet.Engines { @@ -11,16 +13,16 @@ public class EngineParameters public IResolver Resolver { get; set; } = DefaultResolver; public IHost Host { get; set; } - public Action WorkloadActionNoUnroll { get; set; } - public Action WorkloadActionUnroll { get; set; } - public Action OverheadActionNoUnroll { get; set; } - public Action OverheadActionUnroll { get; set; } + public Func> WorkloadActionNoUnroll { get; set; } + public Func> WorkloadActionUnroll { get; set; } + public Func> OverheadActionNoUnroll { get; set; } + public Func> OverheadActionUnroll { get; set; } public Job TargetJob { get; set; } = Job.Default; public long OperationsPerInvoke { get; set; } = 1; - public Action GlobalSetupAction { get; set; } - public Action GlobalCleanupAction { get; set; } - public Action IterationSetupAction { get; set; } - public Action IterationCleanupAction { get; set; } + public Func GlobalSetupAction { get; set; } + public Func GlobalCleanupAction { get; set; } + public Func IterationSetupAction { get; set; } + public Func IterationCleanupAction { get; set; } public bool RunExtraIteration { get; set; } public string BenchmarkName { get; set; } public Diagnosers.CompositeInProcessDiagnoserHandler InProcessDiagnoserHandler { get; set; } diff --git a/src/BenchmarkDotNet/Engines/EngineStage.cs b/src/BenchmarkDotNet/Engines/EngineStage.cs index 8bd031112f..7598297ec3 100644 --- a/src/BenchmarkDotNet/Engines/EngineStage.cs +++ b/src/BenchmarkDotNet/Engines/EngineStage.cs @@ -33,7 +33,7 @@ internal static IEnumerable EnumerateStages(EngineParameters parame int minInvokeCount = parameters.TargetJob.ResolveValue(AccuracyMode.MinInvokeCountCharacteristic, parameters.Resolver); // AOT technically doesn't have a JIT, but we run jit stage regardless because of static constructors. #2004 - var jitStage = new EngineFirstJitStage(evaluateOverhead, parameters); + var jitStage = new EngineJitStage(evaluateOverhead, parameters); yield return jitStage; bool hasUnrollFactor = parameters.TargetJob.HasValue(RunMode.UnrollFactorCharacteristic); @@ -58,13 +58,6 @@ internal static IEnumerable EnumerateStages(EngineParameters parame skipPilotStage = !pilotStage.needsFurtherPilot; } - // The first jit stage only jitted *NoUnroll methods, now we need to jit *Unroll methods if they're going to be used. - // TODO: This stage can be removed after we refactor the engine/codegen to pass the clock into the delegates. - if (!RuntimeInformation.IsAot && unrollFactor != 1) - { - yield return new EngineSecondJitStage(unrollFactor, evaluateOverhead, parameters); - } - if (!skipPilotStage) { var pilotStage = EnginePilotStage.GetStage(invokeCount, unrollFactor, minInvokeCount, parameters); diff --git a/src/BenchmarkDotNet/Engines/IEngine.cs b/src/BenchmarkDotNet/Engines/IEngine.cs index 9754d58354..a877c82d38 100644 --- a/src/BenchmarkDotNet/Engines/IEngine.cs +++ b/src/BenchmarkDotNet/Engines/IEngine.cs @@ -1,6 +1,8 @@ -namespace BenchmarkDotNet.Engines; +using System.Threading.Tasks; + +namespace BenchmarkDotNet.Engines; public interface IEngine { - RunResults Run(); + ValueTask RunAsync(); } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Engines/IterationData.cs b/src/BenchmarkDotNet/Engines/IterationData.cs index f95f686922..de6efdc759 100644 --- a/src/BenchmarkDotNet/Engines/IterationData.cs +++ b/src/BenchmarkDotNet/Engines/IterationData.cs @@ -1,17 +1,18 @@ -using System; +using Perfolizer.Horology; +using System; +using System.Threading.Tasks; -namespace BenchmarkDotNet.Engines +namespace BenchmarkDotNet.Engines; + +internal readonly struct IterationData(IterationMode iterationMode, IterationStage iterationStage, int index, long invokeCount, int unrollFactor, + Func setupAction, Func cleanupAction, Func> workloadAction) { - internal readonly struct IterationData(IterationMode iterationMode, IterationStage iterationStage, int index, long invokeCount, int unrollFactor, - Action setupAction, Action cleanupAction, Action workloadAction) - { - public readonly IterationMode mode = iterationMode; - public readonly IterationStage stage = iterationStage; - public readonly int index = index; - public readonly long invokeCount = invokeCount; - public readonly int unrollFactor = unrollFactor; - public readonly Action setupAction = setupAction; - public readonly Action cleanupAction = cleanupAction; - public readonly Action workloadAction = workloadAction; - } + public readonly IterationMode mode = iterationMode; + public readonly IterationStage stage = iterationStage; + public readonly int index = index; + public readonly long invokeCount = invokeCount; + public readonly int unrollFactor = unrollFactor; + public readonly Func setupAction = setupAction; + public readonly Func cleanupAction = cleanupAction; + public readonly Func> workloadAction = workloadAction; } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Engines/WorkloadContinuerAndValueTaskSource.cs b/src/BenchmarkDotNet/Engines/WorkloadContinuerAndValueTaskSource.cs new file mode 100644 index 0000000000..e1749501df --- /dev/null +++ b/src/BenchmarkDotNet/Engines/WorkloadContinuerAndValueTaskSource.cs @@ -0,0 +1,67 @@ +using JetBrains.Annotations; +using Perfolizer.Horology; +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using System.Threading.Tasks.Sources; + +namespace BenchmarkDotNet.Engines; + +// This is used to prevent allocating a new async state machine on every benchmark iteration. +[UsedImplicitly] +[EditorBrowsable(EditorBrowsableState.Never)] +public sealed class WorkloadContinuerAndValueTaskSource : ICriticalNotifyCompletion, IValueTaskSource +{ + private static readonly Action s_completedSentinel = CompletedMethod; + private static void CompletedMethod() => throw new InvalidOperationException(); + + private Action? continuation; + private ManualResetValueTaskSourceCore valueTaskSourceCore; + + public ValueTask Continue() + { + valueTaskSourceCore.Reset(); + var callback = continuation; + continuation = null; + callback?.Invoke(); + return new(this, valueTaskSourceCore.Version); + } + + public void Complete() + { + var callback = continuation; + continuation = s_completedSentinel; + callback?.Invoke(); + } + + public void SetResult(ClockSpan result) + => valueTaskSourceCore.SetResult(result); + + public void SetException(Exception exception) + => valueTaskSourceCore.SetException(exception); + + // Await infrastructure + public WorkloadContinuerAndValueTaskSource GetAwaiter() + => this; + + public void OnCompleted(Action continuation) + => UnsafeOnCompleted(continuation); + + public void UnsafeOnCompleted(Action continuation) + => this.continuation = continuation; + + public bool IsCompleted + => continuation == s_completedSentinel; + + public void GetResult() { } + + ClockSpan IValueTaskSource.GetResult(short token) + => valueTaskSourceCore.GetResult(token); + + ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) + => valueTaskSourceCore.GetStatus(token); + + void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) + => valueTaskSourceCore.OnCompleted(continuation, state, token, flags); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs b/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs index c88d86565b..eea6614512 100644 --- a/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/ReflectionExtensions.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; namespace BenchmarkDotNet.Extensions @@ -241,5 +242,32 @@ internal static bool IsByRefLike(this Type type) #else => type.IsByRefLike; #endif + + internal static bool IsAwaitable(this Type type) + { + // This does not handle await extension. + var awaiterType = type.GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance)?.ReturnType; + if (awaiterType is null) + { + return false; + } + if (awaiterType.GetMethod(nameof(TaskAwaiter.GetResult), BindingFlags.Public | BindingFlags.Instance) is null) + { + return false; + } + var isCompletedProperty = awaiterType.GetProperty(nameof(TaskAwaiter.IsCompleted), BindingFlags.Public | BindingFlags.Instance); + if (isCompletedProperty?.PropertyType != typeof(bool)) + { + return false; + } + return awaiterType.GetInterfaces().Any(type => typeof(INotifyCompletion).IsAssignableFrom(type)); + } + + internal static Attribute? GetAsyncMethodBuilderAttribute(this MemberInfo memberInfo) + // AsyncMethodBuilderAttribute can come from any assembly, so we need to use reflection by name instead of searching for the exact type. + => memberInfo.GetCustomAttributes(false).FirstOrDefault(attr => attr.GetType().FullName == typeof(AsyncMethodBuilderAttribute).FullName) as Attribute; + + internal static bool HasAsyncMethodBuilderAttribute(this MemberInfo memberInfo) + => memberInfo.GetAsyncMethodBuilderAttribute() != null; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Helpers/AwaitHelper.cs b/src/BenchmarkDotNet/Helpers/AwaitHelper.cs index 98837a7ea6..469f6c772e 100644 --- a/src/BenchmarkDotNet/Helpers/AwaitHelper.cs +++ b/src/BenchmarkDotNet/Helpers/AwaitHelper.cs @@ -7,7 +7,7 @@ namespace BenchmarkDotNet.Helpers { - public static class AwaitHelper + internal static class AwaitHelper { private class ValueTaskWaiter { @@ -80,7 +80,7 @@ internal static MethodInfo GetGetResultMethod(Type taskType) { if (!taskType.IsGenericType) { - return typeof(AwaitHelper).GetMethod(nameof(AwaitHelper.GetResult), BindingFlags.Public | BindingFlags.Static, null, new Type[1] { taskType }, null); + return typeof(AwaitHelper).GetMethod(nameof(GetResult), BindingFlags.Public | BindingFlags.Static, null, [taskType], null); } Type compareType = taskType.GetGenericTypeDefinition() == typeof(ValueTask<>) ? typeof(ValueTask<>) @@ -98,11 +98,11 @@ internal static MethodInfo GetGetResultMethod(Type taskType) return typeof(AwaitHelper).GetMethods(BindingFlags.Public | BindingFlags.Static) .First(m => { - if (m.Name != nameof(AwaitHelper.GetResult)) return false; + if (m.Name != nameof(GetResult)) return false; Type paramType = m.GetParameters().First().ParameterType; return paramType.IsGenericType && paramType.GetGenericTypeDefinition() == compareType; }) - .MakeGenericMethod(new[] { resultType }); + .MakeGenericMethod([resultType]); } } } diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorDefaultValueExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorDefaultValueExtensions.cs new file mode 100644 index 0000000000..1b0ba448e5 --- /dev/null +++ b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorDefaultValueExtensions.cs @@ -0,0 +1,152 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace BenchmarkDotNet.Helpers.Reflection.Emit; + +internal static class IlGeneratorDefaultValueExtensions +{ + public static void EmitSetFieldToDefault(this ILGenerator ilBuilder, FieldInfo fieldInfo) + { + var resultType = fieldInfo.FieldType; + switch (resultType) + { + case null: + case Type t when t == typeof(void): + throw new ArgumentException(); + case Type t when t.IsValueType: + ilBuilder.Emit(OpCodes.Ldflda, fieldInfo); + ilBuilder.Emit(OpCodes.Initobj, resultType); + break; + default: + ilBuilder.Emit(OpCodes.Ldnull); + ilBuilder.Emit(OpCodes.Stfld, fieldInfo); + break; + } + } + + public static void MaybeEmitSetLocalToDefault(this ILGenerator ilBuilder, LocalBuilder? optionalLocal) + { + var resultType = optionalLocal?.LocalType; + switch (resultType) + { + case null: + case Type t when t == typeof(void): + break; + case Type t when t.IsClass || t.IsInterface: + ilBuilder.Emit(OpCodes.Ldnull); + ilBuilder.EmitStloc(optionalLocal); + break; + case Type t when t.UseInitObjForInitLocal(): + EmitInitObj(ilBuilder, resultType, optionalLocal); + break; + default: + EmitLoadDefaultPrimitive(ilBuilder, resultType); + ilBuilder.EmitStloc(optionalLocal); + break; + } + } + + private static bool IsInitLocalPrimitive(this Type t) + { + // var x = default(T): + // C# compiler uses special logic for enum defaults and primitive defaults + // On init local case this logic does not apply for IntPtr & UIntPtr. + + if (t == typeof(void)) + return true; + + if (t.IsEnum) + return true; + + return t.IsPrimitive + && t != typeof(IntPtr) + && t != typeof(UIntPtr); + } + + private static bool UseInitObjForInitLocal(this Type resultType) + { + return resultType.IsValueType && !resultType.IsInitLocalPrimitive(); + } + + private static void EmitInitObj(ILGenerator ilBuilder, Type resultType, LocalBuilder optionalLocalForInitobj) + { + if (optionalLocalForInitobj == null) + throw new ArgumentNullException(nameof(optionalLocalForInitobj)); + + /* + IL_0000: ldloca.s 0 + IL_0002: initobj [mscorlib]System.DateTime + */ + ilBuilder.EmitLdloca(optionalLocalForInitobj); + ilBuilder.Emit(OpCodes.Initobj, resultType); + } + + private static void EmitLoadDefaultPrimitive(this ILGenerator ilBuilder, Type resultType) + { + var valueType = resultType; + if (valueType.IsEnum) + valueType = resultType.GetEnumUnderlyingType(); + + // The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single + // + custom logic for decimal + switch (valueType) + { + case Type t0 when t0 == typeof(bool): + case Type t1 when t1 == typeof(byte): + case Type t2 when t2 == typeof(sbyte): + case Type t3 when t3 == typeof(short): + case Type t4 when t4 == typeof(ushort): + case Type t5 when t5 == typeof(int): + case Type t6 when t6 == typeof(uint): + case Type t7 when t7 == typeof(char): + ilBuilder.Emit(OpCodes.Ldc_I4_0); + break; + case Type t1 when t1 == typeof(ulong): + case Type t2 when t2 == typeof(long): + /* + // return 0L; + IL_0000: ldc.i4.0 + IL_0001: conv.i8 + // return 0uL; + IL_0000: ldc.i4.0 + IL_0001: conv.i8 + */ + ilBuilder.Emit(OpCodes.Ldc_I4_0); + ilBuilder.Emit(OpCodes.Conv_I8); + break; + case Type t when t == typeof(IntPtr): + /* + IL_0000: ldc.i4.0 + IL_0001: conv.i + */ + ilBuilder.Emit(OpCodes.Ldc_I4_0); + ilBuilder.Emit(OpCodes.Conv_I); + break; + case Type t when t == typeof(UIntPtr): + /* + IL_0000: ldc.i4.0 + IL_0001: conv.u + */ + ilBuilder.Emit(OpCodes.Ldc_I4_0); + ilBuilder.Emit(OpCodes.Conv_U); + break; + case Type t when t == typeof(double): + ilBuilder.Emit(OpCodes.Ldc_R8, 0.0d); + break; + case Type t when t == typeof(float): + ilBuilder.Emit(OpCodes.Ldc_R4, 0.0f); + break; + case Type t when t == typeof(decimal): + /* + // return decimal.Zero; + IL_0011: ldsfld valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Zero + */ + var zeroField = typeof(decimal).GetField(nameof(decimal.Zero)); + ilBuilder.Emit(OpCodes.Ldsfld, zeroField); + break; + default: + throw new NotSupportedException($"Cannot emit default for {resultType}."); + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorEmitOpExtensions.cs b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorEmitOpExtensions.cs index 7763d59e91..1611e0010a 100644 --- a/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorEmitOpExtensions.cs +++ b/src/BenchmarkDotNet/Helpers/Reflection.Emit/IlGeneratorEmitOpExtensions.cs @@ -131,5 +131,48 @@ public static void EmitStarg(this ILGenerator ilBuilder, ParameterInfo argument) ilBuilder.Emit(OpCodes.Starg, checked((short) position)); } } + + public static void EmitLdc_I4(this ILGenerator ilBuilder, int value) + { + switch (value) + { + case -1: + ilBuilder.Emit(OpCodes.Ldc_I4_M1); + break; + case 0: + ilBuilder.Emit(OpCodes.Ldc_I4_0); + break; + case 1: + ilBuilder.Emit(OpCodes.Ldc_I4_1); + break; + case 2: + ilBuilder.Emit(OpCodes.Ldc_I4_2); + break; + case 3: + ilBuilder.Emit(OpCodes.Ldc_I4_3); + break; + case 4: + ilBuilder.Emit(OpCodes.Ldc_I4_4); + break; + case 5: + ilBuilder.Emit(OpCodes.Ldc_I4_5); + break; + case 6: + ilBuilder.Emit(OpCodes.Ldc_I4_6); + break; + case 7: + ilBuilder.Emit(OpCodes.Ldc_I4_7); + break; + case 8: + ilBuilder.Emit(OpCodes.Ldc_I4_8); + break; + case var i when sbyte.MinValue <= i && i <= sbyte.MaxValue: + ilBuilder.Emit(OpCodes.Ldc_I4_S, (sbyte) value); + break; + default: + ilBuilder.Emit(OpCodes.Ldc_I4, value); + break; + } + } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs b/src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs index 89e5c38368..688462d4d2 100644 --- a/src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs +++ b/src/BenchmarkDotNet/Parameters/SmartParamBuilder.cs @@ -94,10 +94,11 @@ public SmartArgument(ParameterDefinition[] parameterDefinitions, object value, M public string ToSourceCode() { Type paramType = parameterDefinitions[argumentIndex].ParameterType; - bool isParamRefLike = RunnableReflectionHelpers.IsRefLikeType(paramType); - string cast = isParamRefLike ? $"({Value.GetType().GetCorrectCSharpTypeName()})" - : $"({paramType.GetCorrectCSharpTypeName()})"; // it's an object so we need to cast it to the right type + // it's an object so we need to cast it to the right type + string cast = paramType.IsByRefLike() + ? $"({Value.GetType().GetCorrectCSharpTypeName()})" + : $"({paramType.GetCorrectCSharpTypeName()})"; string callPostfix = source is PropertyInfo ? string.Empty : "()"; diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs index 0c096b6c9e..df83c8b961 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Threading.Tasks; using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Columns; @@ -36,7 +37,7 @@ internal static class BenchmarkRunnerClean internal static readonly IResolver DefaultResolver = new CompositeResolver(EnvironmentResolver.Instance, InfrastructureResolver.Instance); - internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos) + internal static async ValueTask Run(BenchmarkRunInfo[] benchmarkRunInfos) { using var taskbarProgress = new TaskbarProgress(TaskbarProgressState.Indeterminate); @@ -74,7 +75,7 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos) eventProcessor.OnEndValidationStage(); // Ensure that OnEndValidationStage() is called when a critical validation error exists. if (validationErrors.Any(validationError => validationError.IsCritical)) - return new[] { Summary.ValidationFailed(title, resultsFolderPath, logFilePath, validationErrors.ToImmutableArray()) }; + return [Summary.ValidationFailed(title, resultsFolderPath, logFilePath, [..validationErrors])]; int totalBenchmarkCount = supportedBenchmarks.Sum(benchmarkInfo => benchmarkInfo.BenchmarksCases.Length); int benchmarksToRunCount = totalBenchmarkCount - (idToResume + 1); // ids are indexed from 0 @@ -143,8 +144,8 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos) } eventProcessor.OnStartRunBenchmarksInType(benchmarkRunInfo.Type, benchmarkRunInfo.BenchmarksCases); - var summary = Run(benchmarkRunInfo, benchmarkToBuildResult, resolver, compositeLogger, eventProcessor, artifactsToCleanup, - resultsFolderPath, logFilePath, totalBenchmarkCount, in runsChronometer, ref benchmarksToRunCount, + (var summary, benchmarksToRunCount) = await Run(benchmarkRunInfo, benchmarkToBuildResult, resolver, compositeLogger, eventProcessor, artifactsToCleanup, + resultsFolderPath, logFilePath, totalBenchmarkCount, runsChronometer, benchmarksToRunCount, taskbarProgress); eventProcessor.OnEndRunBenchmarksInType(benchmarkRunInfo.Type, summary); @@ -196,18 +197,18 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos) } } - private static Summary Run(BenchmarkRunInfo benchmarkRunInfo, - Dictionary buildResults, - IResolver resolver, - ILogger logger, - EventProcessor eventProcessor, - List artifactsToCleanup, - string resultsFolderPath, - string logFilePath, - int totalBenchmarkCount, - in StartedClock runsChronometer, - ref int benchmarksToRunCount, - TaskbarProgress taskbarProgress) + private static async ValueTask<(Summary summary, int benchmarksToRunCount)> Run(BenchmarkRunInfo benchmarkRunInfo, + Dictionary buildResults, + IResolver resolver, + ILogger logger, + EventProcessor eventProcessor, + List artifactsToCleanup, + string resultsFolderPath, + string logFilePath, + int totalBenchmarkCount, + StartedClock runsChronometer, + int benchmarksToRunCount, + TaskbarProgress taskbarProgress) { var runStart = runsChronometer.GetElapsed(); @@ -216,7 +217,7 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo, var config = benchmarkRunInfo.Config; var cultureInfo = config.CultureInfo ?? DefaultCultureInfo.Instance; var reports = new List(); - string title = GetTitle(new[] { benchmarkRunInfo }); + string title = GetTitle([benchmarkRunInfo]); using var consoleTitler = new ConsoleTitler($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining"); logger.WriteLineInfo($"// Found {benchmarks.Length} benchmarks:"); @@ -244,7 +245,7 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo, artifactsToCleanup.AddRange(buildResult.ArtifactsToCleanup); eventProcessor.OnStartRunBenchmark(benchmark); - var report = RunCore(benchmark, info.benchmarkId, logger, resolver, buildResult, benchmarkRunInfo.CompositeInProcessDiagnoser); + var report = await RunCore(benchmark, info.benchmarkId, logger, resolver, buildResult, benchmarkRunInfo.CompositeInProcessDiagnoser); eventProcessor.OnEndRunBenchmark(benchmark, report); if (report.AllMeasurements.Any(m => m.Operations == 0)) @@ -297,15 +298,20 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo, var runEnd = runsChronometer.GetElapsed(); - return new Summary(title, - reports.ToImmutableArray(), - HostEnvironmentInfo.GetCurrent(), - resultsFolderPath, - logFilePath, - runEnd.GetTimeSpan() - runStart.GetTimeSpan(), - cultureInfo, - Validate(benchmarkRunInfo), // validate them once again, but don't print the output - config.GetColumnHidingRules().ToImmutableArray()); + return ( + new Summary( + title, + [.. reports], + HostEnvironmentInfo.GetCurrent(), + resultsFolderPath, + logFilePath, + runEnd.GetTimeSpan() - runStart.GetTimeSpan(), + cultureInfo, + Validate(benchmarkRunInfo), // validate them once again, but don't print the output + [.. config.GetColumnHidingRules()] + ), + benchmarksToRunCount + ); } private static void PrintSummary(ILogger logger, ImmutableConfig config, Summary summary) @@ -476,7 +482,7 @@ private static BuildResult Build(BuildPartition buildPartition, string rootArtif } } - private static BenchmarkReport RunCore(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, IResolver resolver, BuildResult buildResult, + private static async ValueTask RunCore(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, IResolver resolver, BuildResult buildResult, CompositeInProcessDiagnoser compositeInProcessDiagnoser) { var toolchain = benchmarkCase.GetToolchain(); @@ -484,12 +490,12 @@ private static BenchmarkReport RunCore(BenchmarkCase benchmarkCase, BenchmarkId logger.WriteLineHeader("// **************************"); logger.WriteLineHeader("// Benchmark: " + benchmarkCase.DisplayInfo); - var (success, executeResults, metrics) = Execute(logger, benchmarkCase, benchmarkId, toolchain, buildResult, resolver, compositeInProcessDiagnoser); + var (success, executeResults, metrics) = await Execute(logger, benchmarkCase, benchmarkId, toolchain, buildResult, resolver, compositeInProcessDiagnoser); return new BenchmarkReport(success, benchmarkCase, buildResult, buildResult, executeResults, metrics); } - private static (bool success, List executeResults, List metrics) Execute( + private static async ValueTask<(bool success, List executeResults, List metrics)> Execute( ILogger logger, BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, IToolchain toolchain, BuildResult buildResult, IResolver resolver, CompositeInProcessDiagnoser compositeInProcessDiagnoser) { @@ -516,7 +522,7 @@ private static (bool success, List executeResults, List m // use diagnoser only for the last run (we need single result, not many) bool useDiagnoser = launchIndex == launchCount && noOverheadCompositeDiagnoser != null; - var executeResult = RunExecute( + var executeResult = await RunExecute( logger, benchmarkCase, benchmarkId, @@ -559,7 +565,7 @@ private static (bool success, List executeResults, List m { logger.WriteLineInfo("// Run, Diagnostic"); - var executeResult = RunExecute( + var executeResult = await RunExecute( logger, benchmarkCase, benchmarkId, @@ -588,7 +594,7 @@ private static (bool success, List executeResults, List m if (compositeInProcessDiagnoser.InProcessDiagnosers.Any(d => d.GetRunMode(benchmarkCase) == Diagnosers.RunMode.SeparateLogic)) { - var executeResult = RunExecute( + var executeResult = await RunExecute( logger, benchmarkCase, benchmarkId, @@ -612,10 +618,10 @@ private static (bool success, List executeResults, List m return (true, executeResults, metrics); } - private static ExecuteResult RunExecute(ILogger logger, BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, IToolchain toolchain, + private static async ValueTask RunExecute(ILogger logger, BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, IToolchain toolchain, BuildResult buildResult, IResolver resolver, IDiagnoser? diagnoser, CompositeInProcessDiagnoser compositeInProcessDiagnoser, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { - var executeResult = toolchain.Executor.Execute( + var executeResult = await toolchain.Executor.ExecuteAsync( new ExecuteParameters( buildResult, benchmarkCase, diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerDirty.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerDirty.cs index 1e170f7503..67ac056276 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerDirty.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerDirty.cs @@ -1,12 +1,12 @@ using System; -using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Engines; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Portability; using BenchmarkDotNet.Reports; using JetBrains.Annotations; @@ -20,93 +20,167 @@ public static class BenchmarkRunner [PublicAPI] public static Summary Run(IConfig? config = null, string[]? args = null) { - using (DirtyAssemblyResolveHelper.Create()) - return RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(typeof(T), config, args)); + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAsync(config, args)); } [PublicAPI] public static Summary Run(Type type, IConfig? config = null, string[]? args = null) { - using (DirtyAssemblyResolveHelper.Create()) - return RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(type, config, args)); + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAsync(type, config, args)); } [PublicAPI] public static Summary[] Run(Type[] types, IConfig? config = null, string[]? args = null) { - using (DirtyAssemblyResolveHelper.Create()) - return RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(types, config, args)); + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAsync(types, config, args)); } [PublicAPI] public static Summary Run(Type type, MethodInfo[] methods, IConfig? config = null) { - using (DirtyAssemblyResolveHelper.Create()) - return RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(type, methods, config)); + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAsync(type, methods, config)); } [PublicAPI] public static Summary[] Run(Assembly assembly, IConfig? config = null, string[]? args = null) { - using (DirtyAssemblyResolveHelper.Create()) - return RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(assembly, config, args)); + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAsync(assembly, config, args)); } [PublicAPI] public static Summary Run(BenchmarkRunInfo benchmarkRunInfo) { - using (DirtyAssemblyResolveHelper.Create()) - return RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(new[] { benchmarkRunInfo }).Single()); + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAsync(benchmarkRunInfo)); } [PublicAPI] public static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos) + { + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAsync(benchmarkRunInfos)); + } + + /// + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public static ValueTask RunAsync(IConfig? config = null, string[]? args = null) + => RunAsync(typeof(T), config, args); + + /// + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public static async ValueTask RunAsync(Type type, IConfig? config = null, string[]? args = null) + { + using (DirtyAssemblyResolveHelper.Create()) + { + return await RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(type, config, args)); + } + } + + /// + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public static async ValueTask RunAsync(Type[] types, IConfig? config = null, string[]? args = null) + { + using (DirtyAssemblyResolveHelper.Create()) + { + return await RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(types, config, args)); + } + } + + /// + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public static async ValueTask RunAsync(Type type, MethodInfo[] methods, IConfig? config = null) + { + using (DirtyAssemblyResolveHelper.Create()) + { + return await RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(type, methods, config)); + } + } + + /// + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public static async ValueTask RunAsync(Assembly assembly, IConfig? config = null, string[]? args = null) { using (DirtyAssemblyResolveHelper.Create()) - return RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(benchmarkRunInfos)); + { + return await RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(assembly, config, args)); + } + } + + /// + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public static async ValueTask RunAsync(BenchmarkRunInfo benchmarkRunInfo) + => (await RunAsync([benchmarkRunInfo])).Single(); + + /// + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public static async ValueTask RunAsync(BenchmarkRunInfo[] benchmarkRunInfos) + { + using (DirtyAssemblyResolveHelper.Create()) + { + return await RunWithExceptionHandling(() => RunWithDirtyAssemblyResolveHelper(benchmarkRunInfos)); + } } [MethodImpl(MethodImplOptions.NoInlining)] - private static Summary RunWithDirtyAssemblyResolveHelper(Type type, IConfig? config, string[]? args) + private static async ValueTask RunWithDirtyAssemblyResolveHelper(Type type, IConfig? config, string[]? args) { var summaries = args == null - ? BenchmarkRunnerClean.Run(new[] { BenchmarkConverter.TypeToBenchmarks(type, config) }) - : new BenchmarkSwitcher(new[] { type }).RunWithDirtyAssemblyResolveHelper(args, config, false); + ? await BenchmarkRunnerClean.Run([BenchmarkConverter.TypeToBenchmarks(type, config)]) + : await new BenchmarkSwitcher([type]).RunWithDirtyAssemblyResolveHelper(args, config, false); return summaries.SingleOrDefault() ?? Summary.ValidationFailed($"No benchmarks found in type '{type.Name}'", string.Empty, string.Empty); } [MethodImpl(MethodImplOptions.NoInlining)] - private static Summary RunWithDirtyAssemblyResolveHelper(Type type, MethodInfo[] methods, IConfig? config = null) + private static async ValueTask RunWithDirtyAssemblyResolveHelper(Type type, MethodInfo[] methods, IConfig? config = null) { - var summaries = BenchmarkRunnerClean.Run(new[] { BenchmarkConverter.MethodsToBenchmarks(type, methods, config) }); + var summaries = await BenchmarkRunnerClean.Run([BenchmarkConverter.MethodsToBenchmarks(type, methods, config)]); return summaries.SingleOrDefault() ?? Summary.ValidationFailed($"No benchmarks found in type '{type.Name}'", string.Empty, string.Empty); } [MethodImpl(MethodImplOptions.NoInlining)] - private static Summary[] RunWithDirtyAssemblyResolveHelper(Assembly assembly, IConfig? config, string[]? args) + private static async ValueTask RunWithDirtyAssemblyResolveHelper(Assembly assembly, IConfig? config, string[]? args) => args == null - ? BenchmarkRunnerClean.Run(assembly.GetRunnableBenchmarks().Select(type => BenchmarkConverter.TypeToBenchmarks(type, config)).ToArray()) - : new BenchmarkSwitcher(assembly).RunWithDirtyAssemblyResolveHelper(args, config, false).ToArray(); + ? await BenchmarkRunnerClean.Run(assembly.GetRunnableBenchmarks().Select(type => BenchmarkConverter.TypeToBenchmarks(type, config)).ToArray()) + : (await new BenchmarkSwitcher(assembly).RunWithDirtyAssemblyResolveHelper(args, config, false)).ToArray(); [MethodImpl(MethodImplOptions.NoInlining)] - private static Summary[] RunWithDirtyAssemblyResolveHelper(Type[] types, IConfig? config, string[]? args) + private static async ValueTask RunWithDirtyAssemblyResolveHelper(Type[] types, IConfig? config, string[]? args) => args == null - ? BenchmarkRunnerClean.Run(types.Select(type => BenchmarkConverter.TypeToBenchmarks(type, config)).ToArray()) - : new BenchmarkSwitcher(types).RunWithDirtyAssemblyResolveHelper(args, config, false).ToArray(); + ? await BenchmarkRunnerClean.Run(types.Select(type => BenchmarkConverter.TypeToBenchmarks(type, config)).ToArray()) + : (await new BenchmarkSwitcher(types).RunWithDirtyAssemblyResolveHelper(args, config, false)).ToArray(); [MethodImpl(MethodImplOptions.NoInlining)] - private static Summary[] RunWithDirtyAssemblyResolveHelper(BenchmarkRunInfo[] benchmarkRunInfos) + private static ValueTask RunWithDirtyAssemblyResolveHelper(BenchmarkRunInfo[] benchmarkRunInfos) => BenchmarkRunnerClean.Run(benchmarkRunInfos); - private static Summary RunWithExceptionHandling(Func run) + private static async ValueTask RunWithExceptionHandling(Func> run) { try { - return run(); + return await run(); } catch (InvalidBenchmarkDeclarationException e) { @@ -115,16 +189,16 @@ private static Summary RunWithExceptionHandling(Func run) } } - private static Summary[] RunWithExceptionHandling(Func run) + private static async ValueTask RunWithExceptionHandling(Func> run) { try { - return run(); + return await run(); } catch (InvalidBenchmarkDeclarationException e) { ConsoleLogger.Default.WriteLineError(e.Message); - return new[] { Summary.ValidationFailed(e.Message, string.Empty, string.Empty) }; + return [Summary.ValidationFailed(e.Message, string.Empty, string.Empty)]; } } } diff --git a/src/BenchmarkDotNet/Running/BenchmarkSwitcher.cs b/src/BenchmarkDotNet/Running/BenchmarkSwitcher.cs index 24a2860e6b..1470c9a8c3 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkSwitcher.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkSwitcher.cs @@ -4,9 +4,11 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using BenchmarkDotNet.Configs; using BenchmarkDotNet.ConsoleArguments; using BenchmarkDotNet.ConsoleArguments.ListBenchmarks; +using BenchmarkDotNet.Engines; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Jobs; @@ -21,8 +23,8 @@ namespace BenchmarkDotNet.Running public class BenchmarkSwitcher { private readonly IUserInteraction userInteraction = new UserInteraction(); - private readonly List types = new List(); - private readonly List assemblies = new List(); + private readonly List types = []; + private readonly List assemblies = []; internal BenchmarkSwitcher(IUserInteraction userInteraction) => this.userInteraction = userInteraction; @@ -44,22 +46,19 @@ public class BenchmarkSwitcher [SuppressMessage("ReSharper", "ParameterHidesMember")] public BenchmarkSwitcher With(Assembly[] assemblies) { this.assemblies.AddRange(assemblies); return this; } - [PublicAPI] public static BenchmarkSwitcher FromTypes(Type[] types) => new BenchmarkSwitcher(types); + [PublicAPI] public static BenchmarkSwitcher FromTypes(Type[] types) => new(types); - [PublicAPI] public static BenchmarkSwitcher FromAssembly(Assembly assembly) => new BenchmarkSwitcher(assembly); + [PublicAPI] public static BenchmarkSwitcher FromAssembly(Assembly assembly) => new(assembly); - [PublicAPI] public static BenchmarkSwitcher FromAssemblies(Assembly[] assemblies) => new BenchmarkSwitcher(assemblies); + [PublicAPI] public static BenchmarkSwitcher FromAssemblies(Assembly[] assemblies) => new(assemblies); /// /// Run all available benchmarks. /// [PublicAPI] public IEnumerable RunAll(IConfig? config = null, string[]? args = null) { - args ??= Array.Empty(); - if (ConfigParser.TryUpdateArgs(args, out var updatedArgs, options => options.Filters = new[] { "*" })) - args = updatedArgs; - - return Run(args, config); + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAllAsync(config, args)); } /// @@ -67,33 +66,71 @@ [PublicAPI] public IEnumerable RunAll(IConfig? config = null, string[]? /// [PublicAPI] public Summary RunAllJoined(IConfig? config = null, string[]? args = null) { - args ??= Array.Empty(); - if (ConfigParser.TryUpdateArgs(args, out var updatedArgs, options => (options.Join, options.Filters) = (true, new[] { "*" }))) + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAllJoinedAsync(config, args)); + } + + [PublicAPI] + public IEnumerable Run(string[]? args = null, IConfig? config = null) + { + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + return context.ExecuteUntilComplete(RunAsync(args, config)); + } + + /// + /// Run all available benchmarks. + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public ValueTask> RunAllAsync(IConfig? config = null, string[]? args = null) + { + args ??= []; + if (ConfigParser.TryUpdateArgs(args, out var updatedArgs, options => options.Filters = ["*"])) args = updatedArgs; - return Run(args, config).Single(); + return RunAsync(args, config); } + /// + /// Run all available benchmarks and join them to a single summary. + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// [PublicAPI] - public IEnumerable Run(string[]? args = null, IConfig? config = null) + public async ValueTask RunAllJoinedAsync(IConfig? config = null, string[]? args = null) + { + args ??= []; + if (ConfigParser.TryUpdateArgs(args, out var updatedArgs, options => (options.Join, options.Filters) = (true, ["*"]))) + args = updatedArgs; + + var results = await RunAsync(args, config); + return results.Single(); + } + + /// + /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// + [PublicAPI] + public async ValueTask> RunAsync(string[]? args = null, IConfig? config = null) { // VS generates bad assembly binding redirects for ValueTuple for Full .NET Framework // we need to keep the logic that uses it in a separate method and create DirtyAssemblyResolveHelper first // so it can ignore the version mismatch ;) using (DirtyAssemblyResolveHelper.Create()) - return RunWithDirtyAssemblyResolveHelper(args, config, true); + { + return await RunWithDirtyAssemblyResolveHelper(args, config, true); + } } [MethodImpl(MethodImplOptions.NoInlining)] - internal IEnumerable RunWithDirtyAssemblyResolveHelper(string[]? args, IConfig? config, bool askUserForInput) + internal async ValueTask> RunWithDirtyAssemblyResolveHelper(string[]? args, IConfig? config, bool askUserForInput) { - var notNullArgs = args ?? Array.Empty(); + var notNullArgs = args ?? []; var notNullConfig = config ?? DefaultConfig.Instance; var logger = notNullConfig.GetNonNullCompositeLogger(); var (isParsingSuccess, parsedConfig, options) = ConfigParser.Parse(notNullArgs, logger, notNullConfig); if (!isParsingSuccess) // invalid console args, the ConfigParser printed the error - return Array.Empty(); + return []; if (args == null && Environment.GetCommandLineArgs().Length > 1) // The first element is the executable file name logger.WriteLineHint("You haven't passed command line arguments to BenchmarkSwitcher.Run method. Running with default configuration."); @@ -101,25 +138,25 @@ internal IEnumerable RunWithDirtyAssemblyResolveHelper(string[]? args, if (options.PrintInformation) { logger.WriteLine(HostEnvironmentInfo.GetInformation()); - return Array.Empty(); + return []; } var effectiveConfig = ManualConfig.Union(notNullConfig, parsedConfig); var (allTypesValid, allAvailableTypesWithRunnableBenchmarks) = TypeFilter.GetTypesWithRunnableBenchmarks(types, assemblies, logger); if (!allTypesValid) // there were some invalid and TypeFilter printed errors - return Array.Empty(); + return []; if (allAvailableTypesWithRunnableBenchmarks.IsEmpty()) { userInteraction.PrintNoBenchmarksError(logger); - return Array.Empty(); + return []; } if (options.ListBenchmarkCaseMode != ListBenchmarkCaseMode.Disabled) { PrintList(logger, effectiveConfig, allAvailableTypesWithRunnableBenchmarks, options); - return Array.Empty(); + return []; } var benchmarksToFilter = options.UserProvidedFilters || !askUserForInput @@ -128,18 +165,18 @@ internal IEnumerable RunWithDirtyAssemblyResolveHelper(string[]? args, if (effectiveConfig.Options.HasFlag(ConfigOptions.ApplesToApples)) { - return ApplesToApples(ImmutableConfigBuilder.Create(effectiveConfig), benchmarksToFilter, logger, options); + return await ApplesToApples(ImmutableConfigBuilder.Create(effectiveConfig), benchmarksToFilter, logger, options); } var filteredBenchmarks = TypeFilter.Filter(effectiveConfig, benchmarksToFilter); if (filteredBenchmarks.IsEmpty()) { - userInteraction.PrintWrongFilterInfo(benchmarksToFilter, logger, options.Filters.ToArray()); - return Array.Empty(); + userInteraction.PrintWrongFilterInfo(benchmarksToFilter, logger, [.. options.Filters]); + return []; } - return BenchmarkRunnerClean.Run(filteredBenchmarks); + return await BenchmarkRunnerClean.Run(filteredBenchmarks); } private static void PrintList(ILogger nonNullLogger, IConfig effectiveConfig, IReadOnlyList allAvailableTypesWithRunnableBenchmarks, CommandLineOptions options) @@ -154,24 +191,24 @@ private static void PrintList(ILogger nonNullLogger, IConfig effectiveConfig, IR printer.Print(testNames, nonNullLogger); } - private IEnumerable ApplesToApples(ImmutableConfig effectiveConfig, IReadOnlyList benchmarksToFilter, ILogger logger, CommandLineOptions options) + private async ValueTask> ApplesToApples(ImmutableConfig effectiveConfig, IReadOnlyList benchmarksToFilter, ILogger logger, CommandLineOptions options) { var jobs = effectiveConfig.GetJobs().ToArray(); if (jobs.Length <= 1) { logger.WriteError("To use apples-to-apples comparison you must specify at least two Job objects."); - return Array.Empty(); + return []; } var baselineJob = jobs.SingleOrDefault(job => job.Meta.Baseline); if (baselineJob == default) { logger.WriteError("To use apples-to-apples comparison you must specify exactly ONE baseline Job object."); - return Array.Empty(); + return []; } else if (jobs.Any(job => !job.Run.HasValue(RunMode.IterationCountCharacteristic))) { logger.WriteError("To use apples-to-apples comparison you must specify the number of iterations in explicit way."); - return Array.Empty(); + return []; } Job invocationCountJob = baselineJob @@ -187,12 +224,12 @@ private IEnumerable ApplesToApples(ImmutableConfig effectiveConfig, IRe var invocationCountBenchmarks = TypeFilter.Filter(invocationCountConfig, benchmarksToFilter); if (invocationCountBenchmarks.IsEmpty()) { - userInteraction.PrintWrongFilterInfo(benchmarksToFilter, logger, options.Filters.ToArray()); - return Array.Empty(); + userInteraction.PrintWrongFilterInfo(benchmarksToFilter, logger, [.. options.Filters]); + return []; } logger.WriteLineHeader("Each benchmark is going to be executed just once to get invocation counts."); - Summary[] invocationCountSummaries = BenchmarkRunnerClean.Run(invocationCountBenchmarks); + Summary[] invocationCountSummaries = await BenchmarkRunnerClean.Run(invocationCountBenchmarks); Dictionary<(Descriptor Descriptor, ParameterInstances Parameters), Measurement> dictionary = invocationCountSummaries .SelectMany(summary => summary.Reports) @@ -220,7 +257,7 @@ private IEnumerable ApplesToApples(ImmutableConfig effectiveConfig, IRe .ToArray(); logger.WriteLineHeader("Actual benchmarking is going to happen now!"); - return BenchmarkRunnerClean.Run(benchmarksWithInvocationCount); + return await BenchmarkRunnerClean.Run(benchmarksWithInvocationCount); } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt index e61e525552..a491ae3030 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt @@ -63,12 +63,18 @@ namespace BenchmarkDotNet.Autogenerated { host.WriteLine("You have not specified benchmark id (an integer) so the first benchmark will be executed."); } + using (global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext benchmarkSynchronizationContext = global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext.CreateAndSetCurrent()) + { + global::System.Threading.Tasks.ValueTask runTask; #if NATIVEAOT - $NativeAotSwitch$ + $NativeAotSwitch$ #else - global::System.Type type = typeof(global::BenchmarkDotNet.Autogenerated.UniqueProgramName).Assembly.GetType($"BenchmarkDotNet.Autogenerated.Runnable_{id}"); - type.GetMethod("Run", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.Static).Invoke(null, new global::System.Object[] { host, benchmarkName, diagnoserRunMode }); + runTask = (global::System.Threading.Tasks.ValueTask) typeof(global::BenchmarkDotNet.Autogenerated.UniqueProgramName).Assembly.GetType($"BenchmarkDotNet.Autogenerated.Runnable_{id}") + .GetMethod("Run", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.Static) + .Invoke(null, new global::System.Object[] { host, benchmarkName, diagnoserRunMode }); #endif + benchmarkSynchronizationContext.ExecuteUntilComplete(runTask); + } return 0; } catch (global::System.Exception oom) when (oom is global::System.OutOfMemoryException || oom is global::System.Reflection.TargetInvocationException reflection && reflection.InnerException is global::System.OutOfMemoryException) diff --git a/src/BenchmarkDotNet/Templates/BenchmarkType.txt b/src/BenchmarkDotNet/Templates/BenchmarkType.txt index a6a8a1d69b..21a4b7b02b 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkType.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkType.txt @@ -1,7 +1,8 @@ // Type name must be in sync with DisassemblyDiagnoser.BuildClrMdArgs. - public unsafe sealed class Runnable_$ID$ : $WorkloadTypeName$ + [global::BenchmarkDotNet.Attributes.CompilerServices.AggressivelyOptimizeMethods] + public sealed partial class Runnable_$ID$ : $WorkloadTypeName$ { - public static void Run(global::BenchmarkDotNet.Engines.IHost host, global::System.String benchmarkName, global::BenchmarkDotNet.Diagnosers.RunMode diagnoserRunMode) + public static async global::System.Threading.Tasks.ValueTask Run(global::BenchmarkDotNet.Engines.IHost host, global::System.String benchmarkName, global::BenchmarkDotNet.Diagnosers.RunMode diagnoserRunMode) { global::BenchmarkDotNet.Autogenerated.Runnable_$ID$ instance = new global::BenchmarkDotNet.Autogenerated.Runnable_$ID$(); @@ -42,10 +43,10 @@ WorkloadActionNoUnroll = instance.WorkloadActionNoUnroll, OverheadActionNoUnroll = instance.OverheadActionNoUnroll, OverheadActionUnroll = instance.OverheadActionUnroll, - GlobalSetupAction = instance.globalSetupAction, - GlobalCleanupAction = instance.globalCleanupAction, - IterationSetupAction = instance.iterationSetupAction, - IterationCleanupAction = instance.iterationCleanupAction, + GlobalSetupAction = instance.__GlobalSetup, + GlobalCleanupAction = instance.__GlobalCleanup, + IterationSetupAction = instance.__IterationSetup, + IterationCleanupAction = instance.__IterationCleanup, TargetJob = job, OperationsPerInvoke = $OperationsPerInvoke$, RunExtraIteration = $RunExtraIteration$, @@ -53,7 +54,7 @@ InProcessDiagnoserHandler = compositeInProcessDiagnoserHandler }; - global::BenchmarkDotNet.Engines.RunResults results = new $EngineFactoryType$().Create(engineParameters).Run(); + global::BenchmarkDotNet.Engines.RunResults results = await new $EngineFactoryType$().Create(engineParameters).RunAsync(); host.ReportResults(results); // printing costs memory, do this after runs instance.__TrickTheJIT__(); // compile the method for disassembler, but without actual run of the benchmark ;) @@ -61,84 +62,65 @@ } [global::System.Diagnostics.CodeAnalysis.SetsRequiredMembers] - public Runnable_$ID$() + public unsafe Runnable_$ID$() { - this.globalSetupAction = $GlobalSetupMethodName$; - this.globalCleanupAction = $GlobalCleanupMethodName$; - this.iterationSetupAction = $IterationSetupMethodName$; - this.iterationCleanupAction = $IterationCleanupMethodName$; $InitializeArgumentFields$ $ParamsContent$ } - private global::System.Action globalSetupAction; - private global::System.Action globalCleanupAction; - private global::System.Action iterationSetupAction; - private global::System.Action iterationCleanupAction; - $DeclareArgumentFields$ + // Necessary because of error CS4004: Cannot await in an unsafe context + [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)] + private unsafe struct FieldsContainer + { + $DeclareArgumentFields$ + $ExtraFields$ + } - // this method is used only for the disassembly diagnoser purposes - // the goal is to get this and the benchmarked method jitted, but without executing the benchmarked method itself - public global::System.Int32 NotEleven; - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - public void __TrickTheJIT__() + private global::BenchmarkDotNet.Autogenerated.Runnable_$ID$.FieldsContainer __fieldsContainer; + + private $GlobalSetupModifiers$ global::System.Threading.Tasks.ValueTask __GlobalSetup() { - this.NotEleven = new global::System.Random(123).Next(0, 10); - $DisassemblerEntryMethodName$(); + $GlobalSetupImpl$ } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - public void $DisassemblerEntryMethodName$() + private $GlobalCleanupModifiers$ global::System.Threading.Tasks.ValueTask __GlobalCleanup() { - if (this.NotEleven == 11) - { - $LoadArguments$ - $WorkloadMethodCall$; - } + $GlobalCleanupImpl$ } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - private void __Overhead($ArgumentsDefinition$) + private $IterationSetupModifiers$ global::System.Threading.Tasks.ValueTask __IterationSetup() { + $IterationSetupImpl$ } - [global::System.Runtime.CompilerServices.MethodImpl(global::BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] - private void OverheadActionUnroll(global::System.Int64 invokeCount) + private $IterationCleanupModifiers$ global::System.Threading.Tasks.ValueTask __IterationCleanup() { - $LoadArguments$ - while (--invokeCount >= 0) - { - this.__Overhead($PassArguments$);@Unroll@ - } + $IterationCleanupImpl$ } - [global::System.Runtime.CompilerServices.MethodImpl(global::BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] - private void OverheadActionNoUnroll(global::System.Int64 invokeCount) + // this method is used only for the disassembly diagnoser purposes + // the goal is to get this and the benchmarked method jitted, but without executing the benchmarked method itself + public global::System.Int32 NotEleven; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + public void __TrickTheJIT__() { - $LoadArguments$ - while (--invokeCount >= 0) - { - this.__Overhead($PassArguments$); - } + this.NotEleven = new global::System.Random(123).Next(0, 10); + $DisassemblerEntryMethodName$(); } - [global::System.Runtime.CompilerServices.MethodImpl(global::BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(global::System.Int64 invokeCount) + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoOptimization | global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + public unsafe void $DisassemblerEntryMethodName$() { - $LoadArguments$ - while (--invokeCount >= 0) + if (this.NotEleven == 11) { - $WorkloadMethodCall$;@Unroll@ + $DisassemblerEntryMethodImpl$ } } - [global::System.Runtime.CompilerServices.MethodImpl(global::BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(global::System.Int64 invokeCount) + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + private unsafe void __Overhead($ArgumentsDefinition$) { - $LoadArguments$ - while (--invokeCount >= 0) - { - $WorkloadMethodCall$; - } } + + $CoreImpl$ } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs index 044f169588..add86fd580 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.IO; using System.IO.Pipes; +using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; @@ -16,26 +17,24 @@ namespace BenchmarkDotNet.Toolchains.DotNetCli { [PublicAPI] - public class DotNetCliExecutor : IExecutor + public class DotNetCliExecutor(string customDotNetCliPath) : IExecutor { - public DotNetCliExecutor(string customDotNetCliPath) => CustomDotNetCliPath = customDotNetCliPath; - - private string CustomDotNetCliPath { get; } - - public ExecuteResult Execute(ExecuteParameters executeParameters) + public ValueTask ExecuteAsync(ExecuteParameters executeParameters) { if (!File.Exists(executeParameters.BuildResult.ArtifactsPaths.ExecutablePath)) { executeParameters.Logger.WriteLineError($"Did not find {executeParameters.BuildResult.ArtifactsPaths.ExecutablePath}, but the folder contained:"); foreach (var file in new DirectoryInfo(executeParameters.BuildResult.ArtifactsPaths.BinariesDirectoryPath).GetFiles("*.*")) + { executeParameters.Logger.WriteLineError(file.Name); + } - return ExecuteResult.CreateFailed(); + return new ValueTask(ExecuteResult.CreateFailed()); } try { - return Execute( + var executeResult = Execute( executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, @@ -46,6 +45,7 @@ public ExecuteResult Execute(ExecuteParameters executeParameters) executeParameters.Resolver, executeParameters.LaunchIndex, executeParameters.DiagnoserRunMode); + return new ValueTask(executeResult); } finally { @@ -56,21 +56,21 @@ public ExecuteResult Execute(ExecuteParameters executeParameters) } private ExecuteResult Execute(BenchmarkCase benchmarkCase, - BenchmarkId benchmarkId, - ILogger logger, - ArtifactsPaths artifactsPaths, - IDiagnoser? diagnoser, - CompositeInProcessDiagnoser compositeInProcessDiagnoser, - string executableName, - IResolver resolver, - int launchIndex, - Diagnosers.RunMode diagnoserRunMode) + BenchmarkId benchmarkId, + ILogger logger, + ArtifactsPaths artifactsPaths, + IDiagnoser? diagnoser, + CompositeInProcessDiagnoser compositeInProcessDiagnoser, + string executableName, + IResolver resolver, + int launchIndex, + Diagnosers.RunMode diagnoserRunMode) { - using AnonymousPipeServerStream inputFromBenchmark = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable); - using AnonymousPipeServerStream acknowledgments = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable); + using AnonymousPipeServerStream inputFromBenchmark = new(PipeDirection.In, HandleInheritability.Inheritable); + using AnonymousPipeServerStream acknowledgments = new(PipeDirection.Out, HandleInheritability.Inheritable); var startInfo = DotNetCliCommandExecutor.BuildStartInfo( - CustomDotNetCliPath, + customDotNetCliPath, artifactsPaths.BinariesDirectoryPath, $"{executableName.EscapeCommandLine()} {benchmarkId.ToArguments(inputFromBenchmark.GetClientHandleAsString(), acknowledgments.GetClientHandleAsString(), diagnoserRunMode)}", redirectStandardOutput: true, @@ -79,50 +79,49 @@ private ExecuteResult Execute(BenchmarkCase benchmarkCase, startInfo.SetEnvironmentVariables(benchmarkCase, resolver); - using (Process process = new() { StartInfo = startInfo }) - using (ConsoleExitHandler consoleExitHandler = new(process, logger)) - using (AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false)) - { - Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, inputFromBenchmark, acknowledgments); + using Process process = new() { StartInfo = startInfo }; + using ConsoleExitHandler consoleExitHandler = new(process, logger); + using AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false); + + Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, inputFromBenchmark, acknowledgments); - logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); + logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); - diagnoser?.Handle(HostSignal.BeforeProcessStart, broker.DiagnoserActionParameters); + diagnoser?.Handle(HostSignal.BeforeProcessStart, broker.DiagnoserActionParameters); - process.Start(); + process.Start(); - diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); + diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); - processOutputReader.BeginRead(); + processOutputReader.BeginRead(); - process.EnsureHighPriority(logger); - if (benchmarkCase.Job.Environment.HasValue(EnvironmentMode.AffinityCharacteristic)) - { - process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); - } - - broker.ProcessData(); + process.EnsureHighPriority(logger); + if (benchmarkCase.Job.Environment.HasValue(EnvironmentMode.AffinityCharacteristic)) + { + process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); + } - if (!process.WaitForExit(milliseconds: (int)ExecuteParameters.ProcessExitTimeout.TotalMilliseconds)) - { - logger.WriteLineInfo($"// The benchmarking process did not quit within {ExecuteParameters.ProcessExitTimeout.TotalSeconds} seconds, it's going to get force killed now."); + broker.ProcessData(); - processOutputReader.CancelRead(); - consoleExitHandler.KillProcessTree(); - } - else - { - processOutputReader.StopRead(); - } + if (!process.WaitForExit(milliseconds: (int) ExecuteParameters.ProcessExitTimeout.TotalMilliseconds)) + { + logger.WriteLineInfo($"// The benchmarking process did not quit within {ExecuteParameters.ProcessExitTimeout.TotalSeconds} seconds, it's going to get force killed now."); - return new ExecuteResult(true, - process.HasExited ? process.ExitCode : null, - process.Id, - broker.Results, - broker.PrefixedOutput, - processOutputReader.GetOutputLines(), - launchIndex); + processOutputReader.CancelRead(); + consoleExitHandler.KillProcessTree(); + } + else + { + processOutputReader.StopRead(); } + + return new ExecuteResult(true, + process.HasExited ? process.ExitCode : null, + process.Id, + broker.Results, + broker.PrefixedOutput, + processOutputReader.GetOutputLines(), + launchIndex); } } } diff --git a/src/BenchmarkDotNet/Toolchains/Executor.cs b/src/BenchmarkDotNet/Toolchains/Executor.cs index 70e844ef22..90d287ab8c 100644 --- a/src/BenchmarkDotNet/Toolchains/Executor.cs +++ b/src/BenchmarkDotNet/Toolchains/Executor.cs @@ -5,6 +5,7 @@ using System.IO.Pipes; using System.Linq; using System.Text; +using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; @@ -23,18 +24,19 @@ namespace BenchmarkDotNet.Toolchains [PublicAPI("Used by some of our Superusers that implement their own Toolchains (e.g. Kestrel team)")] public class Executor : IExecutor { - public ExecuteResult Execute(ExecuteParameters executeParameters) + /// + /// Out-of-process executor executes synchronously. + /// + public ValueTask ExecuteAsync(ExecuteParameters executeParameters) { string exePath = executeParameters.BuildResult.ArtifactsPaths.ExecutablePath; - if (!File.Exists(exePath)) - { - return ExecuteResult.CreateFailed(); - } - - return Execute(executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, executeParameters.BuildResult.ArtifactsPaths, - executeParameters.Diagnoser, executeParameters.CompositeInProcessDiagnoser, executeParameters.Resolver, executeParameters.LaunchIndex, - executeParameters.DiagnoserRunMode); + var executeResult = !File.Exists(exePath) + ? ExecuteResult.CreateFailed() + : Execute(executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, executeParameters.BuildResult.ArtifactsPaths, + executeParameters.Diagnoser, executeParameters.CompositeInProcessDiagnoser, executeParameters.Resolver, executeParameters.LaunchIndex, + executeParameters.DiagnoserRunMode); + return new ValueTask(executeResult); } private static ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, ArtifactsPaths artifactsPaths, @@ -48,16 +50,15 @@ private static ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId be string args = benchmarkId.ToArguments(inputFromBenchmark.GetClientHandleAsString(), acknowledgments.GetClientHandleAsString(), diagnoserRunMode); - using (Process process = new() { StartInfo = CreateStartInfo(benchmarkCase, artifactsPaths, args, resolver) }) - using (ConsoleExitHandler consoleExitHandler = new(process, logger)) - using (AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false)) - { - Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, inputFromBenchmark, acknowledgments); + using Process process = new() { StartInfo = CreateStartInfo(benchmarkCase, artifactsPaths, args, resolver) }; + using ConsoleExitHandler consoleExitHandler = new(process, logger); + using AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false); + + Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, inputFromBenchmark, acknowledgments); - diagnoser?.Handle(HostSignal.BeforeProcessStart, new DiagnoserActionParameters(process, benchmarkCase, benchmarkId)); + diagnoser?.Handle(HostSignal.BeforeProcessStart, new DiagnoserActionParameters(process, benchmarkCase, benchmarkId)); - return Execute(process, benchmarkCase, broker, logger, consoleExitHandler, launchIndex, processOutputReader); - } + return Execute(process, benchmarkCase, broker, logger, consoleExitHandler, launchIndex, processOutputReader); } finally { @@ -78,7 +79,7 @@ private static ExecuteResult Execute(Process process, BenchmarkCase benchmarkCas { logger.WriteLineError($"// Failed to start the benchmark process: {ex}"); - return new ExecuteResult(true, null, null, Array.Empty(), Array.Empty(), Array.Empty(), launchIndex); + return new ExecuteResult(true, null, null, [], [], [], launchIndex); } broker.Diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); @@ -166,7 +167,7 @@ private static string GetMonoArguments(Job job, string exePath, string args, IRe { var arguments = job.HasValue(InfrastructureMode.ArgumentsCharacteristic) ? job.ResolveValue(InfrastructureMode.ArgumentsCharacteristic, resolver).OfType().ToArray() - : Array.Empty(); + : []; // from mono --help: "Usage is: mono [options] program [program-options]" var builder = new StringBuilder(30); @@ -174,7 +175,9 @@ private static string GetMonoArguments(Job job, string exePath, string args, IRe builder.Append(job.ResolveValue(EnvironmentMode.JitCharacteristic, resolver) == Jit.Llvm ? "--llvm" : "--nollvm"); foreach (var argument in arguments) + { builder.Append($" {argument.TextRepresentation}"); + } builder.Append($" \"{exePath}\" "); builder.Append(args); diff --git a/src/BenchmarkDotNet/Toolchains/IExecutor.cs b/src/BenchmarkDotNet/Toolchains/IExecutor.cs index 85604ef369..1ffd2aad52 100644 --- a/src/BenchmarkDotNet/Toolchains/IExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/IExecutor.cs @@ -1,10 +1,10 @@ using BenchmarkDotNet.Toolchains.Parameters; using BenchmarkDotNet.Toolchains.Results; +using System.Threading.Tasks; -namespace BenchmarkDotNet.Toolchains +namespace BenchmarkDotNet.Toolchains; + +public interface IExecutor { - public interface IExecutor - { - ExecuteResult Execute(ExecuteParameters executeParameters); - } + ValueTask ExecuteAsync(ExecuteParameters executeParameters); } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/ConsumableTypeInfo.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/ConsumableTypeInfo.cs deleted file mode 100644 index 123f07bf7a..0000000000 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/ConsumableTypeInfo.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableReflectionHelpers; - -namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation -{ - public class ConsumableTypeInfo - { - public ConsumableTypeInfo(Type methodReturnType) - { - if (methodReturnType == null) - throw new ArgumentNullException(nameof(methodReturnType)); - - OriginMethodReturnType = methodReturnType; - - // Only support (Value)Task for parity with other toolchains (and so we can use AwaitHelper). - IsAwaitable = methodReturnType == typeof(Task) || methodReturnType == typeof(ValueTask) - || (methodReturnType.GetTypeInfo().IsGenericType - && (methodReturnType.GetTypeInfo().GetGenericTypeDefinition() == typeof(Task<>) - || methodReturnType.GetTypeInfo().GetGenericTypeDefinition() == typeof(ValueTask<>))); - - if (!IsAwaitable) - { - WorkloadMethodReturnType = methodReturnType; - } - else - { - WorkloadMethodReturnType = methodReturnType - .GetMethod(nameof(Task.GetAwaiter), BindingFlagsPublicInstance) - .ReturnType - .GetMethod(nameof(TaskAwaiter.GetResult), BindingFlagsPublicInstance) - .ReturnType; - GetResultMethod = Helpers.AwaitHelper.GetGetResultMethod(methodReturnType); - } - - if (WorkloadMethodReturnType == null) - throw new InvalidOperationException("Bug: (WorkloadMethodReturnType == null"); - - if (WorkloadMethodReturnType == typeof(void)) - { - IsVoid = true; - } - else if (WorkloadMethodReturnType.IsByRef) - { - IsByRef = true; - } - } - - public Type OriginMethodReturnType { get; } - public Type WorkloadMethodReturnType { get; } - - public MethodInfo? GetResultMethod { get; } - - public bool IsVoid { get; } - public bool IsByRef { get; } - - public bool IsAwaitable { get; } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncCoreEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncCoreEmitter.cs new file mode 100644 index 0000000000..c168fff8d2 --- /dev/null +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncCoreEmitter.cs @@ -0,0 +1,903 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Helpers.Reflection.Emit; +using Perfolizer.Horology; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants; + +namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation; + +partial class RunnableEmitter +{ + // TODO: update this to support runtime-async. + private sealed class AsyncCoreEmitter : RunnableEmitter + { + private FieldInfo workloadContinuerAndValueTaskSourceField; + private FieldInfo clockField; + private FieldInfo invokeCountField; + + protected override void EmitExtraFields(TypeBuilder fieldsContainerBuilder) + { + base.EmitExtraFields(fieldsContainerBuilder); + + workloadContinuerAndValueTaskSourceField = fieldsContainerBuilder.DefineField( + WorkloadContinuerAndValueTaskSourceFieldName, + typeof(WorkloadContinuerAndValueTaskSource), + FieldAttributes.Public); + clockField = fieldsContainerBuilder.DefineField( + ClockFieldName, + typeof(IClock), + FieldAttributes.Public); + invokeCountField = fieldsContainerBuilder.DefineField( + InvokeCountFieldName, + typeof(long), + FieldAttributes.Public); + } + + protected override void EmitExtraGlobalCleanup(ILGenerator ilBuilder, LocalBuilder? thisLocal) + { + // __fieldsContainer.workloadContinuerAndValueTaskSource?.Complete(); + + var callCompleteLabel = ilBuilder.DefineLabel(); // IL_0022 + var skipCompleteLabel = ilBuilder.DefineLabel(); // IL_0027 + + /* + // WorkloadContinuerAndValueTaskSource workloadContinuerAndValueTaskSource = runnable_.__fieldsContainer.workloadContinuerAndValueTaskSource; + IL_0011: ldloc.1 + IL_0012: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer + IL_0017: ldfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::workloadContinuerAndValueTaskSource + */ + if (thisLocal != null) + { + ilBuilder.EmitLdloc(thisLocal); + } + else + { + ilBuilder.Emit(OpCodes.Ldarg_0); + } + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldfld, workloadContinuerAndValueTaskSourceField); + /* + // if (workloadContinuerAndValueTaskSource != null) + IL_001c: dup + IL_001d: brtrue.s IL_0022 + */ + ilBuilder.Emit(OpCodes.Dup); + ilBuilder.Emit(OpCodes.Brtrue_S, callCompleteLabel); + + /* + IL_001f: pop + IL_0020: br.s IL_0027 + */ + ilBuilder.Emit(OpCodes.Pop); + ilBuilder.Emit(OpCodes.Br, skipCompleteLabel); + + /* + // workloadContinuerAndValueTaskSource.Complete(); + IL_0022: call instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::Complete() + */ + ilBuilder.MarkLabel(callCompleteLabel); + ilBuilder.Emit(OpCodes.Call, typeof(WorkloadContinuerAndValueTaskSource).GetMethod(nameof(WorkloadContinuerAndValueTaskSource.Complete), BindingFlags.Public | BindingFlags.Instance)); + + ilBuilder.MarkLabel(skipCompleteLabel); + } + + protected override void EmitCoreImpl() + { + EmitOverhead(); + EmitWorkload(); + } + + private void EmitOverhead() + { + var noUnrollMethod = EmitNoUnrollMethod(); + EmitUnrollMethod(); + + MethodInfo EmitNoUnrollMethod() + { + /* + // private ValueTask OverheadActionNoUnroll(long invokeCount, IClock clock) + .method private hidebysig + instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1 OverheadActionNoUnroll ( + int64 invokeCount, + class [Perfolizer]Perfolizer.Horology.IClock clock + ) cil managed flags(0200) + */ + var invokeCountArg = new EmitParameterInfo(0, InvokeCountParamName, typeof(long)); + var methodBuilder = runnableBuilder + .DefineNonVirtualInstanceMethod( + OverheadActionNoUnrollMethodName, + MethodAttributes.Private, + EmitParameterInfo.CreateReturnParameter(typeof(ValueTask)), + [ + invokeCountArg, + new EmitParameterInfo(1, ClockParamName, typeof(IClock)) + ] + ) + .SetAggressiveOptimizationImplementationFlag(); + invokeCountArg.SetMember(methodBuilder); + + var ilBuilder = methodBuilder.GetILGenerator(); + + /* + .locals init ( + [0] valuetype [Perfolizer]Perfolizer.Horology.StartedClock startedClock + ) + */ + var startedClockLocal = ilBuilder.DeclareLocal(typeof(StartedClock)); + + var startLoopLabel = ilBuilder.DefineLabel(); // IL_000f + + /* + // StartedClock startedClock = ClockExtensions.Start(clock); + IL_0000: ldarg.2 + IL_0001: call valuetype [Perfolizer]Perfolizer.Horology.StartedClock [Perfolizer]Perfolizer.Horology.ClockExtensions::Start(class [Perfolizer]Perfolizer.Horology.IClock) + IL_0006: stloc.0 + */ + ilBuilder.Emit(OpCodes.Ldarg_2); + ilBuilder.Emit(OpCodes.Call, GetStartClockMethod()); + ilBuilder.EmitStloc(startedClockLocal); + + // loop + ilBuilder.EmitLoopBeginFromArgToZero(out var loopStartLabel, out var loopHeadLabel); + { + /* + // __Overhead(); + IL_0009: ldarg.0 + IL_000a: call instance void BenchmarkDotNet.Autogenerated.Runnable_1::__Overhead() + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + EmitLoadArgFieldsForCall(ilBuilder, null); + ilBuilder.Emit(OpCodes.Call, overheadImplementationMethod); + } + ilBuilder.EmitLoopEndFromArgToZero(loopStartLabel, loopHeadLabel, invokeCountArg); + + /* + // return new ValueTask(startedClock.GetElapsed()); + IL_001a: ldloca.s 0 + IL_001c: call instance valuetype [Perfolizer]Perfolizer.Horology.ClockSpan [Perfolizer]Perfolizer.Horology.StartedClock::GetElapsed() + IL_0021: newobj instance void valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1::.ctor(!0) + IL_0026: ret + */ + ilBuilder.EmitLdloca(startedClockLocal); + ilBuilder.Emit(OpCodes.Call, typeof(StartedClock).GetMethod(nameof(StartedClock.GetElapsed), BindingFlags.Public | BindingFlags.Instance)); + ilBuilder.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor([typeof(ClockSpan)])); + ilBuilder.Emit(OpCodes.Ret); + + return methodBuilder; + } + + void EmitUnrollMethod() + { + /* + // private ValueTask OverheadActionUnroll(long invokeCount, IClock clock) + .method private hidebysig + instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1 OverheadActionUnroll ( + int64 invokeCount, + class [Perfolizer]Perfolizer.Horology.IClock clock + ) cil managed flags(0200) + */ + var methodBuilder = runnableBuilder + .DefineNonVirtualInstanceMethod( + OverheadActionUnrollMethodName, + MethodAttributes.Private, + EmitParameterInfo.CreateReturnParameter(typeof(ValueTask)), + [ + new EmitParameterInfo(0, InvokeCountParamName, typeof(long)), + new EmitParameterInfo(1, ClockParamName, typeof(IClock)) + ] + ) + .SetAggressiveOptimizationImplementationFlag(); + + var ilBuilder = methodBuilder.GetILGenerator(); + + /* + // return OverheadActionNoUnroll(invokeCount * 16, clock); + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: ldc.i4.s 16 + IL_0004: conv.i8 + IL_0005: mul + IL_0006: ldarg.2 + IL_0007: call instance valuetype [System.Threading.Tasks.Extensions]System.Threading.Tasks.ValueTask`1 BenchmarkDotNet.Autogenerated.Runnable_0::OverheadActionNoUnroll(int64, class [Perfolizer]Perfolizer.Horology.IClock) + IL_000c: ret + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldarg_1); + ilBuilder.EmitLdc_I4(jobUnrollFactor); + ilBuilder.Emit(OpCodes.Conv_I8); + ilBuilder.Emit(OpCodes.Mul); + ilBuilder.Emit(OpCodes.Ldarg_2); + ilBuilder.Emit(OpCodes.Call, noUnrollMethod); + ilBuilder.Emit(OpCodes.Ret); + } + } + + private Type GetWorkloadCoreAsyncMethodBuilderType() + { + // If the benchmark method overrode the caller type, use that type to get the builder type. + if (Descriptor.WorkloadMethod.ResolveAttribute() is AsyncCallerTypeAttribute asyncCallerTypeAttribute) + { + return GetBuilderTypeFromUserSpecifiedAsyncType(asyncCallerTypeAttribute.AsyncCallerType); + } + // If the benchmark method overrode the builder, use the same builder. + if (Descriptor.WorkloadMethod.GetAsyncMethodBuilderAttribute() is { } methodAttr + && methodAttr.GetType().GetProperty(nameof(AsyncMethodBuilderAttribute.BuilderType), BindingFlags.Public | BindingFlags.Instance)?.GetValue(methodAttr) is Type methodBuilderType) + { + return methodBuilderType; + } + if (Descriptor.WorkloadMethod.ReturnType.GetAsyncMethodBuilderAttribute() is { } typeAttr + && typeAttr.GetType().GetProperty(nameof(AsyncMethodBuilderAttribute.BuilderType), BindingFlags.Public | BindingFlags.Instance)?.GetValue(typeAttr) is Type typeBuilderType) + { + return GetConcreteBuilderType(typeBuilderType); + } + // Task and Task are not annotated with their builder type, the C# compiler special-cases them. + if (Descriptor.WorkloadMethod.ReturnType.IsGenericType && Descriptor.WorkloadMethod.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + return GetConcreteBuilderType(typeof(AsyncTaskMethodBuilder<>)); + } + // Fallback to AsyncTaskMethodBuilder if the benchmark return type is Task or any awaitable type that is not a custom task-like type. + return typeof(AsyncTaskMethodBuilder); + + Type GetBuilderTypeFromUserSpecifiedAsyncType(Type asyncType) + { + if (asyncType.GetAsyncMethodBuilderAttribute() is { } typeAttr + && typeAttr.GetType().GetProperty(nameof(AsyncMethodBuilderAttribute.BuilderType), BindingFlags.Public | BindingFlags.Instance)?.GetValue(typeAttr) is Type typeBuilderType) + { + return GetConcreteBuilderType(typeBuilderType); + } + // Task and Task are not annotated with their builder type, the C# compiler special-cases them. + if (asyncType == typeof(Task)) + { + return typeof(AsyncTaskMethodBuilder); + } + if (asyncType.IsGenericType && asyncType.GetGenericTypeDefinition() == typeof(Task<>)) + { + return typeof(AsyncTaskMethodBuilder<>).MakeGenericType([asyncType.GetGenericArguments()[0]]); + } + throw new NotSupportedException($"AsyncMethodBuilderAttribute not found on type {asyncType.GetDisplayName()} from {Descriptor.DisplayInfo}"); + } + + Type GetConcreteBuilderType(Type builderType) + { + if (!builderType.IsGenericTypeDefinition) + { + return builderType; + } + if (builderType.GetGenericArguments().Length != 1) + { + throw new NotSupportedException($"AsyncMethodBuilder {builderType.GetDisplayName()} has generic arity greater than 1."); + } + var resultType = Descriptor.WorkloadMethod.ReturnType + .GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance) + .ReturnType + .GetMethod(nameof(TaskAwaiter.GetResult), BindingFlags.Public | BindingFlags.Instance) + .ReturnType; + return builderType.MakeGenericType([resultType]); + } + } + + private void EmitWorkload() + { + var asyncMethodBuilderType = GetWorkloadCoreAsyncMethodBuilderType(); + var builderInfo = BeginAsyncStateMachineTypeBuilder(WorkloadCoreMethodName, asyncMethodBuilderType, runnableBuilder); + var (asyncStateMachineTypeBuilder, publicFields, (ilBuilder, endTryLabel, returnLabel, stateLocal, thisLocal, returnDefaultLocal)) = builderInfo; + var (stateField, builderField, _) = publicFields; + var startedClockField = asyncStateMachineTypeBuilder.DefineField( + "5__2", + typeof(StartedClock), + FieldAttributes.Private + ); + var workloadContinuerAwaiterField = asyncStateMachineTypeBuilder.DefineField( + "<>u__1", + typeof(object), + FieldAttributes.Private + ); + var benchmarkAwaiterField = asyncStateMachineTypeBuilder.DefineField( + "<>u__2", + Descriptor.WorkloadMethod.ReturnType + .GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance) + .ReturnType, + FieldAttributes.Private + ); + EmitMoveNextImpl(); + var asyncStateMachineType = CompleteAsyncStateMachineType(asyncMethodBuilderType, builderInfo); + + var workloadCoreMethod = EmitAsyncCallerStub(WorkloadCoreMethodName, asyncStateMachineType, publicFields); + + var startWorkloadMethod = EmitAsyncSingleCall(StartWorkloadMethodName, typeof(AsyncVoidMethodBuilder), workloadCoreMethod, false); + + var noUnrollMethod = EmitNoUnrollMethod(); + EmitUnrollMethod(); + + void EmitMoveNextImpl() + { + /* + .locals init ( + [3] class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource, + [4] valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1 awaitable, + [5] valuetype [System.Runtime]System.Runtime.CompilerServices.ValueTaskAwaiter`1, + [6] int64, + [7] class [System.Runtime]System.Exception e, + ) + */ + var workloadContinuerLocal = ilBuilder.DeclareLocal(typeof(WorkloadContinuerAndValueTaskSource)); + var benchmarkAwaitableLocal = Descriptor.WorkloadMethod.ReturnType.IsValueType + ? ilBuilder.DeclareLocal(Descriptor.WorkloadMethod.ReturnType) + : null; + var benchmarkAwaiterLocal = ilBuilder.DeclareLocal(benchmarkAwaiterField.FieldType); + var invokeCountLocal = ilBuilder.DeclareLocal(typeof(long)); + var exceptionLocal = ilBuilder.DeclareLocal(typeof(Exception)); + + var whileTrueLabel = ilBuilder.DefineLabel(); // IL_001d + var workloadContinuationLabel = ilBuilder.DefineLabel(); // IL_0059 + var workloadContinuationGetResultLabel = ilBuilder.DefineLabel(); // IL_0075 + var benchmarkContinuationLabel = ilBuilder.DefineLabel(); // IL_00f0 + var benchmarkContinuationGetResultLabel = ilBuilder.DefineLabel(); // IL_010d + var startClockLabel = ilBuilder.DefineLabel(); // IL_009a + var callBenchmarkLoopLabel = ilBuilder.DefineLabel(); // IL_0115 + var callBenchmarkLabel = ilBuilder.DefineLabel(); // IL_00b2 + + // Not sure why Roslyn emits this, but we match it exactly. + /* + IL_000e: ldloc.0 + // _ = 1; + IL_000f: ldc.i4.1 + IL_0010: pop + IL_0011: pop + IL_0012: nop + */ + ilBuilder.EmitLdloc(stateLocal); + ilBuilder.Emit(OpCodes.Ldc_I4_1); + ilBuilder.Emit(OpCodes.Pop); + ilBuilder.Emit(OpCodes.Pop); + ilBuilder.Emit(OpCodes.Nop); + + ilBuilder.BeginExceptionBlock(); + { + /* + // if (num != 0) + IL_0013: ldloc.0 + IL_0014: brfalse.s IL_0059 + */ + ilBuilder.EmitLdloc(stateLocal); + ilBuilder.Emit(OpCodes.Brfalse_S, workloadContinuationLabel); + + /* + // if (num != 1) + IL_0016: ldloc.0 + IL_0017: ldc.i4.1 + IL_0018: beq IL_00f0 + */ + ilBuilder.EmitLdloc(stateLocal); + ilBuilder.Emit(OpCodes.Ldc_I4_1); + ilBuilder.Emit(OpCodes.Beq, benchmarkContinuationLabel); + + /* + // awaiter2 = runnable_.__fieldsContainer.workloadContinuerAndValueTaskSource.GetAwaiter(); + IL_001d: ldloc.1 + IL_001e: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0023: ldfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::workloadContinuerAndValueTaskSource + IL_0028: callvirt instance class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::GetAwaiter() + IL_002d: stloc.3 + */ + ilBuilder.MarkLabel(whileTrueLabel); + ilBuilder.EmitLdloc(thisLocal); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldfld, workloadContinuerAndValueTaskSourceField); + ilBuilder.Emit(OpCodes.Callvirt, typeof(WorkloadContinuerAndValueTaskSource).GetMethod(nameof(WorkloadContinuerAndValueTaskSource.GetAwaiter), BindingFlags.Public | BindingFlags.Instance)); + ilBuilder.EmitStloc(workloadContinuerLocal); + /* + // if (!awaiter2.IsCompleted) + IL_002e: ldloc.3 + IL_002f: callvirt instance bool [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::get_IsCompleted() + IL_0034: brtrue.s IL_0075 + */ + ilBuilder.EmitLdloc(workloadContinuerLocal); + ilBuilder.Emit(OpCodes.Callvirt, typeof(WorkloadContinuerAndValueTaskSource).GetProperty(nameof(WorkloadContinuerAndValueTaskSource.IsCompleted), BindingFlags.Public | BindingFlags.Instance).GetMethod); + ilBuilder.Emit(OpCodes.Brtrue_S, workloadContinuationGetResultLabel); + /* + // num = (<>1__state = 0); + IL_0036: ldarg.0 + IL_0037: ldc.i4.0 + IL_0038: dup + IL_0039: stloc.0 + IL_003a: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>1__state' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_0); + ilBuilder.Emit(OpCodes.Dup); + ilBuilder.EmitStloc(stateLocal); + ilBuilder.Emit(OpCodes.Stfld, stateField); + /* + // <>u__1 = awaiter2; + IL_003f: ldarg.0 + IL_0040: ldloc.3 + IL_0041: stfld object BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>u__1' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.EmitLdloc(workloadContinuerLocal); + ilBuilder.Emit(OpCodes.Stfld, workloadContinuerAwaiterField); + /* + // <>t__builder.AwaitUnsafeOnCompletedd__17>(ref awaiter2, ref this); + IL_0046: ldarg.0 + IL_0047: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder`1 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>t__builder' + IL_004c: ldloca.s 3 + IL_004e: ldarg.0 + IL_004f: call instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder`1::AwaitUnsafeOnCompletedd__17'>(!!0&, !!1&) + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, builderField); + ilBuilder.EmitLdloca(workloadContinuerLocal); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Call, GetAwaitOnCompletedMethod(asyncMethodBuilderType, typeof(WorkloadContinuerAndValueTaskSource), asyncStateMachineTypeBuilder)); + /* + // return; + IL_0054: leave IL_01a7 + */ + ilBuilder.Emit(OpCodes.Leave, returnLabel); + + /* + // WorkloadContinuerAndValueTaskSource awaiter2 = (WorkloadContinuerAndValueTaskSource)<>u__1; + IL_0059: ldarg.0 + IL_005a: ldfld object BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>u__1' + IL_005f: castclass [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource + IL_0064: stloc.3 + */ + ilBuilder.MarkLabel(workloadContinuationLabel); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldfld, workloadContinuerAwaiterField); + ilBuilder.Emit(OpCodes.Castclass, typeof(WorkloadContinuerAndValueTaskSource)); + ilBuilder.EmitStloc(workloadContinuerLocal); + /* + // <>u__1 = null; + IL_0065: ldarg.0 + IL_0066: ldnull + IL_0067: stfld object BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>u__1' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.EmitSetFieldToDefault(workloadContinuerAwaiterField); + /* + // num = (<>1__state = -1); + IL_006c: ldarg.0 + IL_006d: ldc.i4.m1 + IL_006e: dup + IL_006f: stloc.0 + IL_0070: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>1__state' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_M1); + ilBuilder.Emit(OpCodes.Dup); + ilBuilder.EmitStloc(stateLocal); + ilBuilder.Emit(OpCodes.Stfld, stateField); + + /* + // awaiter2.GetResult(); + IL_0075: ldloc.3 + IL_0076: callvirt instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::GetResult() + */ + ilBuilder.MarkLabel(workloadContinuationGetResultLabel); + ilBuilder.EmitLdloc(workloadContinuerLocal); + ilBuilder.Emit(OpCodes.Callvirt, typeof(WorkloadContinuerAndValueTaskSource).GetMethod(nameof(WorkloadContinuerAndValueTaskSource.GetResult), BindingFlags.Public | BindingFlags.Instance)); + /* + // if (!runnable_.__fieldsContainer.workloadContinuerAndValueTaskSource.IsCompleted) + IL_007b: ldloc.1 + IL_007c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0081: ldfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::workloadContinuerAndValueTaskSource + IL_0086: callvirt instance bool [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::get_IsCompleted() + IL_008b: brfalse.s IL_009a + */ + ilBuilder.EmitLdloc(thisLocal); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldfld, workloadContinuerAndValueTaskSourceField); + ilBuilder.Emit(OpCodes.Callvirt, typeof(WorkloadContinuerAndValueTaskSource).GetProperty(nameof(WorkloadContinuerAndValueTaskSource.IsCompleted), BindingFlags.Public | BindingFlags.Instance).GetMethod); + ilBuilder.Emit(OpCodes.Brfalse_S, startClockLabel); + + /* + // result = default(GcStats); + IL_008d: ldloca.s 2 + IL_008f: initobj [BenchmarkDotNet]BenchmarkDotNet.Engines.GcStats + */ + ilBuilder.MaybeEmitSetLocalToDefault(returnDefaultLocal); + /* + IL_0095: leave IL_0193 + */ + ilBuilder.Emit(OpCodes.Leave, endTryLabel); + + /* + // 5__2 = ClockExtensions.Start(runnable_.__fieldsContainer.clock); + IL_009a: ldarg.0 + IL_009b: ldloc.1 + IL_009c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_00a1: ldfld class [Perfolizer]Perfolizer.Horology.IClock BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::clock + IL_00a6: call valuetype [Perfolizer]Perfolizer.Horology.StartedClock [Perfolizer]Perfolizer.Horology.ClockExtensions::Start(class [Perfolizer]Perfolizer.Horology.IClock) + IL_00ab: stfld valuetype [Perfolizer]Perfolizer.Horology.StartedClock BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'5__2' + */ + ilBuilder.MarkLabel(startClockLabel); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.EmitLdloc(thisLocal); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldfld, clockField); + ilBuilder.Emit(OpCodes.Call, GetStartClockMethod()); + ilBuilder.Emit(OpCodes.Stfld, startedClockField); + /* + // goto IL_0115; + IL_00b0: br.s IL_0115 + */ + ilBuilder.Emit(OpCodes.Br, callBenchmarkLoopLabel); + + /* + // awaiter = ((AllSetupAndCleanupAttributeBenchmarksTask)runnable_).Benchmark2().GetAwaiter(); + IL_00b2: ldloc.1 + IL_00b3: call instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1 [BenchmarkDotNet.IntegrationTests]BenchmarkDotNet.IntegrationTests.AllSetupAndCleanupTest/AllSetupAndCleanupAttributeBenchmarksTask::Benchmark2() + IL_00b8: stloc.s 4 + IL_00ba: ldloca.s 4 + */ + ilBuilder.MarkLabel(callBenchmarkLabel); + if (!Descriptor.WorkloadMethod.IsStatic) + { + ilBuilder.EmitLdloc(thisLocal); + } + EmitLoadArgFieldsForCall(ilBuilder, thisLocal); + ilBuilder.Emit(OpCodes.Call, Descriptor.WorkloadMethod); + if (benchmarkAwaitableLocal == null) + { + ilBuilder.Emit(OpCodes.Callvirt, Descriptor.WorkloadMethod.ReturnType.GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance)); + } + else + { + ilBuilder.EmitStloc(benchmarkAwaitableLocal); + ilBuilder.EmitLdloca(benchmarkAwaitableLocal); + ilBuilder.Emit(OpCodes.Call, Descriptor.WorkloadMethod.ReturnType.GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance)); + } + /* + // if (!awaiter.IsCompleted) + IL_00bc: call instance valuetype [System.Runtime]System.Runtime.CompilerServices.ValueTaskAwaiter`1 valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1::GetAwaiter() + IL_00c1: stloc.s 5 + IL_00c3: ldloca.s 5 + IL_00c5: call instance bool valuetype [System.Runtime]System.Runtime.CompilerServices.ValueTaskAwaiter`1::get_IsCompleted() + IL_00ca: brtrue.s IL_010d + */ + ilBuilder.EmitStloc(benchmarkAwaiterLocal); + ilBuilder.EmitLdloca(benchmarkAwaiterLocal); + ilBuilder.Emit(OpCodes.Call, benchmarkAwaiterField.FieldType.GetProperty(nameof(TaskAwaiter.IsCompleted), BindingFlags.Public | BindingFlags.Instance).GetMethod); + ilBuilder.Emit(OpCodes.Brtrue_S, benchmarkContinuationGetResultLabel); + + /* + // num = (<>1__state = 1); + IL_00cc: ldarg.0 + IL_00cd: ldc.i4.1 + IL_00ce: dup + IL_00cf: stloc.0 + IL_00d0: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>1__state' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_1); + ilBuilder.Emit(OpCodes.Dup); + ilBuilder.EmitStloc(stateLocal); + ilBuilder.Emit(OpCodes.Stfld, stateField); + /* + // <>u__2 = awaiter; + IL_00d5: ldarg.0 + IL_00d6: ldloc.s 5 + IL_00d8: stfld valuetype [System.Runtime]System.Runtime.CompilerServices.ValueTaskAwaiter`1 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>u__2' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.EmitLdloc(benchmarkAwaiterLocal); + ilBuilder.Emit(OpCodes.Stfld, benchmarkAwaiterField); + /* + // <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); + IL_00dd: ldarg.0 + IL_00de: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder`1 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>t__builder' + IL_00e3: ldloca.s 5 + IL_00e5: ldarg.0 + IL_00e6: call instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder`1::AwaitUnsafeOnCompleted, valuetype BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'>(!!0&, !!1&) + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, builderField); + ilBuilder.EmitLdloca(benchmarkAwaiterLocal); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Call, GetAwaitOnCompletedMethod(asyncMethodBuilderType, benchmarkAwaiterField.FieldType, asyncStateMachineTypeBuilder)); + /* + // return; + IL_00eb: leave IL_01a7 + */ + ilBuilder.Emit(OpCodes.Leave, returnLabel); + + /* + // awaiter = <>u__2; + IL_00f0: ldarg.0 + IL_00f1: ldfld valuetype [System.Runtime]System.Runtime.CompilerServices.ValueTaskAwaiter`1 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>u__2' + IL_00f6: stloc.s 5 + */ + ilBuilder.MarkLabel(benchmarkContinuationLabel); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldfld, benchmarkAwaiterField); + ilBuilder.EmitStloc(benchmarkAwaiterLocal); + /* + // <>u__2 = default(ValueTaskAwaiter); + IL_00f8: ldarg.0 + IL_00f9: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.ValueTaskAwaiter`1 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>u__2' + IL_00fe: initobj valuetype [System.Runtime]System.Runtime.CompilerServices.ValueTaskAwaiter`1 + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.EmitSetFieldToDefault(benchmarkAwaiterField); + /* + // num = (<>1__state = -1); + IL_0104: ldarg.0 + IL_0105: ldc.i4.m1 + IL_0106: dup + IL_0107: stloc.0 + IL_0108: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'<>1__state' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_M1); + ilBuilder.Emit(OpCodes.Dup); + ilBuilder.EmitStloc(stateLocal); + ilBuilder.Emit(OpCodes.Stfld, stateField); + + /* + // awaiter.GetResult(); + IL_010d: ldloca.s 5 + IL_010f: call instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.ValueTaskAwaiter`1::GetResult() + IL_0114: pop + */ + ilBuilder.MarkLabel(benchmarkContinuationGetResultLabel); + ilBuilder.EmitLdloca(benchmarkAwaiterLocal); + var benchmarkAwaiterGetResultMethod = benchmarkAwaiterField.FieldType.GetMethod(nameof(TaskAwaiter.GetResult), BindingFlags.Public | BindingFlags.Instance); + ilBuilder.Emit(OpCodes.Call, benchmarkAwaiterGetResultMethod); + if (benchmarkAwaiterGetResultMethod.ReturnType != typeof(void)) + { + ilBuilder.Emit(OpCodes.Pop); + } + + /* + // if (--runnable_.__fieldsContainer.invokeCount >= 0) + IL_0115: ldloc.1 + IL_0116: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_011b: ldflda int64 BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::invokeCount + IL_0120: dup + IL_0121: ldind.i8 + IL_0122: ldc.i4.1 + IL_0123: conv.i8 + IL_0124: sub + IL_0125: stloc.s 6 + IL_0127: ldloc.s 6 + IL_0129: stind.i8 + */ + ilBuilder.MarkLabel(callBenchmarkLoopLabel); + ilBuilder.EmitLdloc(thisLocal); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldflda, invokeCountField); + ilBuilder.Emit(OpCodes.Dup); + ilBuilder.Emit(OpCodes.Ldind_I8); + ilBuilder.Emit(OpCodes.Ldc_I4_1); + ilBuilder.Emit(OpCodes.Conv_I8); + ilBuilder.Emit(OpCodes.Sub); + ilBuilder.EmitStloc(invokeCountLocal); + ilBuilder.EmitLdloc(invokeCountLocal); + ilBuilder.Emit(OpCodes.Stind_I8); + /* + // runnable_.__fieldsContainer.workloadContinuerAndValueTaskSource.SetResult(5__2.GetElapsed()); + IL_012a: ldloc.s 6 + IL_012c: ldc.i4.0 + IL_012d: conv.i8 + IL_012e: bge.s IL_00b2 + */ + ilBuilder.EmitLdloc(invokeCountLocal); + ilBuilder.Emit(OpCodes.Ldc_I4_0); + ilBuilder.Emit(OpCodes.Conv_I8); + ilBuilder.Emit(OpCodes.Bge, callBenchmarkLabel); + + /* + IL_0130: ldloc.1 + IL_0131: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0136: ldfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::workloadContinuerAndValueTaskSource + IL_013b: ldarg.0 + IL_013c: ldflda valuetype [Perfolizer]Perfolizer.Horology.StartedClock BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'5__2' + IL_0141: call instance valuetype [Perfolizer]Perfolizer.Horology.ClockSpan [Perfolizer]Perfolizer.Horology.StartedClock::GetElapsed() + IL_0146: callvirt instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::SetResult(valuetype [Perfolizer]Perfolizer.Horology.ClockSpan) + */ + ilBuilder.EmitLdloc(thisLocal); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldfld, workloadContinuerAndValueTaskSourceField); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, startedClockField); + ilBuilder.Emit(OpCodes.Call, typeof(StartedClock).GetMethod(nameof(StartedClock.GetElapsed), BindingFlags.Public | BindingFlags.Instance)); + ilBuilder.Emit(OpCodes.Callvirt, typeof(WorkloadContinuerAndValueTaskSource).GetMethod(nameof(WorkloadContinuerAndValueTaskSource.SetResult), [typeof(ClockSpan)])); + /* + // 5__2 = default(StartedClock); + IL_014b: ldarg.0 + IL_014c: ldflda valuetype [Perfolizer]Perfolizer.Horology.StartedClock BenchmarkDotNet.Autogenerated.Runnable_1/'<__WorkloadCore>d__17'::'5__2' + IL_0151: initobj [Perfolizer]Perfolizer.Horology.StartedClock + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.EmitSetFieldToDefault(startedClockField); + /* + // goto IL_001d; + IL_0157: br IL_001d + */ + ilBuilder.Emit(OpCodes.Br, whileTrueLabel); + } + // end .try + ilBuilder.BeginCatchBlock(typeof(Exception)); + { + /* + // catch (Exception exception) + IL_015c: stloc.s 7 + */ + ilBuilder.EmitStloc(exceptionLocal); + /* + // runnable_.__fieldsContainer.workloadContinuerAndValueTaskSource.SetException(exception); + IL_015e: ldloc.1 + IL_015f: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0164: ldfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::workloadContinuerAndValueTaskSource + IL_0169: ldloc.s 7 + IL_016b: callvirt instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::SetException(class [System.Runtime]System.Exception) + */ + ilBuilder.EmitLdloc(thisLocal); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldfld, workloadContinuerAndValueTaskSourceField); + ilBuilder.EmitLdloc(exceptionLocal); + ilBuilder.Emit(OpCodes.Callvirt, typeof(WorkloadContinuerAndValueTaskSource).GetMethod(nameof(WorkloadContinuerAndValueTaskSource.SetException), [typeof(Exception)])); + /* + // result = default(GcStats); + IL_0170: ldloca.s 2 + IL_0172: initobj [BenchmarkDotNet]BenchmarkDotNet.Engines.GcStats + IL_0178: leave.s IL_0193 + */ + ilBuilder.MaybeEmitSetLocalToDefault(returnDefaultLocal); + + ilBuilder.EndExceptionBlock(); + } // end handler + } + + MethodInfo EmitNoUnrollMethod() + { + /* + // private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) + .method private hidebysig + instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1 WorkloadActionNoUnroll ( + int64 invokeCount, + class [Perfolizer]Perfolizer.Horology.IClock clock + ) cil managed flags(0200) + */ + var methodBuilder = runnableBuilder + .DefineNonVirtualInstanceMethod( + WorkloadActionNoUnrollMethodName, + MethodAttributes.Private, + EmitParameterInfo.CreateReturnParameter(typeof(ValueTask)), + [ + new EmitParameterInfo(0, InvokeCountParamName, typeof(long)), + new EmitParameterInfo(1, ClockParamName, typeof(IClock)) + ] + ) + .SetAggressiveOptimizationImplementationFlag(); + + var ilBuilder = methodBuilder.GetILGenerator(); + + var callContinueLabel = ilBuilder.DefineLabel(); // IL_003b + + /* + // __fieldsContainer.invokeCount = invokeCount; + IL_0000: ldarg.0 + IL_0001: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0006: ldarg.1 + IL_0007: stfld int64 BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::invokeCount + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldarg_1); + ilBuilder.Emit(OpCodes.Stfld, invokeCountField); + /* + // __fieldsContainer.clock = clock; + IL_000c: ldarg.0 + IL_000d: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0012: ldarg.2 + IL_0013: stfld class [Perfolizer]Perfolizer.Horology.IClock BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::clock + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldarg_2); + ilBuilder.Emit(OpCodes.Stfld, clockField); + /* + // if (__fieldsContainer.workloadContinuerAndValueTaskSource == null) + IL_0018: ldarg.0 + IL_0019: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_001e: ldfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::workloadContinuerAndValueTaskSource + IL_0023: brtrue.s IL_003b + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldfld, workloadContinuerAndValueTaskSourceField); + ilBuilder.Emit(OpCodes.Brtrue_S, callContinueLabel); + + /* + // __fieldsContainer.workloadContinuerAndValueTaskSource = new WorkloadContinuerAndValueTaskSource(); + IL_0025: ldarg.0 + IL_0026: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_002b: newobj instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::.ctor() + IL_0030: stfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::workloadContinuerAndValueTaskSource + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Newobj, typeof(WorkloadContinuerAndValueTaskSource).GetConstructor([])); + ilBuilder.Emit(OpCodes.Stfld, workloadContinuerAndValueTaskSourceField); + /* + // __StartWorkload(); + IL_0035: ldarg.0 + IL_0036: call instance void BenchmarkDotNet.Autogenerated.Runnable_1::__StartWorkload() + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Call, startWorkloadMethod); + + /* + // return __fieldsContainer.workloadContinuerAndValueTaskSource.Continue(); + IL_003b: ldarg.0 + IL_003c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0041: ldfld class [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::workloadContinuerAndValueTaskSource + IL_0046: callvirt instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1 [BenchmarkDotNet]BenchmarkDotNet.Engines.WorkloadContinuerAndValueTaskSource::Continue() + IL_004b: ret + */ + ilBuilder.MarkLabel(callContinueLabel); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + ilBuilder.Emit(OpCodes.Ldfld, workloadContinuerAndValueTaskSourceField); + ilBuilder.Emit(OpCodes.Callvirt, typeof(WorkloadContinuerAndValueTaskSource).GetMethod(nameof(WorkloadContinuerAndValueTaskSource.Continue), BindingFlags.Public | BindingFlags.Instance)); + ilBuilder.Emit(OpCodes.Ret); + + return methodBuilder; + } + + void EmitUnrollMethod() + { + /* + // private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) + .method private hidebysig + instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1 WorkloadActionUnroll ( + int64 invokeCount, + class [Perfolizer]Perfolizer.Horology.IClock clock + ) cil managed flags(0200) + */ + var methodBuilder = runnableBuilder + .DefineNonVirtualInstanceMethod( + WorkloadActionUnrollMethodName, + MethodAttributes.Private, + EmitParameterInfo.CreateReturnParameter(typeof(ValueTask)), + [ + new EmitParameterInfo(0, InvokeCountParamName, typeof(long)), + new EmitParameterInfo(1, ClockParamName, typeof(IClock)) + ] + ) + .SetAggressiveOptimizationImplementationFlag(); + + var ilBuilder = methodBuilder.GetILGenerator(); + + /* + // return WorkloadActionNoUnroll(invokeCount * 16, clock); + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: ldc.i4.s 16 + IL_0004: conv.i8 + IL_0005: mul + IL_0006: ldarg.2 + IL_0007: call instance valuetype [System.Threading.Tasks.Extensions]System.Threading.Tasks.ValueTask`1 BenchmarkDotNet.Autogenerated.Runnable_0::WorkloadActionNoUnroll(int64, class [Perfolizer]Perfolizer.Horology.IClock) + IL_000c: ret + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldarg_1); + ilBuilder.EmitLdc_I4(jobUnrollFactor); + ilBuilder.Emit(OpCodes.Conv_I8); + ilBuilder.Emit(OpCodes.Mul); + ilBuilder.Emit(OpCodes.Ldarg_2); + ilBuilder.Emit(OpCodes.Call, noUnrollMethod); + ilBuilder.Emit(OpCodes.Ret); + } + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncStateMachineEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncStateMachineEmitter.cs new file mode 100644 index 0000000000..82c6cd5253 --- /dev/null +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncStateMachineEmitter.cs @@ -0,0 +1,570 @@ +using BenchmarkDotNet.Helpers.Reflection.Emit; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants; + +namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation; + +partial class RunnableEmitter +{ + // Roslyn generates ordinals in declaration order of every member. + // We don't necessarily emit members in the same order (or at all in the case of Runnable_#.Run), so we map it to the expected Roslyn ordinal. + // This doesn't really matter for the runtime, but it helps with the NaiveRunnableEmitDiff tests. + private readonly Dictionary s_asyncMethodToOrdinalMap = new() + { + { GlobalSetupMethodName, 4 }, + { GlobalCleanupMethodName, 5 }, + { IterationSetupMethodName, 6 }, + { IterationCleanupMethodName, 7 }, + { OverheadActionUnrollMethodName, 12 }, + { OverheadActionNoUnrollMethodName, 13 }, + { WorkloadActionUnrollMethodName, 14 }, + { WorkloadActionNoUnrollMethodName, 15 }, + { StartWorkloadMethodName, 16 }, + { WorkloadCoreMethodName, 17 }, + }; + + private record struct AsyncStateMachineFields(FieldInfo StateField, FieldInfo BuilderField, FieldInfo? ThisField); + private record struct AsyncStateMachineMoveNextInfo(ILGenerator IlBuilder, Label EndTryLabel, Label ReturnLabel, LocalBuilder StateLocal, LocalBuilder? ThisLocal, LocalBuilder? ReturnDefaultLocal); + private record struct AsyncStateMachineBuilderInfo(TypeBuilder TypeBuilder, AsyncStateMachineFields PublicFields, AsyncStateMachineMoveNextInfo MoveNextInfo); + + private MethodInfo EmitAsyncCallerStub(string methodName, Type asyncStateMachineType, AsyncStateMachineFields asyncStateMachineFields) + { + var (stateField, builderField, thisField) = asyncStateMachineFields; + var asyncMethodBuilderType = builderField.FieldType; + + // AsyncVoidMethodBuilder for `async void` has no Task property. + var taskGetMethod = asyncMethodBuilderType.GetProperty(nameof(AsyncTaskMethodBuilder.Task), BindingFlags.Public | BindingFlags.Instance)?.GetMethod; + /* + .method private hidebysig + instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask __GlobalSetup () cil managed flags(0200) + */ + var methodBuilder = runnableBuilder + .DefineNonVirtualInstanceMethod( + methodName, + MethodAttributes.Private, + taskGetMethod?.ReturnParameter ?? EmitParameterInfo.CreateReturnVoidParameter() + ) + .SetAggressiveOptimizationImplementationFlag(); + + // [AsyncStateMachine(typeof(<__GlobalSetup>d__4))] + var attrCtor = typeof(AsyncStateMachineAttribute).GetConstructor([typeof(Type)]) + ?? throw new MissingMemberException(nameof(AsyncStateMachineAttribute)); + methodBuilder.SetCustomAttribute(new CustomAttributeBuilder(attrCtor, [asyncStateMachineType])); + + ILGenerator ilBuilder = methodBuilder.GetILGenerator(); + /* + .locals init ( + [0] valuetype BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4' + ) + */ + var asyncStateMachineLocal = ilBuilder.DeclareLocal(asyncStateMachineType); + /* + // stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create(); + IL_0000: ldloca.s 0 + IL_0002: call valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create() + IL_0007: stfld valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>t__builder' + */ + ilBuilder.EmitLdloca(asyncStateMachineLocal); + ilBuilder.Emit(OpCodes.Call, asyncMethodBuilderType.GetMethod(nameof(AsyncTaskMethodBuilder.Create), BindingFlags.Public | BindingFlags.Static)); + ilBuilder.Emit(OpCodes.Stfld, builderField); + if (thisField is not null) + { + /* + // stateMachine.<>4__this = this; + IL_000c: ldloca.s 0 + IL_000e: ldarg.0 + IL_000f: stfld class BenchmarkDotNet.Autogenerated.Runnable_0 BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>4__this' + */ + ilBuilder.EmitLdloca(asyncStateMachineLocal); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Stfld, thisField); + } + /* + // stateMachine.<>1__state = -1; + IL_0014: ldloca.s 0 + IL_0016: ldc.i4.m1 + IL_0017: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>1__state' + */ + ilBuilder.EmitLdloca(asyncStateMachineLocal); + ilBuilder.Emit(OpCodes.Ldc_I4_M1); + ilBuilder.Emit(OpCodes.Stfld, stateField); + /* + // stateMachine.<>t__builder.Start(ref stateMachine); + IL_001c: ldloca.s 0 + IL_001e: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>t__builder' + IL_0023: ldloca.s 0 + IL_0025: call instance void [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Startd__4'>(!!0&) + */ + ilBuilder.EmitLdloca(asyncStateMachineLocal); + ilBuilder.Emit(OpCodes.Ldflda, builderField); + ilBuilder.EmitLdloca(asyncStateMachineLocal); + var startMethod = asyncMethodBuilderType + .GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Single(method => method.IsGenericMethod && method.Name == nameof(AsyncTaskMethodBuilder.Start)) + .MakeGenericMethod(asyncStateMachineType); + ilBuilder.Emit(OpCodes.Call, startMethod); + + if (taskGetMethod != null) + { + /* + // return stateMachine.<>t__builder.Task; + IL_002a: ldloca.s 0 + IL_002c: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>t__builder' + IL_0031: call instance class [System.Runtime]System.Threading.Tasks.Task [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task() + */ + ilBuilder.EmitLdloca(asyncStateMachineLocal); + ilBuilder.Emit(OpCodes.Ldflda, builderField); + ilBuilder.Emit(OpCodes.Call, taskGetMethod); + } + /* + IL_0036: ret + */ + ilBuilder.Emit(OpCodes.Ret); + + return methodBuilder; + } + + private AsyncStateMachineBuilderInfo BeginAsyncStateMachineTypeBuilder(string callerMethodName, Type asyncMethodBuilderType, Type? thisType) + { + /* + [StructLayout(LayoutKind.Auto)] + [CompilerGenerated] + private struct <__GlobalSetup>d__4 : IAsyncStateMachine + */ + int ordinal = s_asyncMethodToOrdinalMap[callerMethodName]; + var asyncStateMachineTypeBuilder = runnableBuilder.DefineNestedType( + $"<{callerMethodName}>d__{ordinal}", + TypeAttributes.NestedPrivate | TypeAttributes.AutoLayout | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, + typeof(ValueType)); + nestedTypeBuilders.Add(asyncStateMachineTypeBuilder); + + asyncStateMachineTypeBuilder.AddInterfaceImplementation(typeof(IAsyncStateMachine)); + + var attrCtor = typeof(CompilerGeneratedAttribute).GetConstructor([]) + ?? throw new MissingMemberException(nameof(CompilerGeneratedAttribute)); + asyncStateMachineTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(attrCtor, [])); + + /* + .field public int32 '<>1__state' + .field public valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder '<>t__builder' + .field public class BenchmarkDotNet.Autogenerated.Runnable_0 '<>4__this' + */ + var stateField = asyncStateMachineTypeBuilder.DefineField("<>1__state", typeof(int), FieldAttributes.Public); + var builderField = asyncStateMachineTypeBuilder.DefineField("<>t__builder", asyncMethodBuilderType, FieldAttributes.Public); + var thisField = thisType is not null + ? asyncStateMachineTypeBuilder.DefineField("<>4__this", thisType, FieldAttributes.Public) + : null; + + var moveNextInfo = BeginEmitMoveNext(); + EmitSetStateMachine(); + + return new(asyncStateMachineTypeBuilder, new(stateField, builderField, thisField), moveNextInfo); + + AsyncStateMachineMoveNextInfo BeginEmitMoveNext() + { + /* + .method private final hidebysig newslot virtual + instance void MoveNext () cil managed flags(0200) + */ + var methodBuilder = asyncStateMachineTypeBuilder + .DefineMethod( + nameof(IAsyncStateMachine.MoveNext), + MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + typeof(void), + [] + ) + .SetAggressiveOptimizationImplementationFlag(); + + /* + .override method instance void [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext() + */ + asyncStateMachineTypeBuilder.DefineMethodOverride( + methodBuilder, + typeof(IAsyncStateMachine).GetMethod(nameof(IAsyncStateMachine.MoveNext)) + ); + + var ilBuilder = methodBuilder.GetILGenerator(); + + var stateLocal = ilBuilder.DeclareLocal(typeof(int)); + var thisLocal = thisType is not null + ? ilBuilder.DeclareLocal(thisType) + : null; + var returnDefaultLocal = asyncMethodBuilderType.GetMethod(nameof(AsyncTaskMethodBuilder.SetResult), BindingFlags.Public | BindingFlags.Instance).GetParameters() is [var setResultParam] + ? ilBuilder.DeclareLocal(setResultParam.ParameterType) + : null; + + var endTryLabel = ilBuilder.DefineLabel(); // IL_0082 + var returnLabel = ilBuilder.DefineLabel(); // IL_0095 + + /* + // int num = <>1__state; + IL_0000: ldarg.0 + IL_0001: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>1__state' + IL_0006: stloc.0 + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldfld, stateField); + ilBuilder.Emit(OpCodes.Stloc, stateLocal); + if (thisField is not null) + { + /* + // Runnable_0 runnable_ = <>4__this; + IL_0007: ldarg.0 + IL_0008: ldfld class BenchmarkDotNet.Autogenerated.Runnable_0 BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>4__this' + IL_000d: stloc.1 + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldfld, thisField); + ilBuilder.Emit(OpCodes.Stloc, thisLocal); + } + + ilBuilder.BeginExceptionBlock(); + { + // Core impl emitted by caller. + } + // Catch block and the rest emitted by CompleteAsyncStateMachineTypeBuilder. + + return new(ilBuilder, endTryLabel, returnLabel, stateLocal, thisLocal, returnDefaultLocal); + } + + void EmitSetStateMachine() + { + /* + .method private final hidebysig newslot virtual + instance void SetStateMachine ( + class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine + ) cil managed flags(0200) + */ + var methodBuilder = asyncStateMachineTypeBuilder + .DefineMethod( + nameof(IAsyncStateMachine.SetStateMachine), + MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + typeof(void), + [typeof(IAsyncStateMachine)] + ) + .SetAggressiveOptimizationImplementationFlag(); + + // [DebuggerHidden] + var attrCtor = typeof(DebuggerHiddenAttribute).GetConstructor([]) + ?? throw new MissingMemberException(nameof(DebuggerHiddenAttribute)); + methodBuilder.SetCustomAttribute(new CustomAttributeBuilder(attrCtor, [])); + + /* + .override method instance void [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine) + */ + asyncStateMachineTypeBuilder.DefineMethodOverride( + methodBuilder, + typeof(IAsyncStateMachine).GetMethod(nameof(IAsyncStateMachine.SetStateMachine)) + ); + + var ilBuilder = methodBuilder.GetILGenerator(); + + /* + // <>t__builder.SetStateMachine(stateMachine); + IL_0000: ldarg.0 + IL_0001: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>t__builder' + IL_0006: ldarg.1 + IL_0007: call instance void [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder::SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine) + IL_000c: ret + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, builderField); + ilBuilder.Emit(OpCodes.Ldarg_1); + ilBuilder.Emit(OpCodes.Call, asyncMethodBuilderType.GetMethod(nameof(AsyncTaskMethodBuilder.SetStateMachine), BindingFlags.Public | BindingFlags.Instance)); + ilBuilder.Emit(OpCodes.Ret); + } + } + + private Type CompleteAsyncStateMachineType(Type asyncMethodBuilderType, AsyncStateMachineBuilderInfo asyncStateMachineBuilderInfo) + { + var (asyncStateMachineTypeBuilder, (stateField, builderField, _), (ilBuilder, endTryLabel, returnLabel, _, _, returnDefaultLocal)) = asyncStateMachineBuilderInfo; + + var setResultMethod = asyncMethodBuilderType.GetMethod(nameof(AsyncTaskMethodBuilder.SetResult), BindingFlags.Public | BindingFlags.Instance); + + // end .try + ilBuilder.BeginCatchBlock(typeof(Exception)); + { + /* + // catch (Exception exception) + IL_006b: stloc.3 + */ + var exLocal = ilBuilder.DeclareLocal(typeof(Exception)); + ilBuilder.Emit(OpCodes.Stloc, exLocal); + /* + // <>1__state = -2; + IL_006c: ldarg.0 + IL_006d: ldc.i4.s -2 + IL_006f: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>1__state' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_S, (sbyte) -2); + ilBuilder.Emit(OpCodes.Stfld, stateField); + /* + // <>t__builder.SetException(exception); + IL_0074: ldarg.0 + IL_0075: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>t__builder' + IL_007a: ldloc.3 + IL_007b: call instance void [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder::SetException(class [System.Runtime]System.Exception) + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, builderField); + ilBuilder.EmitLdloc(exLocal); + ilBuilder.Emit(OpCodes.Call, asyncMethodBuilderType.GetMethod(nameof(AsyncTaskMethodBuilder.SetException), BindingFlags.Public | BindingFlags.Instance)); + + ilBuilder.EndExceptionBlock(); + } // end handler + /* + // <>1__state = -2; + IL_0082: ldarg.0 + IL_0083: ldc.i4.s -2 + IL_0085: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>1__state' + */ + // IL_0082: + ilBuilder.MarkLabel(endTryLabel); + + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_S, (sbyte) -2); + ilBuilder.Emit(OpCodes.Stfld, stateField); + /* + // <>t__builder.SetResult(); + IL_008a: ldarg.0 + IL_008b: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>t__builder' + IL_0090: call instance void [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder::SetResult() + + -or- + + // <>t__builder.SetResult(result); + IL_018f: ldarg.0 + IL_0190: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder`1 BenchmarkDotNet.Autogenerated.Runnable_0/'d__17'::'<>t__builder' + IL_0195: ldloc.2 + IL_0196: call instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder`1::SetResult(!0) + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, builderField); + if (returnDefaultLocal != null) + { + ilBuilder.EmitLdloc(returnDefaultLocal); + } + ilBuilder.Emit(OpCodes.Call, setResultMethod); + + // IL_0095: + ilBuilder.MarkLabel(returnLabel); + /* + IL_0095: ret + */ + ilBuilder.Emit(OpCodes.Ret); + + return asyncStateMachineTypeBuilder; + } + + /* + private async ValueTask __GlobalSetup() + { + await base.GlobalSetup(); + } + */ + private MethodInfo EmitAsyncSingleCall(string methodName, Type asyncMethodBuilderType, MethodInfo methodToCall, bool isGlobalCleanup) + { + bool needsThisLocal = !methodToCall.IsStatic + || (isGlobalCleanup && this is AsyncCoreEmitter); + var builderInfo = BeginAsyncStateMachineTypeBuilder(methodName, asyncMethodBuilderType, needsThisLocal ? runnableBuilder : null); + var (asyncStateMachineTypeBuilder, publicFields, (ilBuilder, _, returnLabel, stateLocal, thisLocal, _)) = builderInfo; + var (stateField, builderField, _) = publicFields; + EmitMoveNextImpl(); + var asyncStateMachineType = CompleteAsyncStateMachineType(asyncMethodBuilderType, builderInfo); + + return EmitAsyncCallerStub(methodName, asyncStateMachineType, publicFields); + + void EmitMoveNextImpl() + { + var getAwaiterMethod = methodToCall.ReturnType.GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance); + var awaiterType = getAwaiterMethod.ReturnType; + + // .field private valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter '<>u__1' + var awaiterField = asyncStateMachineTypeBuilder.DefineField( + "<>u__1", + awaiterType.IsValueType ? awaiterType : typeof(object), + FieldAttributes.Private); + + var awaiterLocal = ilBuilder.DeclareLocal(awaiterType); + var awaitableLocal = methodToCall.ReturnType.IsValueType + ? ilBuilder.DeclareLocal(methodToCall.ReturnType) + : null; + + var state0Label = ilBuilder.DefineLabel(); // IL_0046 + var completedLabel = ilBuilder.DefineLabel(); // IL_0062 + + /* + // if (num != 0) + IL_000e: ldloc.0 + IL_000f: brfalse.s IL_0046 + */ + ilBuilder.EmitLdloc(stateLocal); + ilBuilder.Emit(OpCodes.Brfalse_S, state0Label); + + if (isGlobalCleanup) + { + EmitExtraGlobalCleanup(ilBuilder, thisLocal); + } + + /* + // awaiter = runnable_.GlobalSetup().GetAwaiter(); + IL_0011: ldloc.1 + IL_0012: call instance class [System.Runtime]System.Threading.Tasks.Task [BenchmarkDotNet.IntegrationTests]BenchmarkDotNet.IntegrationTests.AllSetupAndCleanupTest/AllSetupAndCleanupAttributeBenchmarksTask::GlobalSetup() + IL_0017: callvirt instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter [System.Runtime]System.Threading.Tasks.Task::GetAwaiter() + IL_001c: stloc.2 + */ + if (!methodToCall.IsStatic) + { + ilBuilder.EmitLdloc(thisLocal); + } + ilBuilder.Emit(OpCodes.Call, methodToCall); + if (awaitableLocal is null) + { + ilBuilder.Emit(OpCodes.Callvirt, getAwaiterMethod); + } + else + { + ilBuilder.EmitStloc(awaitableLocal); + ilBuilder.EmitLdloca(awaitableLocal); + ilBuilder.Emit(OpCodes.Call, getAwaiterMethod); + } + ilBuilder.Emit(OpCodes.Stloc, awaiterLocal); + /* + // if (!awaiter.IsCompleted) + IL_001d: ldloca.s 2 + IL_001f: call instance bool [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::get_IsCompleted() + IL_0024: brtrue.s IL_0062 + */ + if (awaiterType.IsValueType) + { + ilBuilder.EmitLdloca(awaiterLocal); + ilBuilder.Emit(OpCodes.Call, awaiterType.GetProperty(nameof(TaskAwaiter.IsCompleted), BindingFlags.Public | BindingFlags.Instance).GetMethod); + } + else + { + ilBuilder.EmitLdloc(awaiterLocal); + ilBuilder.Emit(OpCodes.Callvirt, awaiterType.GetProperty(nameof(TaskAwaiter.IsCompleted), BindingFlags.Public | BindingFlags.Instance).GetMethod); + } + ilBuilder.Emit(OpCodes.Brtrue_S, completedLabel); + + /* + // num = (<>1__state = 0); + IL_0026: ldarg.0 + IL_0027: ldc.i4.0 + IL_0028: dup + IL_0029: stloc.0 + IL_002a: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>1__state' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_0); + ilBuilder.Emit(OpCodes.Dup); + ilBuilder.Emit(OpCodes.Stloc, stateLocal); + ilBuilder.Emit(OpCodes.Stfld, stateField); + /* + // <>u__1 = awaiter; + IL_002f: ldarg.0 + IL_0030: ldloc.2 + IL_0031: stfld valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>u__1' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.EmitLdloc(awaiterLocal); + ilBuilder.Emit(OpCodes.Stfld, awaiterField); + /* + // <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); + IL_0036: ldarg.0 + IL_0037: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>t__builder' + IL_003c: ldloca.s 2 + IL_003e: ldarg.0 + IL_003f: call instance void [System.Runtime]System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder::AwaitUnsafeOnCompletedd__4'>(!!0&, !!1&) + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, builderField); + ilBuilder.EmitLdloca(awaiterLocal); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Call, GetAwaitOnCompletedMethod(asyncMethodBuilderType, awaiterType, asyncStateMachineTypeBuilder)); + /* + // return; + IL_0044: leave.s IL_0095 + */ + ilBuilder.Emit(OpCodes.Leave_S, returnLabel); + + /* + // awaiter = <>u__1; + IL_0046: ldarg.0 + IL_0047: ldfld valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>u__1' + IL_004c: stloc.2 + */ + // IL_0046: + ilBuilder.MarkLabel(state0Label); + + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldfld, awaiterField); + ilBuilder.Emit(OpCodes.Stloc, awaiterLocal); + /* + // <>u__1 = default(TaskAwaiter); + IL_004d: ldarg.0 + IL_004e: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>u__1' + IL_0053: initobj [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.EmitSetFieldToDefault(awaiterField); + /* + // num = (<>1__state = -1); + IL_0059: ldarg.0 + IL_005a: ldc.i4.m1 + IL_005b: dup + IL_005c: stloc.0 + IL_005d: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/'<__GlobalSetup>d__4'::'<>1__state' + */ + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_M1); + ilBuilder.Emit(OpCodes.Dup); + ilBuilder.Emit(OpCodes.Stloc, stateLocal); + ilBuilder.Emit(OpCodes.Stfld, stateField); + + /* + // awaiter.GetResult(); + IL_0062: ldloca.s 2 + IL_0064: call instance void [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::GetResult() + IL_0069: leave.s IL_0082 + */ + // IL_0062: + ilBuilder.MarkLabel(completedLabel); + + var getResultMethod = awaiterType.GetMethod(nameof(TaskAwaiter.GetResult), BindingFlags.Public | BindingFlags.Instance); + if (awaiterType.IsValueType) + { + ilBuilder.EmitLdloca(awaiterLocal); + ilBuilder.Emit(OpCodes.Call, getResultMethod); + } + else + { + ilBuilder.EmitLdloc(awaiterLocal); + ilBuilder.Emit(OpCodes.Callvirt, getResultMethod); + } + if (getResultMethod.ReturnType != typeof(void)) + { + ilBuilder.Emit(OpCodes.Pop); + } + } + } + + private static MethodInfo GetAwaitOnCompletedMethod(Type asyncMethodBuilderType, Type awaiterType, Type asyncStateMachineType) + { + var awaitOnCompletedMethodName = typeof(ICriticalNotifyCompletion).IsAssignableFrom(awaiterType) + ? nameof(AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted) + : nameof(AsyncTaskMethodBuilder.AwaitOnCompleted); + return asyncMethodBuilderType + .GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Single(method => method.IsGenericMethod && method.Name == awaitOnCompletedMethodName) + .MakeGenericMethod(awaiterType, asyncStateMachineType); + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs index 5477a30e3e..e2c57da15a 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs @@ -7,14 +7,17 @@ using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Security; +using System.Threading.Tasks; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers.Reflection.Emit; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Properties; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.Results; +using Perfolizer.Horology; using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants; using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableReflectionHelpers; @@ -24,34 +27,19 @@ namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation /// A helper type that emits code that matches BenchmarkType.txt template. /// IMPORTANT: this type IS NOT thread safe. /// - internal class RunnableEmitter + internal abstract partial class RunnableEmitter { + private RunnableEmitter() { } + /// /// Maps action args to fields that store arg values. /// - private class ArgFieldInfo - { - public ArgFieldInfo(FieldInfo field, Type argLocalsType, MethodInfo opImplicitMethod) - { - Field = field; - ArgLocalsType = argLocalsType; - OpImplicitMethod = opImplicitMethod; - } - - public FieldInfo Field { get; } - - public Type ArgLocalsType { get; } - - public MethodInfo OpImplicitMethod { get; } - } + private record struct ArgFieldInfo(FieldInfo Field, Type ArgLocalsType, MethodInfo OpImplicitMethod); /// - /// Emits assembly with runnables from current build partition.. + /// Emits assembly with runnables from current build partition. /// - public static Assembly EmitPartitionAssembly( - GenerateResult generateResult, - BuildPartition buildPartition, - ILogger logger) + public static Assembly EmitPartitionAssembly(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) { var assemblyResultPath = generateResult.ArtifactsPaths.ExecutablePath; var assemblyFileName = Path.GetFileName(assemblyResultPath); @@ -62,8 +50,15 @@ public static Assembly EmitPartitionAssembly( var moduleBuilder = DefineModuleBuilder(assemblyBuilder, assemblyFileName, saveToDisk); foreach (var benchmark in buildPartition.Benchmarks) { - var runnableEmitter = new RunnableEmitter(buildPartition, moduleBuilder); - runnableEmitter.EmitRunnableCore(benchmark); + var returnType = benchmark.BenchmarkCase.Descriptor.WorkloadMethod.ReturnType; + RunnableEmitter runnableEmitter = returnType.IsAwaitable() + ? new AsyncCoreEmitter() + : new SyncCoreEmitter(); + runnableEmitter.buildPartition = buildPartition ?? throw new ArgumentNullException(nameof(buildPartition)); + runnableEmitter.moduleBuilder = moduleBuilder ?? throw new ArgumentNullException(nameof(moduleBuilder)); + runnableEmitter.benchmark = benchmark; + runnableEmitter.jobUnrollFactor = benchmark.BenchmarkCase.Job.ResolveValue(RunMode.UnrollFactorCharacteristic, buildPartition.Resolver); + runnableEmitter.EmitRunnableCore(); } if (saveToDisk) @@ -75,7 +70,6 @@ public static Assembly EmitPartitionAssembly( return assemblyBuilder; } - private static bool ShouldSaveToDisk(IConfig config) { if (!BenchmarkDotNetInfo.Instance.IsRelease) @@ -166,15 +160,11 @@ private static ModuleBuilder DefineModuleBuilder(AssemblyBuilder assemblyBuilder return moduleBuilder; } - private static TypeBuilder DefineRunnableTypeBuilder( - BenchmarkBuildInfo benchmark, - ModuleBuilder moduleBuilder) + private TypeBuilder DefineRunnableTypeBuilder() { // .class public auto ansi sealed beforefieldinit BenchmarkDotNet.Autogenerated.Runnable_0 // extends [BenchmarkDotNet]BenchmarkDotNet.Samples.SampleBenchmark - var benchmarkDescriptor = benchmark.BenchmarkCase.Descriptor; - - var workloadType = benchmarkDescriptor.Type.GetTypeInfo(); + var workloadType = Descriptor.Type.GetTypeInfo(); var workloadTypeAttributes = workloadType.Attributes; if (workloadTypeAttributes.HasFlag(TypeAttributes.NestedPublic)) { @@ -190,11 +180,7 @@ private static TypeBuilder DefineRunnableTypeBuilder( return result; } - private static void EmitNoArgsMethodCallPopReturn( - MethodBuilder methodBuilder, - MethodInfo targetMethod, - ILGenerator ilBuilder, - bool forceDirectCall) + private static void EmitNoArgsMethodCallPopReturn(MethodBuilder methodBuilder, MethodInfo targetMethod, ILGenerator ilBuilder) { if (targetMethod == null) throw new ArgumentNullException(nameof(targetMethod)); @@ -213,7 +199,7 @@ private static void EmitNoArgsMethodCallPopReturn( */ if (targetMethod.IsStatic) { - ilBuilder.EmitStaticCall(targetMethod, Array.Empty()); + ilBuilder.EmitStaticCall(targetMethod, []); } else if (methodBuilder.IsStatic) { @@ -223,147 +209,62 @@ private static void EmitNoArgsMethodCallPopReturn( else { ilBuilder.Emit(OpCodes.Ldarg_0); - ilBuilder.EmitInstanceCallThisValueOnStack( - null, - targetMethod, - Array.Empty(), - forceDirectCall); + ilBuilder.EmitInstanceCallThisValueOnStack(null, targetMethod, [], true); } if (targetMethod.ReturnType != typeof(void)) ilBuilder.Emit(OpCodes.Pop); } - private readonly BuildPartition buildPartition; - private readonly ModuleBuilder moduleBuilder; - + private BuildPartition buildPartition; + private ModuleBuilder moduleBuilder; private BenchmarkBuildInfo benchmark; - private List argFields; private int jobUnrollFactor; private TypeBuilder runnableBuilder; - private ConsumableTypeInfo consumableInfo; - private ConsumableTypeInfo globalSetupReturnInfo; - private ConsumableTypeInfo globalCleanupReturnInfo; - private ConsumableTypeInfo iterationSetupReturnInfo; - private ConsumableTypeInfo iterationCleanupReturnInfo; - - private FieldBuilder globalSetupActionField; - private FieldBuilder globalCleanupActionField; - private FieldBuilder iterationSetupActionField; - private FieldBuilder iterationCleanupActionField; + private readonly List nestedTypeBuilders = []; + + private FieldBuilder fieldsContainerField; + private readonly List argFields = []; private FieldBuilder notElevenField; - // ReSharper disable NotAccessedField.Local - private ConstructorBuilder ctorMethod; - private MethodBuilder trickTheJitMethod; private MethodBuilder overheadImplementationMethod; - private MethodBuilder overheadActionUnrollMethod; - private MethodBuilder overheadActionNoUnrollMethod; - private MethodBuilder workloadActionUnrollMethod; - private MethodBuilder workloadActionNoUnrollMethod; - private MethodBuilder forDisassemblyDiagnoserMethod; - - private MethodBuilder globalSetupMethod; - private MethodBuilder globalCleanupMethod; - private MethodBuilder iterationSetupMethod; - private MethodBuilder iterationCleanupMethod; - - private MethodBuilder runMethod; - // ReSharper restore NotAccessedField.Local - - private RunnableEmitter(BuildPartition buildPartition, ModuleBuilder moduleBuilder) - { - if (buildPartition == null) - throw new ArgumentNullException(nameof(buildPartition)); - if (moduleBuilder == null) - throw new ArgumentNullException(nameof(moduleBuilder)); - - this.buildPartition = buildPartition; - this.moduleBuilder = moduleBuilder; - } private Descriptor Descriptor => benchmark.BenchmarkCase.Descriptor; + private Type BenchmarkReturnType => Descriptor.WorkloadMethod.ReturnType; - // ReSharper disable once UnusedMethodReturnValue.Local - private Type EmitRunnableCore(BenchmarkBuildInfo newBenchmark) + private void EmitRunnableCore() { - if (newBenchmark == null) - throw new ArgumentNullException(nameof(newBenchmark)); - - InitForEmitRunnable(newBenchmark); - - // 1. Emit fields - DefineFields(); - - // 2. Define members - ctorMethod = DefineCtor(); - trickTheJitMethod = DefineTrickTheJitMethod(); + runnableBuilder = DefineRunnableTypeBuilder(); - // Overhead impl - overheadImplementationMethod = EmitOverheadImplementation(OverheadImplementationMethodName); - overheadActionUnrollMethod = EmitOverheadAction(OverheadActionUnrollMethodName, jobUnrollFactor); - overheadActionNoUnrollMethod = EmitOverheadAction(OverheadActionNoUnrollMethodName, 1); - - // Workload impl - workloadActionUnrollMethod = EmitWorkloadAction(WorkloadActionUnrollMethodName, jobUnrollFactor); - workloadActionNoUnrollMethod = EmitWorkloadAction(WorkloadActionNoUnrollMethodName, 1); - - // __ForDisassemblyDiagnoser__ impl - forDisassemblyDiagnoserMethod = EmitForDisassemblyDiagnoser(ForDisassemblyDiagnoserMethodName); - - // 4. Instance completion - // Emit wrappers for setup/cleanup callbacks + EmitFields(); + EmitCtor(); EmitSetupCleanupMethods(); + EmitTrickTheJit(); + overheadImplementationMethod = EmitOverheadImplementation(OverheadImplementationMethodName); + EmitCoreImpl(); - // Emit methods that depend on others - EmitTrickTheJitBody(); - EmitCtorBody(); - - // 5. Emit Run() logic - runMethod = EmitRunMethod(); - -#if NETFRAMEWORK - return runnableBuilder.CreateType(); -#else - return runnableBuilder.CreateTypeInfo(); -#endif - } - - private void InitForEmitRunnable(BenchmarkBuildInfo newBenchmark) - { - // Init current state - argFields = new List(); - benchmark = newBenchmark; - jobUnrollFactor = benchmark.BenchmarkCase.Job.ResolveValue( - RunMode.UnrollFactorCharacteristic, - buildPartition.Resolver); - - consumableInfo = new ConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.WorkloadMethod.ReturnType); - globalSetupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.GlobalSetupMethod?.ReturnType); - globalCleanupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.GlobalCleanupMethod?.ReturnType); - iterationSetupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.IterationSetupMethod?.ReturnType); - iterationCleanupReturnInfo = GetConsumableTypeInfo(benchmark.BenchmarkCase.Descriptor.IterationCleanupMethod?.ReturnType); - - // Init type - runnableBuilder = DefineRunnableTypeBuilder(benchmark, moduleBuilder); + foreach (var nestedTypeBuilder in nestedTypeBuilders) + { + nestedTypeBuilder.CreateTypeInfo(); + } + runnableBuilder.CreateTypeInfo(); } - private static ConsumableTypeInfo GetConsumableTypeInfo(Type methodReturnType) - { - return methodReturnType == null ? null : new ConsumableTypeInfo(methodReturnType); - } + protected abstract void EmitCoreImpl(); - private void DefineFields() + private void EmitFields() { - globalSetupActionField = - runnableBuilder.DefineField(GlobalSetupActionFieldName, typeof(Action), FieldAttributes.Private); - globalCleanupActionField = - runnableBuilder.DefineField(GlobalCleanupActionFieldName, typeof(Action), FieldAttributes.Private); - iterationSetupActionField = - runnableBuilder.DefineField(IterationSetupActionFieldName, typeof(Action), FieldAttributes.Private); - iterationCleanupActionField = - runnableBuilder.DefineField(IterationCleanupActionFieldName, typeof(Action), FieldAttributes.Private); + /* + private unsafe struct FieldsContainer + { + } + */ + var fieldsContainerBuilder = runnableBuilder.DefineNestedType( + "FieldsContainer", + TypeAttributes.NestedPrivate | TypeAttributes.AutoLayout | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, + typeof(ValueType)); + nestedTypeBuilders.Add(fieldsContainerBuilder); // Define arg fields foreach (var parameter in Descriptor.WorkloadMethod.GetParameters()) @@ -380,7 +281,7 @@ private void DefineFields() argFieldType = argLocalsType.GetElementType() ?? throw new InvalidOperationException($"Bug: cannot get field type from {argLocalsType}"); } - else if (IsRefLikeType(parameterType) && argValue.Value != null) + else if (parameterType.IsByRefLike() && argValue.Value != null) { argLocalsType = parameterType; @@ -397,248 +298,150 @@ private void DefineFields() argFieldType = parameterType; } - if (IsRefLikeType(argFieldType)) + if (argFieldType.IsByRefLike()) throw new NotSupportedException( $"Passing ref readonly structs by ref is not supported (cannot store {argFieldType} as a class field)."); - var argField = runnableBuilder.DefineField( + var argField = fieldsContainerBuilder.DefineField( ArgFieldPrefix + parameter.Position, argFieldType, - FieldAttributes.Private); + FieldAttributes.Public); - argFields.Add(new ArgFieldInfo(argField, argLocalsType, opConversion)); + argFields.Add(new(argField, argLocalsType, opConversion)); } + EmitExtraFields(fieldsContainerBuilder); + + // private FieldsContainer __fieldsContainer; + fieldsContainerField = runnableBuilder.DefineField( + FieldsContainerName, + fieldsContainerBuilder, + FieldAttributes.Private); + notElevenField = runnableBuilder.DefineField(NotElevenFieldName, typeof(int), FieldAttributes.Public); } - private ConstructorBuilder DefineCtor() + protected virtual void EmitExtraFields(TypeBuilder fieldsContainerBuilder) { } + + private void EmitCtor() { // .method public hidebysig specialname rtspecialname // instance void.ctor() cil managed - return runnableBuilder.DefinePublicInstanceCtor(); - } - - private MethodBuilder DefineTrickTheJitMethod() - { - // .method public hidebysig - // instance void __TrickTheJIT__() cil managed noinlining nooptimization - var result = runnableBuilder - .DefinePublicNonVirtualVoidInstanceMethod(TrickTheJitCoreMethodName) - .SetNoInliningImplementationFlag() - .SetNoOptimizationImplementationFlag(); - - return result; + var ctorMethod = runnableBuilder.DefinePublicInstanceCtor(); + var ilBuilder = ctorMethod.GetILGenerator(); + ilBuilder.EmitCallBaseParameterlessCtor(ctorMethod); + ilBuilder.EmitCtorReturn(ctorMethod); } - private MethodBuilder EmitOverheadImplementation(string methodName) + protected void EmitLoadArgFieldsForCall(ILGenerator ilBuilder, LocalBuilder? runnableLocal) { - //.method private hidebysig - // instance void __Overhead(int64 arg0) cil managed - - // Replace arg names - var parameters = Descriptor.WorkloadMethod.GetParameters() - .Select(p => - (ParameterInfo) new EmitParameterInfo( - p.Position, - ArgParamPrefix + p.Position, - p.ParameterType, - p.Attributes, - null)) - .ToArray(); - - var methodBuilder = runnableBuilder.DefineNonVirtualInstanceMethod( - methodName, - MethodAttributes.Private, - EmitParameterInfo.CreateReturnVoidParameter(), - parameters) - .SetNoInliningImplementationFlag(); - - var ilBuilder = methodBuilder.GetILGenerator(); /* - // return; - IL_0001: ret - */ - ilBuilder.EmitVoidReturn(methodBuilder); + // base.InvokeOnceVoid(__fieldsContainer.__argField0, __fieldsContainer.__argField1); + IL_000b: ldarg.0 + IL_000c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0011: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::__argField0 + IL_0016: ldarg.0 + IL_0017: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_001c: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::__argField1 - return methodBuilder; - } + // -or- - private MethodBuilder EmitOverheadAction(string methodName, int unrollFactor) - => EmitActionImpl(methodName, overheadImplementationMethod, EmitCallOverhead, unrollFactor); + // base.InvokeOnceVoid(ref __fieldsContainer.__argField0, ref __fieldsContainer.__argField1); + IL_000b: ldarg.0 + IL_000c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_2::__fieldsContainer + IL_0011: ldflda bool BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer::__argField0 + IL_0016: ldarg.0 + IL_0017: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_2::__fieldsContainer + IL_001c: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer::__argField1 - private MethodBuilder EmitWorkloadAction(string methodName, int unrollFactor) - => EmitActionImpl(methodName, Descriptor.WorkloadMethod, EmitCallWorkload, unrollFactor); + // -or- (ref struct arg call) - private void EmitCallOverhead(ILGenerator ilBuilder, IReadOnlyList argLocals) - { - /* - // __Overhead(); - IL_0008: ldarg.0 - IL_0009: call instance void BenchmarkDotNet.Autogenerated.Runnable_0::__Overhead() + // base.InvokeOnceVoid((Span)__fieldsContainer.__argField0, __fieldsContainer.__argField1); + IL_000b: ldarg.0 + IL_000c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer + IL_0011: ldfld bool[] BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField0 + IL_0016: call valuetype [System.Runtime]System.Span`1 valuetype [System.Runtime]System.Span`1::op_Implicit(!0[]) + IL_001b: ldarg.0 + IL_001c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer + IL_0021: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField1 */ - ilBuilder.Emit(OpCodes.Ldarg_0); - ilBuilder.EmitLdLocals(argLocals); - ilBuilder.Emit(OpCodes.Call, overheadImplementationMethod); - } - - private void EmitCallWorkload(ILGenerator ilBuilder, IReadOnlyList argLocals) - { - /* - // InvokeOnceVoid(); - IL_0008: ldarg.0 - IL_0009: call instance void [BenchmarkDotNet.IntegrationTests]BenchmarkDotNet.IntegrationTests.InProcessEmitTest/BenchmarkAllCases::InvokeOnceVoid() - */ - MethodInfo invokeMethod = Descriptor.WorkloadMethod; - if (!invokeMethod.IsStatic) + foreach (var argFieldInfo in argFields) { - ilBuilder.Emit(OpCodes.Ldarg_0); - } - ilBuilder.EmitLdLocals(argLocals); - ilBuilder.Emit(OpCodes.Call, invokeMethod); + if (runnableLocal is not null) + ilBuilder.EmitLdloc(runnableLocal); + else + ilBuilder.Emit(OpCodes.Ldarg_0); - if (consumableInfo.IsAwaitable) - { - /* - // BenchmarkDotNet.Helpers.AwaitHelper.GetResult(...); - IL_000e: call !!0 BenchmarkDotNet.Helpers.AwaitHelper::GetResult(valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1) - */ - ilBuilder.Emit(OpCodes.Call, consumableInfo.GetResultMethod); - } + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); - if (consumableInfo.WorkloadMethodReturnType != typeof(void)) - { - // IL_000b: pop - ilBuilder.Emit(OpCodes.Pop); + if (argFieldInfo.ArgLocalsType.IsByRef) + ilBuilder.Emit(OpCodes.Ldflda, argFieldInfo.Field); + else + ilBuilder.Emit(OpCodes.Ldfld, argFieldInfo.Field); + + if (argFieldInfo.OpImplicitMethod != null) + ilBuilder.Emit(OpCodes.Call, argFieldInfo.OpImplicitMethod); } } - private MethodBuilder EmitActionImpl(string methodName, MethodInfo invokeMethod, Action> callMethodEmitter, int unrollFactor) + private void EmitSetupCleanupMethods() { - // .method private hidebysig - // instance void OverheadActionUnroll(int64 invokeCount) cil managed aggressiveoptimization - var invokeCountArg = new EmitParameterInfo(0, InvokeCountParamName, typeof(long)); - var actionMethodBuilder = runnableBuilder.DefineNonVirtualInstanceMethod( - methodName, - MethodAttributes.Private, - EmitParameterInfo.CreateReturnVoidParameter(), - invokeCountArg) - .SetAggressiveOptimizationImplementationFlag(); - invokeCountArg.SetMember(actionMethodBuilder); - - // Emit impl - var ilBuilder = actionMethodBuilder.GetILGenerator(); - - // init locals - var argLocals = EmitDeclareArgLocals(ilBuilder); - - // load fields - EmitLoadArgFieldsToLocals(ilBuilder, argLocals); - - // loop - ilBuilder.EmitLoopBeginFromArgToZero(out var loopStartLabel, out var loopHeadLabel); - { - for (int u = 0; u < unrollFactor; u++) - { - callMethodEmitter(ilBuilder, argLocals); - } - } - ilBuilder.EmitLoopEndFromArgToZero(loopStartLabel, loopHeadLabel, invokeCountArg); - - // IL_003a: ret - ilBuilder.EmitVoidReturn(actionMethodBuilder); - - return actionMethodBuilder; + EmitSetupCleanup(GlobalSetupMethodName, Descriptor.GlobalSetupMethod, false); + EmitSetupCleanup(GlobalCleanupMethodName, Descriptor.GlobalCleanupMethod, true); + EmitSetupCleanup(IterationSetupMethodName, Descriptor.IterationSetupMethod, false); + EmitSetupCleanup(IterationCleanupMethodName, Descriptor.IterationCleanupMethod, false); } - private IReadOnlyList EmitDeclareArgLocals(ILGenerator ilBuilder, bool skipFirst = false) + private void EmitTrickTheJit() { - // NB: c# compiler does not store first arg in locals for static calls - /* - .locals init ( - [0] int64, // argFields[0] - [1] int32, // argFields[1] - ) - // -or- (static calls) - .locals init ( - [0] int32, // argFields[1] - ) - */ - bool first = true; - var argLocals = new List(argFields.Count); - foreach (var argField in argFields) - { - if (!first || !skipFirst) - { - argLocals.Add(ilBuilder.DeclareLocal(argField.ArgLocalsType)); - } - - first = false; - } + var forDisassemblyDiagnoserMethod = EmitForDisassemblyDiagnoserMethod(); - return argLocals; - } + // .method public hidebysig + // instance void __TrickTheJIT__() cil managed noinlining nooptimization + var trickTheJitMethod = runnableBuilder + .DefinePublicNonVirtualVoidInstanceMethod(TrickTheJitCoreMethodName) + .SetNoInliningImplementationFlag() + .SetNoOptimizationImplementationFlag(); - private void EmitLoadArgFieldsToLocals(ILGenerator ilBuilder, IReadOnlyList argLocals, bool skipFirstArg = false) - { - // NB: c# compiler does not store first arg in locals for static calls - int localsOffset = argFields.Count > 0 && skipFirstArg ? -1 : 0; - if (argLocals.Count != argFields.Count + localsOffset) - throw new InvalidOperationException("Bug: argLocals.Count != _argFields.Count + localsOffset"); + var ilBuilder = trickTheJitMethod.GetILGenerator(); /* - // long _argField = __argField0; - IL_0000: ldarg.0 - IL_0001: ldfld int64 BenchmarkDotNet.Autogenerated.Runnable_0::__argField0 - IL_0006: stloc.0 - IL_0007: ldarg.1 - IL_0008: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0::__argField1 - IL_000c: stloc.1 - // -or- - // ref int _argField = ref __argField0; - IL_0000: ldarg.0 - IL_0001: ldflda int64 BenchmarkDotNet.Autogenerated.Runnable_0::__argField0 - IL_0006: stloc.0 - IL_0007: ldarg.1 - IL_000b: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_0::__argField1 - IL_000c: stloc.1 - // -or- (static call) - // long _argField = __argField0; - IL_0000: ldarg.0 - IL_0001: ldfld int64 BenchmarkDotNet.Autogenerated.Runnable_0::__argField0 - IL_0006: ldarg.1 - IL_0007: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0::__argField1 - IL_000b: stloc.0 // offset by -1 - // -or- (ref struct arg call) + // NotEleven = new Random(123).Next(0, 10); IL_0000: ldarg.0 - IL_0001: ldfld int32[] BenchmarkDotNet.Autogenerated.Runnable_0::__argField0 - IL_0006: call valuetype [System.Memory]System.Span`1 valuetype [System.Memory]System.Span`1::op_Implicit(!0[]) - IL_000b: stloc.0 - IL_000c: ldarg.1 - IL_000d: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0::__argField1 - IL_0012: stloc.1 + IL_0001: ldc.i4.s 123 + IL_0003: newobj instance void [mscorlib]System.Random::.ctor(int32) + IL_0008: ldc.i4.0 + IL_0009: ldc.i4.s 10 + IL_000b: callvirt instance int32 [mscorlib]System.Random::Next(int32, int32) + IL_0010: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_0::NotEleven */ - for (int i = 0; i < argFields.Count; i++) - { - ilBuilder.Emit(OpCodes.Ldarg_0); - var argFieldInfo = argFields[i]; + var randomCtor = typeof(Random).GetConstructor(new[] { typeof(int) }) + ?? throw new MissingMemberException(nameof(Random)); + var randomNextMethod = typeof(Random).GetMethod(nameof(Random.Next), new[] { typeof(int), typeof(int) }) + ?? throw new MissingMemberException(nameof(Random.Next)); - if (argFieldInfo.ArgLocalsType.IsByRef) - ilBuilder.Emit(OpCodes.Ldflda, argFieldInfo.Field); - else - ilBuilder.Emit(OpCodes.Ldfld, argFieldInfo.Field); + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldc_I4_S, (byte)123); + ilBuilder.Emit(OpCodes.Newobj, randomCtor); + ilBuilder.Emit(OpCodes.Ldc_I4_0); + ilBuilder.Emit(OpCodes.Ldc_I4_S, (byte)10); + ilBuilder.Emit(OpCodes.Callvirt, randomNextMethod); + ilBuilder.Emit(OpCodes.Stfld, notElevenField); - if (argFieldInfo.OpImplicitMethod != null) - ilBuilder.Emit(OpCodes.Call, argFieldInfo.OpImplicitMethod); + /* + // __ForDisassemblyDiagnoser__(); + IL_0015: ldarg.0 + IL_0016: call instance int32 BenchmarkDotNet.Autogenerated.Runnable_0::__ForDisassemblyDiagnoser__() + IL_001b: pop + */ + EmitNoArgsMethodCallPopReturn(trickTheJitMethod, forDisassemblyDiagnoserMethod, ilBuilder); - var localsIndex = i + localsOffset; - if (localsIndex >= 0) - ilBuilder.EmitStloc(argLocals[localsIndex]); - } + // IL_001b: ret + ilBuilder.EmitVoidReturn(trickTheJitMethod); } - private MethodBuilder EmitForDisassemblyDiagnoser(string methodName) + private MethodBuilder EmitForDisassemblyDiagnoserMethod() { // .method public hidebysig // instance void __ForDisassemblyDiagnoser__() cil managed noinlining nooptimization @@ -646,23 +449,15 @@ private MethodBuilder EmitForDisassemblyDiagnoser(string methodName) var workloadReturnParameter = EmitParameterInfo.CreateReturnParameter(typeof(void)); var methodBuilder = runnableBuilder .DefineNonVirtualInstanceMethod( - methodName, + ForDisassemblyDiagnoserMethodName, MethodAttributes.Public, - workloadReturnParameter) + workloadReturnParameter + ) .SetNoInliningImplementationFlag() .SetNoOptimizationImplementationFlag(); var ilBuilder = methodBuilder.GetILGenerator(); - /* - .locals init ( - [0] int64, - ) - */ - // NB: c# compiler does not store first arg in locals for static calls - var skipFirstArg = workloadMethod.IsStatic; - var argLocals = EmitDeclareArgLocals(ilBuilder, skipFirstArg); - var notElevenLabel = ilBuilder.DefineLabel(); /* // if (NotEleven == 11) @@ -673,42 +468,27 @@ .locals init ( */ ilBuilder.Emit(OpCodes.Ldarg_0); ilBuilder.Emit(OpCodes.Ldfld, notElevenField); - ilBuilder.Emit(OpCodes.Ldc_I4_S, (byte)11); + ilBuilder.Emit(OpCodes.Ldc_I4_S, (byte) 11); ilBuilder.Emit(OpCodes.Bne_Un, notElevenLabel); { /* - // long _argField = __argField0; - IL_000a: ldarg.0 - IL_000b: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0::__argField0 - IL_0010: stloc.0 - */ - EmitLoadArgFieldsToLocals(ilBuilder, argLocals, skipFirstArg); - - /* - IL_0026: ldarg.0 - IL_0027: ldloc.0 - IL_0028: ldloc.1 - IL_0029: ldloc.2 - IL_002a: ldloc.3 - IL_002b: call instance class [System.Private.CoreLib]System.Threading.Tasks.Task`1 BenchmarkDotNet.Helpers.Runnable_0::WorkloadMethod(string, string, string, string) + // base.Simple(__fieldsContainer.__argField0, __fieldsContainer.__argField1); + IL_000a: ldarg.0 + IL_000b: ldarg.0 + IL_000c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_0011: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::__argField0 + IL_0016: ldarg.0 + IL_0017: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer + IL_001c: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::__argField1 + IL_0021: call instance void [BenchmarkDotNet.IntegrationTests]BenchmarkDotNet.IntegrationTests.ArgumentsTests/WithArguments::Simple(bool, int32) */ if (!workloadMethod.IsStatic) { ilBuilder.Emit(OpCodes.Ldarg_0); } - ilBuilder.EmitLdLocals(argLocals); + EmitLoadArgFieldsForCall(ilBuilder, null); ilBuilder.Emit(OpCodes.Call, workloadMethod); - - if (consumableInfo.IsAwaitable) - { - /* - // BenchmarkDotNet.Helpers.AwaitHelper.GetResult(...); - IL_000e: call !!0 BenchmarkDotNet.Helpers.AwaitHelper::GetResult(valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1) - */ - ilBuilder.Emit(OpCodes.Call, consumableInfo.GetResultMethod); - } - - if (consumableInfo.WorkloadMethodReturnType != typeof(void)) + if (BenchmarkReturnType != typeof(void)) { ilBuilder.Emit(OpCodes.Pop); } @@ -722,294 +502,49 @@ .locals init ( return methodBuilder; } - private void EmitSetupCleanupMethods() - { - // Emit Setup/Cleanup methods - // We emit empty method instead of EmptyAction = "() => { }" - globalSetupMethod = EmitWrapperMethod(GlobalSetupMethodName, Descriptor.GlobalSetupMethod, globalSetupReturnInfo); - globalCleanupMethod = EmitWrapperMethod(GlobalCleanupMethodName, Descriptor.GlobalCleanupMethod, globalCleanupReturnInfo); - iterationSetupMethod = EmitWrapperMethod(IterationSetupMethodName, Descriptor.IterationSetupMethod, iterationSetupReturnInfo); - iterationCleanupMethod = EmitWrapperMethod(IterationCleanupMethodName, Descriptor.IterationCleanupMethod, iterationCleanupReturnInfo); - } - - private MethodBuilder EmitWrapperMethod(string methodName, MethodInfo optionalTargetMethod, ConsumableTypeInfo returnTypeInfo) - { - var methodBuilder = runnableBuilder.DefinePrivateVoidInstanceMethod(methodName); - - var ilBuilder = methodBuilder.GetILGenerator(); - - if (optionalTargetMethod != null) - { - if (returnTypeInfo?.IsAwaitable == true) - { - EmitAwaitableSetupTeardown(methodBuilder, optionalTargetMethod, ilBuilder, returnTypeInfo); - } - else - { - EmitNoArgsMethodCallPopReturn(methodBuilder, optionalTargetMethod, ilBuilder, forceDirectCall: true); - } - } - - ilBuilder.EmitVoidReturn(methodBuilder); - - return methodBuilder; - } - - private void EmitAwaitableSetupTeardown( - MethodBuilder methodBuilder, - MethodInfo targetMethod, - ILGenerator ilBuilder, - ConsumableTypeInfo returnTypeInfo) - { - if (targetMethod == null) - throw new ArgumentNullException(nameof(targetMethod)); - - if (returnTypeInfo.WorkloadMethodReturnType == typeof(void)) - { - ilBuilder.Emit(OpCodes.Ldarg_0); - } - /* - // call for instance - // GlobalSetup(); - IL_0006: ldarg.0 - IL_0007: call instance void [BenchmarkDotNet]BenchmarkDotNet.Samples.SampleBenchmark::GlobalSetup() - */ - /* - // call for static - // GlobalSetup(); - IL_0006: call string [BenchmarkDotNet]BenchmarkDotNet.Samples.SampleBenchmark::GlobalCleanup() - */ - if (targetMethod.IsStatic) - { - ilBuilder.Emit(OpCodes.Call, targetMethod); - - } - else if (methodBuilder.IsStatic) - { - throw new InvalidOperationException( - $"[BUG] Static method {methodBuilder.Name} tries to call instance member {targetMethod.Name}"); - } - else - { - ilBuilder.Emit(OpCodes.Ldarg_0); - ilBuilder.Emit(OpCodes.Call, targetMethod); - } - - /* - // BenchmarkDotNet.Helpers.AwaitHelper.GetResult(...); - IL_000e: call !!0 BenchmarkDotNet.Helpers.AwaitHelper::GetResult(valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1) - */ - - ilBuilder.Emit(OpCodes.Call, returnTypeInfo.GetResultMethod); - ilBuilder.Emit(OpCodes.Pop); - } - - private void EmitCtorBody() - { - var ilBuilder = ctorMethod.GetILGenerator(); - - ilBuilder.EmitCallBaseParameterlessCtor(ctorMethod); - - ilBuilder.EmitSetDelegateToThisField(globalSetupActionField, globalSetupMethod); - ilBuilder.EmitSetDelegateToThisField(globalCleanupActionField, globalCleanupMethod); - ilBuilder.EmitSetDelegateToThisField(iterationSetupActionField, iterationSetupMethod); - ilBuilder.EmitSetDelegateToThisField(iterationCleanupActionField, iterationCleanupMethod); - - ilBuilder.EmitCtorReturn(ctorMethod); - } - - private void EmitTrickTheJitBody() - { - var ilBuilder = trickTheJitMethod.GetILGenerator(); - - /* - // NotEleven = new Random(123).Next(0, 10); - IL_0000: ldarg.0 - IL_0001: ldc.i4.s 123 - IL_0003: newobj instance void [mscorlib]System.Random::.ctor(int32) - IL_0008: ldc.i4.0 - IL_0009: ldc.i4.s 10 - IL_000b: callvirt instance int32 [mscorlib]System.Random::Next(int32, int32) - IL_0010: stfld int32 BenchmarkDotNet.Autogenerated.Runnable_0::NotEleven - */ - var randomCtor = typeof(Random).GetConstructor(new[] { typeof(int) }) - ?? throw new MissingMemberException(nameof(Random)); - var randomNextMethod = typeof(Random).GetMethod(nameof(Random.Next), new[] { typeof(int), typeof(int) }) - ?? throw new MissingMemberException(nameof(Random.Next)); - - ilBuilder.Emit(OpCodes.Ldarg_0); - ilBuilder.Emit(OpCodes.Ldc_I4_S, (byte)123); - ilBuilder.Emit(OpCodes.Newobj, randomCtor); - ilBuilder.Emit(OpCodes.Ldc_I4_0); - ilBuilder.Emit(OpCodes.Ldc_I4_S, (byte)10); - ilBuilder.Emit(OpCodes.Callvirt, randomNextMethod); - ilBuilder.Emit(OpCodes.Stfld, notElevenField); - - /* - // __ForDisassemblyDiagnoser__(); - IL_0015: ldarg.0 - IL_0016: call instance int32 BenchmarkDotNet.Autogenerated.Runnable_0::__ForDisassemblyDiagnoser__() - IL_001b: pop - */ - EmitNoArgsMethodCallPopReturn(trickTheJitMethod, forDisassemblyDiagnoserMethod, ilBuilder, forceDirectCall: true); - - // IL_001b: ret - ilBuilder.EmitVoidReturn(trickTheJitMethod); - } - - private MethodBuilder EmitRunMethod() + private MethodBuilder EmitOverheadImplementation(string methodName) { - var prepareForRunMethodTemplate = typeof(RunnableReuse).GetMethod(nameof(RunnableReuse.PrepareForRun)) - ?? throw new MissingMemberException(nameof(RunnableReuse.PrepareForRun)); - (Job, EngineParameters, IEngineFactory) resultTuple = new(); - /* - .method public hidebysig static - void Run ( - class [BenchmarkDotNet]BenchmarkDotNet.Engines.IHost host, - class [BenchmarkDotNet]BenchmarkDotNet.Toolchains.Parameters.ExecuteParameters parameters, - ) cil managed + .method private hidebysig + instance void __Overhead (int64 arg0) cil managed noinlining flags(0200) */ - var argsExceptInstance = prepareForRunMethodTemplate - .GetParameters() - .Skip(1) - .Select(p => (ParameterInfo) new EmitParameterInfo(p.Position - 1, p.Name, p.ParameterType, p.Attributes, null)) + // Replace arg names + var parameters = Descriptor.WorkloadMethod.GetParameters() + .Select(p => + (ParameterInfo) new EmitParameterInfo( + p.Position, + ArgParamPrefix + p.Position, + p.ParameterType, + p.Attributes, + null)) .ToArray(); - var methodBuilder = runnableBuilder.DefineStaticMethod( - RunMethodName, - MethodAttributes.Public, - EmitParameterInfo.CreateReturnVoidParameter(), - argsExceptInstance); - argsExceptInstance = methodBuilder.GetEmitParameters(argsExceptInstance); - var hostArg = argsExceptInstance[0]; - var parametersArg = argsExceptInstance[1]; - - var ilBuilder = methodBuilder.GetILGenerator(); - - /* - .locals init ( - [0] class BenchmarkDotNet.Autogenerated.Runnable_0, - [1] class [BenchmarkDotNet]BenchmarkDotNet.Jobs.Job, - [2] class [BenchmarkDotNet]BenchmarkDotNet.Engines.EngineParameters, - [3] class [BenchmarkDotNet]BenchmarkDotNet.Engines.IEngineFactory, - [4] valuetype [BenchmarkDotNet]BenchmarkDotNet.Engines.RunResults + var methodBuilder = runnableBuilder + .DefineNonVirtualInstanceMethod( + methodName, + MethodAttributes.Private, + EmitParameterInfo.CreateReturnVoidParameter(), + parameters ) - */ - var instanceLocal = ilBuilder.DeclareLocal(runnableBuilder); - var jobLocal = ilBuilder.DeclareLocal(typeof(Job)); - var engineParametersLocal = ilBuilder.DeclareLocal(typeof(EngineParameters)); - var engineFactoryLocal = ilBuilder.DeclareLocal(typeof(IEngineFactory)); - var runResultsLocal = ilBuilder.DeclareLocal(typeof(RunResults)); - - /* - // Runnable_0 instance = new Runnable_0(); - IL_0000: newobj instance void BenchmarkDotNet.Autogenerated.Runnable_0::.ctor() - IL_0005: stloc.0 - */ - ilBuilder.Emit(OpCodes.Newobj, ctorMethod); - ilBuilder.EmitStloc(instanceLocal); - - /* - // (Job, EngineParameters, IEngineFactory) valueTuple = RunnableReuse.PrepareForRun(instance, host, parameters); - IL_0006: ldloc.0 - IL_0007: ldarg.0 - IL_0008: ldarg.1 - IL_0009: call valuetype [mscorlib]System.ValueTuple`3 [BenchmarkDotNet]BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableReuse::PrepareForRun(!!0, class [BenchmarkDotNet]BenchmarkDotNet.Engines.IHost, class [BenchmarkDotNet]BenchmarkDotNet.Toolchains.Parameters.ExecuteParameters) - */ - ilBuilder.EmitLdloc(instanceLocal); - ilBuilder.EmitLdarg(hostArg); - ilBuilder.EmitLdarg(parametersArg); - ilBuilder.Emit(OpCodes.Call, prepareForRunMethodTemplate.MakeGenericMethod(runnableBuilder)); - - /* - // Job job = valueTuple.Item1; - IL_000e: dup - IL_000f: ldfld !0 valuetype [mscorlib]System.ValueTuple`3::Item1 - IL_0014: stloc.1 - */ - ilBuilder.Emit(OpCodes.Dup); - ilBuilder.Emit(OpCodes.Ldfld, resultTuple.GetType().GetField(nameof(resultTuple.Item1))); - ilBuilder.EmitStloc(jobLocal); - /* - // EngineParameters engineParameters = valueTuple.Item2; - IL_0015: dup - IL_0016: ldfld !1 valuetype [mscorlib]System.ValueTuple`3::Item2 - IL_001b: stloc.2 - */ - ilBuilder.Emit(OpCodes.Dup); - ilBuilder.Emit(OpCodes.Ldfld, resultTuple.GetType().GetField(nameof(resultTuple.Item2))); - ilBuilder.EmitStloc(engineParametersLocal); - /* - // IEngineFactory engineFactory = valueTuple.Item3; - IL_001c: ldfld !2 valuetype [mscorlib]System.ValueTuple`3::Item3 - IL_0021: stloc.3 - */ - ilBuilder.Emit(OpCodes.Ldfld, resultTuple.GetType().GetField(nameof(resultTuple.Item3))); - ilBuilder.EmitStloc(engineFactoryLocal); - - var notNullLabel = ilBuilder.DefineLabel(); - /* - // if (job != null) { ... } // translates to "if null: return; else: ..." - IL_0022: ldloc.1 - IL_0023: brtrue.s IL_0026 - IL_0025: ret - */ - ilBuilder.EmitLdloc(jobLocal); - ilBuilder.Emit(OpCodes.Brtrue_S, notNullLabel); - ilBuilder.EmitVoidReturn(methodBuilder); - - /* - // RunResults results = engineFactory.Create(engineParameters).Run(); - IL_0026: ldloc.3 - IL_0027: ldloc.2 - IL_0028: callvirt instance class [BenchmarkDotNet]BenchmarkDotNet.Engines.IEngine [BenchmarkDotNet]BenchmarkDotNet.Engines.IEngineFactory::Create(class [BenchmarkDotNet]BenchmarkDotNet.Engines.EngineParameters) - IL_002d: callvirt instance valuetype [BenchmarkDotNet]BenchmarkDotNet.Engines.RunResults [BenchmarkDotNet]BenchmarkDotNet.Engines.IEngine::Run() - IL_0032: stloc.s 4 - */ - var createReadyToRunMethod = typeof(IEngineFactory).GetMethod(nameof(IEngineFactory.Create)) - ?? throw new MissingMemberException(nameof(IEngineFactory.Create)); - var runMethodImpl = typeof(IEngine).GetMethod(nameof(IEngine.Run)) - ?? throw new MissingMemberException(nameof(IEngine.Run)); - ilBuilder.MarkLabel(notNullLabel); - ilBuilder.EmitLdloc(engineFactoryLocal); - ilBuilder.EmitLdloc(engineParametersLocal); - ilBuilder.Emit(OpCodes.Callvirt, createReadyToRunMethod); - ilBuilder.Emit(OpCodes.Callvirt, runMethodImpl); - ilBuilder.EmitStloc(runResultsLocal); - /* - // host.ReportResults(runResults); - IL_0034: ldarg.0 - IL_0035: ldloc.s 4 - IL_0037: callvirt instance void [BenchmarkDotNet]BenchmarkDotNet.Engines.IHost::ReportResults(valuetype [BenchmarkDotNet]BenchmarkDotNet.Engines.RunResults) - */ + .SetNoInliningImplementationFlag() + .SetAggressiveOptimizationImplementationFlag(); - var reportResultsMethod = typeof(IHost).GetMethod(nameof(IHost.ReportResults)) - ?? throw new MissingMemberException(nameof(IHost.ReportResults)); - ilBuilder.EmitLdarg(hostArg); - ilBuilder.EmitLdloc(runResultsLocal); - ilBuilder.Emit(OpCodes.Callvirt, reportResultsMethod); + var ilBuilder = methodBuilder.GetILGenerator(); /* - // runnable_.__TrickTheJIT__(); - IL_003c: ldloc.0 - IL_003d: callvirt instance void BenchmarkDotNet.Autogenerated.ReplaceMe.Runnable_0::__TrickTheJIT__() + // return; + IL_0001: ret */ - ilBuilder.Emit(OpCodes.Ldloc_0); - ilBuilder.Emit(OpCodes.Callvirt, trickTheJitMethod); - /* - // engineParameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterEngine); - IL_0042: ldloc.2 - IL_0043: callvirt instance class [BenchmarkDotNet]BenchmarkDotNet.Diagnosers.CompositeInProcessDiagnoserHandler [BenchmarkDotNet]BenchmarkDotNet.Engines.EngineParameters::get_InProcessDiagnoserHandler() - IL_0048: ldc.i4.5 - IL_0049: callvirt instance void [BenchmarkDotNet]BenchmarkDotNet.Diagnosers.CompositeInProcessDiagnoserHandler::Handle(valuetype [BenchmarkDotNet]BenchmarkDotNet.Engines.BenchmarkSignal) - */ - ilBuilder.EmitLdloc(engineParametersLocal); - ilBuilder.Emit(OpCodes.Callvirt, typeof(EngineParameters).GetProperty(nameof(EngineParameters.InProcessDiagnoserHandler)).GetGetMethod()); - ilBuilder.Emit(OpCodes.Ldc_I4_5); - ilBuilder.Emit(OpCodes.Callvirt, typeof(Diagnosers.CompositeInProcessDiagnoserHandler).GetMethod(nameof(Diagnosers.CompositeInProcessDiagnoserHandler.Handle))); - ilBuilder.EmitVoidReturn(methodBuilder); return methodBuilder; } + + private MethodInfo GetStartClockMethod() + => typeof(ClockExtensions).GetMethod( + nameof(ClockExtensions.Start), + BindingFlags.Public | BindingFlags.Static, + null, + [typeof(IClock)], + null + ); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SetupCleanupEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SetupCleanupEmitter.cs new file mode 100644 index 0000000000..1bcd7c06f4 --- /dev/null +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SetupCleanupEmitter.cs @@ -0,0 +1,71 @@ +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Helpers.Reflection.Emit; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation; + +partial class RunnableEmitter +{ + private void EmitSetupCleanup(string methodName, MethodInfo? methodToCall, bool isGlobalCleanup) + { + if (methodToCall?.ReturnType.IsAwaitable() == true) + { + EmitAsyncSetupCleanup(methodName, methodToCall, isGlobalCleanup); + } + else + { + EmitSyncSetupCleanup(methodName, methodToCall, isGlobalCleanup); + } + } + + private void EmitSyncSetupCleanup(string methodName, MethodInfo? methodToCall, bool isGlobalCleanup) + { + /* + .method private hidebysig + instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask __GlobalSetup () cil managed flags(0200) + */ + var methodBuilder = runnableBuilder + .DefineNonVirtualInstanceMethod( + methodName, + MethodAttributes.Private, + EmitParameterInfo.CreateReturnParameter(typeof(ValueTask)) + ) + .SetAggressiveOptimizationImplementationFlag(); + var ilBuilder = methodBuilder.GetILGenerator(); + /* + .locals init ( + [0] valuetype [System.Runtime]System.Threading.Tasks.ValueTask + ) + */ + var valueTaskLocal = ilBuilder.DeclareLocal(typeof(ValueTask)); + if (isGlobalCleanup) + { + EmitExtraGlobalCleanup(ilBuilder, null); + } + if (methodToCall != null) + { + EmitNoArgsMethodCallPopReturn(methodBuilder, methodToCall, ilBuilder); + } + /* + // return new ValueTask(); + IL_0000: ldloca.s 0 + IL_0002: initobj [System.Runtime]System.Threading.Tasks.ValueTask + IL_0008: ldloc.0 + IL_0009: ret + */ + ilBuilder.EmitLdloca(valueTaskLocal); + ilBuilder.Emit(OpCodes.Initobj, typeof(ValueTask)); + ilBuilder.EmitLdloc(valueTaskLocal); + ilBuilder.Emit(OpCodes.Ret); + } + + + private void EmitAsyncSetupCleanup(string methodName, MethodInfo methodToCall, bool isGlobalCleanup) + => EmitAsyncSingleCall(methodName, typeof(AsyncValueTaskMethodBuilder), methodToCall, isGlobalCleanup); + + // this.__fieldsContainer.workloadContinuerAndValueTaskSource?.Complete(); + protected abstract void EmitExtraGlobalCleanup(ILGenerator ilBuilder, LocalBuilder? thisLocal); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SyncCoreEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SyncCoreEmitter.cs new file mode 100644 index 0000000000..6d0d0e9e27 --- /dev/null +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SyncCoreEmitter.cs @@ -0,0 +1,163 @@ +using BenchmarkDotNet.Helpers.Reflection.Emit; +using Perfolizer.Horology; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading.Tasks; +using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants; + +namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation; + +partial class RunnableEmitter +{ + private sealed class SyncCoreEmitter : RunnableEmitter + { + protected override void EmitExtraGlobalCleanup(ILGenerator ilBuilder, LocalBuilder? thisLocal) { } + + protected override void EmitCoreImpl() + { + EmitAction(OverheadActionUnrollMethodName, overheadImplementationMethod, jobUnrollFactor); + EmitAction(OverheadActionNoUnrollMethodName, overheadImplementationMethod, 1); + EmitAction(WorkloadActionUnrollMethodName, Descriptor.WorkloadMethod, jobUnrollFactor); + EmitAction(WorkloadActionNoUnrollMethodName, Descriptor.WorkloadMethod, 1); + } + + private MethodBuilder EmitAction(string methodName, MethodInfo methodToCall, int unrollFactor) + { + /* + .method private hidebysig + instance valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1 OverheadActionNoUnroll ( + int64 invokeCount, + class [Perfolizer]Perfolizer.Horology.IClock clock + ) cil managed flags(0200) + */ + var invokeCountArg = new EmitParameterInfo(0, InvokeCountParamName, typeof(long)); + var actionMethodBuilder = runnableBuilder + .DefineNonVirtualInstanceMethod( + methodName, + MethodAttributes.Private, + EmitParameterInfo.CreateReturnParameter(typeof(ValueTask)), + [ + invokeCountArg, + new EmitParameterInfo(1, ClockParamName, typeof(IClock)) + ] + ) + .SetAggressiveOptimizationImplementationFlag(); + invokeCountArg.SetMember(actionMethodBuilder); + + var ilBuilder = actionMethodBuilder.GetILGenerator(); + + // init locals + var argLocals = argFields.Select(a => ilBuilder.DeclareLocal(a.ArgLocalsType)).ToList(); + var startedClockLocal = ilBuilder.DeclareLocal(typeof(StartedClock)); + + // load fields + EmitLoadArgFieldsToLocals(ilBuilder, argLocals); + + /* + // StartedClock startedClock = ClockExtensions.Start(clock); + IL_0000: ldarg.2 + IL_0001: call valuetype [Perfolizer]Perfolizer.Horology.StartedClock [Perfolizer]Perfolizer.Horology.ClockExtensions::Start(class [Perfolizer]Perfolizer.Horology.IClock) + IL_0006: stloc.0 + */ + ilBuilder.Emit(OpCodes.Ldarg_2); + ilBuilder.Emit(OpCodes.Call, GetStartClockMethod()); + ilBuilder.EmitStloc(startedClockLocal); + + // loop + ilBuilder.EmitLoopBeginFromArgToZero(out var loopStartLabel, out var loopHeadLabel); + { + for (int u = 0; u < unrollFactor; u++) + { + /* + // InvokeOnceVoid(); + IL_0008: ldarg.0 + IL_0009: call instance void [BenchmarkDotNet.IntegrationTests]BenchmarkDotNet.IntegrationTests.InProcessEmitTest/BenchmarkAllCases::InvokeOnceVoid() + */ + if (!methodToCall.IsStatic) + { + ilBuilder.Emit(OpCodes.Ldarg_0); + } + ilBuilder.EmitLdLocals(argLocals); + ilBuilder.Emit(OpCodes.Call, methodToCall); + + if (methodToCall.ReturnType != typeof(void)) + { + // IL_000b: pop + ilBuilder.Emit(OpCodes.Pop); + } + } + } + ilBuilder.EmitLoopEndFromArgToZero(loopStartLabel, loopHeadLabel, invokeCountArg); + + /* + // return new ValueTask(startedClock.GetElapsed()); + IL_0034: ldloca.s 2 + IL_0036: call instance valuetype [Perfolizer]Perfolizer.Horology.ClockSpan [Perfolizer]Perfolizer.Horology.StartedClock::GetElapsed() + IL_003b: newobj instance void valuetype [System.Runtime]System.Threading.Tasks.ValueTask`1::.ctor(!0) + IL_0040: ret + */ + ilBuilder.EmitLdloca(startedClockLocal); + ilBuilder.Emit(OpCodes.Call, typeof(StartedClock).GetMethod(nameof(StartedClock.GetElapsed), BindingFlags.Public | BindingFlags.Instance)); + ilBuilder.Emit(OpCodes.Newobj, typeof(ValueTask).GetConstructor([typeof(ClockSpan)])); + ilBuilder.Emit(OpCodes.Ret); + + return actionMethodBuilder; + } + + private void EmitLoadArgFieldsToLocals(ILGenerator ilBuilder, List argLocals) + { + /* + // bool _argField = __fieldsContainer.__argField0; + IL_0000: ldarg.0 + IL_0001: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer + IL_0006: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField0 + IL_000b: stloc.0 + // int _argField2 = __fieldsContainer.__argField1; + IL_000c: ldarg.0 + IL_000d: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer + IL_0012: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField1 + IL_0017: stloc.1 + + // -or- + + // ref bool _argField = ref __fieldsContainer.__argField0; + IL_0000: ldarg.0 + IL_0001: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer + IL_0006: ldflda bool BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField0 + IL_000b: stloc.0 + // ref int _argField2 = ref __fieldsContainer.__argField1; + IL_000c: ldarg.0 + IL_000d: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer + IL_0012: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField1 + IL_0017: stloc.1 + + // -or- (ref struct arg call) + + // Span arg = __fieldsContainer.__argField0; + IL_0000: ldarg.0 + IL_0001: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer + IL_0006: ldfld int32[] BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField0 + IL_000b: call valuetype [System.Runtime]System.Span`1 valuetype [System.Runtime]System.Span`1::op_Implicit(!0[]) + IL_0010: stloc.0 + */ + for (int i = 0; i < argFields.Count; i++) + { + ilBuilder.Emit(OpCodes.Ldarg_0); + ilBuilder.Emit(OpCodes.Ldflda, fieldsContainerField); + + var argFieldInfo = argFields[i]; + if (argFieldInfo.ArgLocalsType.IsByRef) + ilBuilder.Emit(OpCodes.Ldflda, argFieldInfo.Field); + else + ilBuilder.Emit(OpCodes.Ldfld, argFieldInfo.Field); + + if (argFieldInfo.OpImplicitMethod != null) + ilBuilder.Emit(OpCodes.Call, argFieldInfo.OpImplicitMethod); + + ilBuilder.EmitStloc(argLocals[i]); + } + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableConstants.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableConstants.cs index 0a0ac33313..a22e7262da 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableConstants.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableConstants.cs @@ -1,23 +1,15 @@ namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation { - /// - /// A helper type that emits code that matches BenchmarkType.txt template. - /// IMPORTANT: this type IS NOT thread safe. - /// - public class RunnableConstants + internal class RunnableConstants { - public const string IsByRefLikeAttributeTypeName = "System.Runtime.CompilerServices.IsByRefLikeAttribute"; public const string OpImplicitMethodName = "op_Implicit"; public const string DynamicAssemblySuffix = "Emitted"; public const string EmittedTypePrefix = "BenchmarkDotNet.Autogenerated.Runnable_"; public const string ArgFieldPrefix = "__argField"; public const string ArgParamPrefix = "arg"; + public const string FieldsContainerName = "__fieldsContainer"; - public const string GlobalSetupActionFieldName = "globalSetupAction"; - public const string GlobalCleanupActionFieldName = "globalCleanupAction"; - public const string IterationSetupActionFieldName = "iterationSetupAction"; - public const string IterationCleanupActionFieldName = "iterationCleanupAction"; public const string NotElevenFieldName = "NotEleven"; public const string TrickTheJitCoreMethodName = "__TrickTheJIT__"; @@ -29,14 +21,21 @@ public class RunnableConstants public const string WorkloadActionNoUnrollMethodName = "WorkloadActionNoUnroll"; public const string ForDisassemblyDiagnoserMethodName = "__ForDisassemblyDiagnoser__"; public const string InvokeCountParamName = "invokeCount"; + public const string ClockParamName = "clock"; public const string DummyParamName = "_"; - public const string GlobalSetupMethodName = "GlobalSetup"; - public const string GlobalCleanupMethodName = "GlobalCleanup"; - public const string IterationSetupMethodName = "IterationSetup"; - public const string IterationCleanupMethodName = "IterationCleanup"; + public const string GlobalSetupMethodName = "__GlobalSetup"; + public const string GlobalCleanupMethodName = "__GlobalCleanup"; + public const string IterationSetupMethodName = "__IterationSetup"; + public const string IterationCleanupMethodName = "__IterationCleanup"; public const string RunMethodName = "Run"; + + public const string WorkloadContinuerAndValueTaskSourceFieldName = "workloadContinuerAndValueTaskSource"; + public const string ClockFieldName = "clock"; + public const string InvokeCountFieldName = "invokeCount"; + public const string StartWorkloadMethodName = "__StartWorkload"; + public const string WorkloadCoreMethodName = "__WorkloadCore"; } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableProgram.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableProgram.cs deleted file mode 100644 index 2516f4e6d5..0000000000 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableProgram.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Reflection; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Toolchains.Parameters; -using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants; -using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableReflectionHelpers; - -namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation -{ - internal class RunnableProgram - { - internal static int Run(Assembly partitionAssembly, IHost host, ExecuteParameters parameters) - { - // the first thing to do is to let diagnosers hook in before anything happens - // so all jit-related diagnosers can catch first jit compilation! - host.BeforeAnythingElse(); - - try - { - // we are not using Runnable here in any direct way in order to avoid strong dependency Main<=>Runnable - // which could cause the jitting/assembly loading to happen before we do anything - // we have some jitting diagnosers and we want them to catch all the informations!! - - var runCallback = GetRunCallback(parameters.BenchmarkId, partitionAssembly); - - runCallback.Invoke(null, [host, parameters]); - return 0; - } - catch (Exception oom) when ( - oom is OutOfMemoryException || - oom is TargetInvocationException reflection && reflection.InnerException is OutOfMemoryException) - { - DumpOutOfMemory(host, oom); - return -1; - } - catch (Exception ex) - { - DumpError(host, ex); - return -1; - } - finally - { - host.AfterAll(); - } - } - - private static MethodInfo GetRunCallback( - BenchmarkId benchmarkId, Assembly partitionAssembly) - { - var runnableType = partitionAssembly.GetType(GetRunnableTypeName(benchmarkId)); - - var runnableMethod = runnableType.GetMethod(RunMethodName, BindingFlagsPublicStatic); - - return runnableMethod; - } - - private static string GetRunnableTypeName(BenchmarkId benchmarkId) - { - return EmittedTypePrefix + benchmarkId; - } - - private static void DumpOutOfMemory(IHost host, Exception oom) - { - host.WriteLine(); - host.WriteLine("OutOfMemoryException!"); - host.WriteLine( - "BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); - host.WriteLine( - "If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); - host.WriteLine( - "You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); - host.WriteLine(); - host.WriteLine(oom.ToString()); - } - - private static void DumpError(IHost host, Exception ex) - { - host.WriteLine(); - host.WriteLine(ex.ToString()); - } - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableReflectionHelpers.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableReflectionHelpers.cs index a19d6c29a7..9c26e64049 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableReflectionHelpers.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableReflectionHelpers.cs @@ -1,8 +1,10 @@ using System; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using BenchmarkDotNet.Parameters; using BenchmarkDotNet.Running; +using Perfolizer.Horology; using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants; namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation @@ -31,18 +33,12 @@ private static object TryChangeType(object value, Type targetType) { var implicitOp = GetImplicitConversionOpFromTo(value.GetType(), targetType); if (implicitOp != null) - return implicitOp.Invoke(null, new[] { value }); + return implicitOp.Invoke(null, [value]); } return value; } - public static bool IsRefLikeType(Type t) - { - return t.IsValueType - && t.GetCustomAttributes().Any(a => a.GetType().FullName == IsByRefLikeAttributeTypeName); - } - public static MethodInfo GetImplicitConversionOpFromTo(Type from, Type to) { return GetImplicitConversionOpCore(to, from, to) @@ -58,35 +54,28 @@ private static MethodInfo GetImplicitConversionOpCore(Type owner, Type from, Typ && m.GetParameters().Single().ParameterType == from); } - public static void SetArgumentField( - T instance, - BenchmarkCase benchmarkCase, - ParameterInfo argInfo, - int argIndex) + public static void SetArgumentField(object instance, BenchmarkCase benchmarkCase, ParameterInfo argInfo, int argIndex) { - var argValue = benchmarkCase.Parameters.GetArgument(argInfo.Name); - if (argValue == null) - { - throw new InvalidOperationException($"Can't find arg member for {argInfo.Name}."); - } + var argValue = benchmarkCase.Parameters.GetArgument(argInfo.Name) + ?? throw new InvalidOperationException($"Can't find arg member for {argInfo.Name}."); + + var containerField = instance.GetType().GetField(FieldsContainerName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + ?? throw new InvalidOperationException("FieldsContainer field not found on runnable instance."); + + var container = containerField.GetValue(instance); - var type = instance.GetType(); var argName = ArgFieldPrefix + argIndex; - if (type.GetField(argName, BindingFlagsNonPublicInstance) is var f && f != null) - { - f.SetValue(instance, TryChangeType(argValue.Value, f.FieldType)); - } - else - { - throw new InvalidOperationException($"Can't find arg member for {argInfo.Name}."); - } + + var argField = container.GetType().GetField(argName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + ?? throw new InvalidOperationException($"Can't find arg member {argName} inside FieldsContainer."); + + argField.SetValue(container, TryChangeType(argValue.Value, argField.FieldType)); + containerField.SetValue(instance, container); } - public static void SetParameter( - T instance, - ParameterInstance paramInfo) + public static void SetParameter(object instance, ParameterInstance paramInfo) { - var instanceArg = paramInfo.IsStatic ? null : (object)instance; + var instanceArg = paramInfo.IsStatic ? null : instance; var bindingFlags = paramInfo.IsStatic ? BindingFlagsAllStatic : BindingFlagsAllInstance; var type = instance.GetType(); @@ -104,28 +93,17 @@ public static void SetParameter( } } - public static Action CallbackFromField(T instance, string memberName) - { - return GetFieldValueCore(instance, memberName); - } - - public static Action LoopCallbackFromMethod(T instance, string memberName) + public static Func SetupOrCleanupCallbackFromMethod(object instance, string memberName) { - return GetDelegateCore>(instance, memberName); + return GetDelegateCore>(instance, memberName); } - private static TResult GetFieldValueCore(T instance, string memberName) + public static Func> LoopCallbackFromMethod(object instance, string memberName) { - var result = instance.GetType().GetField( - memberName, - BindingFlagsAllInstance); - if (result == null) - throw new InvalidOperationException($"Can't find a member {memberName}."); - - return (TResult)result.GetValue(instance); + return GetDelegateCore>>(instance, memberName); } - private static TDelegate GetDelegateCore(T instance, string memberName) + private static TDelegate GetDelegateCore(object instance, string memberName) { var result = instance.GetType().GetMethod( memberName, diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableReuse.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableReuse.cs deleted file mode 100644 index c80ccc3b4a..0000000000 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableReuse.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.Linq; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Environments; -using BenchmarkDotNet.Exporters; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Toolchains.Parameters; -using BenchmarkDotNet.Validators; -using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants; -using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableReflectionHelpers; - -namespace BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation -{ - public static class RunnableReuse - { - public static (Job, EngineParameters, IEngineFactory) PrepareForRun(T instance, IHost host, ExecuteParameters parameters) - { - var benchmarkCase = parameters.BenchmarkCase; - FillObjectMembers(instance, benchmarkCase); - - DumpEnvironment(host); - - var job = CreateJob(benchmarkCase); - DumpJob(host, job); - - var errors = BenchmarkProcessValidator.Validate(job, instance); - if (ValidationErrorReporter.ReportIfAny(errors, host)) - return default; - - var compositeInProcessDiagnoserHandler = new CompositeInProcessDiagnoserHandler( - parameters.CompositeInProcessDiagnoser.InProcessDiagnosers - .Select((d, i) => InProcessDiagnoserRouter.Create(d, benchmarkCase, i)) - .Where(r => r.handler != null) - .ToArray(), - host, - parameters.DiagnoserRunMode, - new InProcessDiagnoserActionArgs(instance) - ); - if (parameters.DiagnoserRunMode == Diagnosers.RunMode.SeparateLogic) - { - compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.SeparateLogic); - return default; - } - compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.BeforeEngine); - - var engineParameters = CreateEngineParameters(instance, benchmarkCase, host, compositeInProcessDiagnoserHandler); - var engineFactory = GetEngineFactory(benchmarkCase); - - return (job, engineParameters, engineFactory); - } - - public static void FillObjectMembers(T instance, BenchmarkCase benchmarkCase) - { - var argIndex = 0; - foreach (var argInfo in benchmarkCase.Descriptor.WorkloadMethod.GetParameters()) - { - SetArgumentField(instance, benchmarkCase, argInfo, argIndex); - argIndex++; - } - - foreach (var paramInfo in benchmarkCase.Parameters.Items - .Where(parameter => !parameter.IsArgument)) - { - SetParameter(instance, paramInfo); - } - } - - private static void DumpEnvironment(IHost host) - { - host.WriteLine(); - foreach (var infoLine in BenchmarkEnvironmentInfo.GetCurrent().ToFormattedString()) - { - host.WriteLine("// {0}", infoLine); - } - } - - private static Job CreateJob(BenchmarkCase benchmarkCase) - { - var job = new Job(); - job.Apply(benchmarkCase.Job); - job.Freeze(); - return job; - } - - private static void DumpJob(IHost host, Job job) - { - host.WriteLine("// Job: {0}", job.DisplayInfo); - host.WriteLine(); - } - - private static IEngineFactory GetEngineFactory(BenchmarkCase benchmarkCase) - { - return benchmarkCase.Job.ResolveValue( - InfrastructureMode.EngineFactoryCharacteristic, - InfrastructureResolver.Instance); - } - - private static EngineParameters CreateEngineParameters(T instance, BenchmarkCase benchmarkCase, IHost host, CompositeInProcessDiagnoserHandler inProcessDiagnoserHandler) - => new() - { - Host = host, - WorkloadActionUnroll = LoopCallbackFromMethod(instance, WorkloadActionUnrollMethodName), - WorkloadActionNoUnroll = LoopCallbackFromMethod(instance, WorkloadActionNoUnrollMethodName), - OverheadActionNoUnroll = LoopCallbackFromMethod(instance, OverheadActionNoUnrollMethodName), - OverheadActionUnroll = LoopCallbackFromMethod(instance, OverheadActionUnrollMethodName), - GlobalSetupAction = CallbackFromField(instance, GlobalSetupActionFieldName), - GlobalCleanupAction = CallbackFromField(instance, GlobalCleanupActionFieldName), - IterationSetupAction = CallbackFromField(instance, IterationSetupActionFieldName), - IterationCleanupAction = CallbackFromField(instance, IterationCleanupActionFieldName), - TargetJob = benchmarkCase.Job, - OperationsPerInvoke = benchmarkCase.Descriptor.OperationsPerInvoke, - RunExtraIteration = benchmarkCase.Config.HasExtraIterationDiagnoser(benchmarkCase), - BenchmarkName = FullNameProvider.GetBenchmarkName(benchmarkCase), - InProcessDiagnoserHandler = inProcessDiagnoserHandler - }; - } -} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs index 25a1271666..98babe8a8f 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Extensions; @@ -16,14 +17,15 @@ namespace BenchmarkDotNet.Toolchains.InProcess.Emit { internal class InProcessEmitExecutor(bool executeOnSeparateThread) : IExecutor { - public ExecuteResult Execute(ExecuteParameters executeParameters) + public async ValueTask ExecuteAsync(ExecuteParameters executeParameters) { var host = new InProcessHost(executeParameters.BenchmarkCase, executeParameters.Logger, executeParameters.Diagnoser); int exitCode = -1; if (executeOnSeparateThread) { - var runThread = new Thread(() => exitCode = ExecuteCore(host, executeParameters)); + var taskCompletionSource = new TaskCompletionSource(); + var runThread = new Thread(async () => taskCompletionSource.SetResult(await ExecuteCore(host, executeParameters))); if (executeParameters.BenchmarkCase.Descriptor.WorkloadMethod.GetCustomAttributes(false).Any() && OsDetector.IsWindows()) @@ -35,17 +37,20 @@ public ExecuteResult Execute(ExecuteParameters executeParameters) runThread.Start(); runThread.Join(); + + // We must await the task after joining the thread or else it can deadlock. + exitCode = await taskCompletionSource.Task; } else { - exitCode = ExecuteCore(host, executeParameters); + exitCode = await ExecuteCore(host, executeParameters); } host.HandleInProcessDiagnoserResults(executeParameters.BenchmarkCase, executeParameters.CompositeInProcessDiagnoser); return ExecuteResult.FromRunResults(host.RunResults, exitCode); } - private int ExecuteCore(IHost host, ExecuteParameters parameters) + private async ValueTask ExecuteCore(IHost host, ExecuteParameters parameters) { int exitCode = -1; var process = Process.GetCurrentProcess(); @@ -65,10 +70,7 @@ private int ExecuteCore(IHost host, ExecuteParameters parameters) process.TrySetAffinity(affinity.Value, parameters.Logger); } - var generatedAssembly = ((InProcessEmitArtifactsPath)parameters.BuildResult.ArtifactsPaths) - .GeneratedAssembly; - - exitCode = RunnableProgram.Run(generatedAssembly, host, parameters); + exitCode = await InProcessEmitRunner.Run(host, parameters); } catch (Exception ex) { diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs new file mode 100644 index 0000000000..728c8d62a3 --- /dev/null +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs @@ -0,0 +1,141 @@ +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains.Parameters; +using BenchmarkDotNet.Validators; +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableConstants; +using static BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation.RunnableReflectionHelpers; + +namespace BenchmarkDotNet.Toolchains.InProcess.Emit; + +internal static class InProcessEmitRunner +{ + public static async ValueTask Run(IHost host, ExecuteParameters parameters) + { + // the first thing to do is to let diagnosers hook in before anything happens + // so all jit-related diagnosers can catch first jit compilation! + host.BeforeAnythingElse(); + + try + { + var runnableType = ((InProcessEmitArtifactsPath) parameters.BuildResult.ArtifactsPaths) + .GeneratedAssembly + .GetType(EmittedTypePrefix + parameters.BenchmarkId); + + await RunCore(runnableType, host, parameters); + + return 0; + } + catch (Exception oom) when (oom is OutOfMemoryException || oom is TargetInvocationException reflection && reflection.InnerException is OutOfMemoryException) + { + host.WriteLine(); + host.WriteLine("OutOfMemoryException!"); + host.WriteLine("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); + host.WriteLine("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); + host.WriteLine("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); + host.WriteLine(); + host.WriteLine(oom.ToString()); + + return -1; + } + catch (Exception ex) + { + host.WriteLine(); + host.WriteLine(ex.ToString()); + return -1; + } + finally + { + host.AfterAll(); + } + } + + private static async ValueTask RunCore(Type runnableType, IHost host, ExecuteParameters parameters) + { + var benchmarkCase = parameters.BenchmarkCase; + + var instance = Activator.CreateInstance(runnableType); + FillMembers(instance, benchmarkCase); + + host.WriteLine(); + foreach (string infoLine in BenchmarkEnvironmentInfo.GetCurrent().ToFormattedString()) + { + host.WriteLine($"// {infoLine}"); + } + var job = new Job().Apply(benchmarkCase.Job).Freeze(); + host.WriteLine($"// Job: {job.DisplayInfo}"); + host.WriteLine(); + + var errors = BenchmarkProcessValidator.Validate(job, instance); + if (ValidationErrorReporter.ReportIfAny(errors, host)) + return; + + var compositeInProcessDiagnoserHandler = new Diagnosers.CompositeInProcessDiagnoserHandler( + parameters.CompositeInProcessDiagnoser.InProcessDiagnosers + .Select((d, i) => Diagnosers.InProcessDiagnoserRouter.Create(d, benchmarkCase, i)) + .Where(r => r.handler != null) + .ToArray(), + host, + parameters.DiagnoserRunMode, + new Diagnosers.InProcessDiagnoserActionArgs(instance) + ); + if (parameters.DiagnoserRunMode == Diagnosers.RunMode.SeparateLogic) + { + compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.SeparateLogic); + return; + } + compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.BeforeEngine); + + var engineParameters = new EngineParameters() + { + Host = host, + WorkloadActionUnroll = LoopCallbackFromMethod(instance, WorkloadActionUnrollMethodName), + WorkloadActionNoUnroll = LoopCallbackFromMethod(instance, WorkloadActionNoUnrollMethodName), + OverheadActionNoUnroll = LoopCallbackFromMethod(instance, OverheadActionNoUnrollMethodName), + OverheadActionUnroll = LoopCallbackFromMethod(instance, OverheadActionUnrollMethodName), + GlobalSetupAction = SetupOrCleanupCallbackFromMethod(instance, GlobalSetupMethodName), + GlobalCleanupAction = SetupOrCleanupCallbackFromMethod(instance, GlobalCleanupMethodName), + IterationSetupAction = SetupOrCleanupCallbackFromMethod(instance, IterationSetupMethodName), + IterationCleanupAction = SetupOrCleanupCallbackFromMethod(instance, IterationCleanupMethodName), + TargetJob = benchmarkCase.Job, + OperationsPerInvoke = benchmarkCase.Descriptor.OperationsPerInvoke, + RunExtraIteration = benchmarkCase.Config.HasExtraIterationDiagnoser(benchmarkCase), + BenchmarkName = FullNameProvider.GetBenchmarkName(benchmarkCase), + InProcessDiagnoserHandler = compositeInProcessDiagnoserHandler + }; + + var results = await job + .ResolveValue(InfrastructureMode.EngineFactoryCharacteristic, InfrastructureResolver.Instance) + .Create(engineParameters) + .RunAsync(); + host.ReportResults(results); + + runnableType.GetMethod(TrickTheJitCoreMethodName).Invoke(instance, []); + + compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterEngine); + } + + private static void FillMembers(object instance, BenchmarkCase benchmarkCase) + { + var argIndex = 0; + foreach (var argInfo in benchmarkCase.Descriptor.WorkloadMethod.GetParameters()) + { + SetArgumentField(instance, benchmarkCase, argInfo, argIndex); + argIndex++; + } + + foreach (var paramInfo in benchmarkCase.Parameters.Items) + { + if (!paramInfo.IsArgument) + { + SetParameter(instance, paramInfo); + } + } + } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs index 27e20c1867..b96bd255b0 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkAction.cs @@ -1,17 +1,13 @@ -using System; +using Perfolizer.Horology; +using System; +using System.Threading.Tasks; -namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit -{ - /// Common API to run the Setup/Clean/Idle/Run methods - internal abstract class BenchmarkAction - { - /// Gets or sets invoke single callback. - /// Invoke single callback. - public Action InvokeSingle { get; protected set; } +namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit; - /// Gets or sets invoke multiple times callback. - /// Invoke multiple times callback. - public Action InvokeUnroll { get; protected set; } - public Action InvokeNoUnroll{ get; protected set; } - } +internal abstract class BenchmarkAction +{ + public Func InvokeSingle { get; protected set; } + public Func> InvokeUnroll { get; protected set; } + public Func> InvokeNoUnroll { get; protected set; } + public abstract void Complete(); } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs index 4473a909eb..d1bd0e8a4d 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory.cs @@ -3,161 +3,137 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Threading.Tasks; - using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Running; -namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit +namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit; + +internal static partial class BenchmarkActionFactory { - /// Helper class that creates instances. - internal static partial class BenchmarkActionFactory + /// + /// Dispatch method that creates using + /// or to find correct implementation. + /// Either or should be not null. + /// + private static BenchmarkAction CreateCore(object instance, MethodInfo? targetMethod, MethodInfo? fallbackIdleSignature, int unrollFactor) { - /// - /// Dispatch method that creates using - /// or to find correct implementation. - /// Either or should be not null. - /// - private static BenchmarkAction CreateCore(object instance, MethodInfo? targetMethod, MethodInfo? fallbackIdleSignature, int unrollFactor) - { - PrepareInstanceAndResultType(instance, targetMethod, fallbackIdleSignature, out var resultInstance, out var resultType); + PrepareInstanceAndResultType(instance, targetMethod, fallbackIdleSignature, out var resultInstance, out var resultType); - if (resultType == typeof(void)) - return new BenchmarkActionVoid(resultInstance, targetMethod, unrollFactor); + if (resultType == typeof(void)) + return new BenchmarkActionVoid(resultInstance, targetMethod, unrollFactor); - if (resultType == typeof(void*)) - return new BenchmarkActionVoidPointer(resultInstance, targetMethod, unrollFactor); - - if (resultType.IsByRef) - { - var returnParameter = targetMethod?.ReturnParameter ?? fallbackIdleSignature.ReturnParameter; - // IsReadOnlyAttribute is not part of netstandard2.0, so we need to check the attribute name as usual. - if (returnParameter.GetCustomAttributes().Any(attribute => attribute.GetType().FullName == "System.Runtime.CompilerServices.IsReadOnlyAttribute")) - return Create( - typeof(BenchmarkActionByRefReadonly<>).MakeGenericType(resultType.GetElementType()), - resultInstance, - targetMethod, - unrollFactor); + if (resultType == typeof(void*)) + return new BenchmarkActionVoidPointer(resultInstance, targetMethod, unrollFactor); + if (resultType.IsByRef) + { + var returnParameter = targetMethod?.ReturnParameter ?? fallbackIdleSignature.ReturnParameter; + // IsReadOnlyAttribute is not part of netstandard2.0, so we need to check the attribute name as usual. + if (returnParameter.GetCustomAttributes().Any(attribute => attribute.GetType().FullName == "System.Runtime.CompilerServices.IsReadOnlyAttribute")) return Create( - typeof(BenchmarkActionByRef<>).MakeGenericType(resultType.GetElementType()), + typeof(BenchmarkActionByRefReadonly<>).MakeGenericType(resultType.GetElementType()), resultInstance, targetMethod, unrollFactor); - } - - if (resultType == typeof(Task)) - return new BenchmarkActionTask(resultInstance, targetMethod, unrollFactor); - - if (resultType == typeof(ValueTask)) - return new BenchmarkActionValueTask(resultInstance, targetMethod, unrollFactor); - - if (resultType.GetTypeInfo().IsGenericType) - { - var genericType = resultType.GetGenericTypeDefinition(); - var argType = resultType.GenericTypeArguments[0]; - if (typeof(Task<>) == genericType) - return Create( - typeof(BenchmarkActionTask<>).MakeGenericType(argType), - resultInstance, - targetMethod, - unrollFactor); - - if (typeof(ValueTask<>).IsAssignableFrom(genericType)) - return Create( - typeof(BenchmarkActionValueTask<>).MakeGenericType(argType), - resultInstance, - targetMethod, - unrollFactor); - } return Create( - typeof(BenchmarkAction<>).MakeGenericType(resultType), + typeof(BenchmarkActionByRef<>).MakeGenericType(resultType.GetElementType()), resultInstance, targetMethod, unrollFactor); } - private static void PrepareInstanceAndResultType( - object instance, MethodInfo targetMethod, MethodInfo fallbackIdleSignature, - out object resultInstance, out Type resultType) + if (resultType == typeof(Task)) + return new BenchmarkActionTask(resultInstance, targetMethod, unrollFactor); + + if (resultType == typeof(ValueTask)) + return new BenchmarkActionValueTask(resultInstance, targetMethod, unrollFactor); + + if (resultType.GetTypeInfo().IsGenericType) + { + var genericType = resultType.GetGenericTypeDefinition(); + var argType = resultType.GenericTypeArguments[0]; + if (typeof(Task<>) == genericType) + return Create( + typeof(BenchmarkActionTask<>).MakeGenericType(argType), + resultInstance, + targetMethod, + unrollFactor); + + if (typeof(ValueTask<>).IsAssignableFrom(genericType)) + return Create( + typeof(BenchmarkActionValueTask<>).MakeGenericType(argType), + resultInstance, + targetMethod, + unrollFactor); + } + + if (resultType.IsAwaitable()) { - var signature = targetMethod ?? fallbackIdleSignature; - if (signature == null) - throw new ArgumentNullException( - nameof(fallbackIdleSignature), - $"Either {nameof(targetMethod)} or {nameof(fallbackIdleSignature)} should be not null."); - - if (!signature.IsStatic && instance == null) - throw new ArgumentNullException( - nameof(instance), - $"The {nameof(instance)} parameter should be not null as invocation method is instance method."); - - resultInstance = signature.IsStatic ? null : instance; - resultType = signature.ReturnType; - - if (resultType == typeof(void)) - { - // DONTTOUCH: async should be checked for target method - // as fallbackIdleSignature used for result type detection only. - bool isUsingAsyncKeyword = targetMethod?.HasAttribute() ?? false; - if (isUsingAsyncKeyword) - throw new NotSupportedException("Async void is not supported by design."); - } - else if (resultType.IsPointer && resultType != typeof(void*)) - { - throw new NotSupportedException("InProcessNoEmitToolchain only supports void* return, not T*"); - } + throw new NotSupportedException($"{nameof(InProcessNoEmitToolchain)} does not support returning awaitable types except (Value)Task()."); } - /// Helper to enforce .ctor signature. - private static BenchmarkActionBase Create(Type actionType, object instance, MethodInfo method, int unrollFactor) => - (BenchmarkActionBase)Activator.CreateInstance(actionType, instance, method, unrollFactor); - - private static void FallbackMethod() { } - private static readonly MethodInfo FallbackSignature = new Action(FallbackMethod).GetMethodInfo(); - - /// Creates run benchmark action. - /// Descriptor info. - /// Instance of target. - /// Unroll factor. - /// Run benchmark action. - public static BenchmarkAction CreateWorkload(Descriptor descriptor, object instance, int unrollFactor) => - CreateCore(instance, descriptor.WorkloadMethod, null, unrollFactor); - - /// Creates idle benchmark action. - /// Descriptor info. - /// Instance of target. - /// Unroll factor. - /// Idle benchmark action. - public static BenchmarkAction CreateOverhead(Descriptor descriptor, object instance, int unrollFactor) => - CreateCore(instance, null, FallbackSignature, unrollFactor); - - /// Creates global setup benchmark action. - /// Descriptor info. - /// Instance of target. - /// Setup benchmark action. - public static BenchmarkAction CreateGlobalSetup(Descriptor descriptor, object instance) => - CreateCore(instance, descriptor.GlobalSetupMethod, FallbackSignature, 1); - - /// Creates global cleanup benchmark action. - /// Descriptor info. - /// Instance of target. - /// Cleanup benchmark action. - public static BenchmarkAction CreateGlobalCleanup(Descriptor descriptor, object instance) => - CreateCore(instance, descriptor.GlobalCleanupMethod, FallbackSignature, 1); - - /// Creates global setup benchmark action. - /// Descriptor info. - /// Instance of target. - /// Setup benchmark action. - public static BenchmarkAction CreateIterationSetup(Descriptor descriptor, object instance) => - CreateCore(instance, descriptor.IterationSetupMethod, FallbackSignature, 1); - - /// Creates global cleanup benchmark action. - /// Descriptor info. - /// Instance of target. - /// Cleanup benchmark action. - public static BenchmarkAction CreateIterationCleanup(Descriptor descriptor, object instance) => - CreateCore(instance, descriptor.IterationCleanupMethod, FallbackSignature, 1); + return Create( + typeof(BenchmarkAction<>).MakeGenericType(resultType), + resultInstance, + targetMethod, + unrollFactor); } + + private static void PrepareInstanceAndResultType( + object instance, MethodInfo targetMethod, MethodInfo fallbackIdleSignature, + out object resultInstance, out Type resultType) + { + var signature = targetMethod ?? fallbackIdleSignature; + if (signature == null) + throw new ArgumentNullException( + nameof(fallbackIdleSignature), + $"Either {nameof(targetMethod)} or {nameof(fallbackIdleSignature)} should be not null."); + + if (!signature.IsStatic && instance == null) + throw new ArgumentNullException( + nameof(instance), + $"The {nameof(instance)} parameter should be not null as invocation method is instance method."); + + resultInstance = signature.IsStatic ? null : instance; + resultType = signature.ReturnType; + + if (resultType == typeof(void)) + { + // DONTTOUCH: async should be checked for target method + // as fallbackIdleSignature used for result type detection only. + bool isUsingAsyncKeyword = targetMethod?.HasAttribute() ?? false; + if (isUsingAsyncKeyword) + throw new NotSupportedException("Async void is not supported by design."); + } + else if (resultType.IsPointer && resultType != typeof(void*)) + { + throw new NotSupportedException("InProcessNoEmitToolchain only supports void* return, not T*"); + } + } + + /// Helper to enforce .ctor signature. + private static BenchmarkActionBase Create(Type actionType, object instance, MethodInfo method, int unrollFactor) => + (BenchmarkActionBase)Activator.CreateInstance(actionType, instance, method, unrollFactor); + + private static void FallbackMethod() { } + private static readonly MethodInfo FallbackSignature = new Action(FallbackMethod).GetMethodInfo(); + + public static BenchmarkAction CreateWorkload(Descriptor descriptor, object instance, int unrollFactor) => + CreateCore(instance, descriptor.WorkloadMethod, null, unrollFactor); + + public static BenchmarkAction CreateOverhead(Descriptor descriptor, object instance, int unrollFactor) => + CreateCore(instance, null, FallbackSignature, unrollFactor); + + public static BenchmarkAction CreateGlobalSetup(Descriptor descriptor, object instance) => + CreateCore(instance, descriptor.GlobalSetupMethod, FallbackSignature, 1); + + public static BenchmarkAction CreateGlobalCleanup(Descriptor descriptor, object instance) => + CreateCore(instance, descriptor.GlobalCleanupMethod, FallbackSignature, 1); + + public static BenchmarkAction CreateIterationSetup(Descriptor descriptor, object instance) => + CreateCore(instance, descriptor.IterationSetupMethod, FallbackSignature, 1); + + public static BenchmarkAction CreateIterationCleanup(Descriptor descriptor, object instance) => + CreateCore(instance, descriptor.IterationCleanupMethod, FallbackSignature, 1); } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Base.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Base.cs index 542e11fa95..3551dae874 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Base.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Base.cs @@ -12,14 +12,14 @@ 1. Overhead signature should match to the benchmark method signature (including 2. Should work under .Net native. Uses Delegate.Combine instead of emitting the code. 3. High data locality and no additional allocations / JIT where possible. This means NO closures allowed, no allocations but in .ctor and for LastCallResult boxing, - all state should be stored explicitly as BenchmarkAction's fields. + all state should be stored explicitly as BenchmarkFunc's fields. 4. There can be multiple benchmark actions per single target instance (workload, globalSetup, globalCleanup methods), so target instantiation is not a responsibility of the benchmark action. 5. Implementation should match to the code in BenchmarkProgram.txt. */ // DONTTOUCH: Be VERY CAREFUL when changing the code. - // Please, ensure that the implementation is in sync with content of BenchmarkProgram.txt + // Please, ensure that the implementation is in sync with content of BenchmarkType.txt internal static partial class BenchmarkActionFactory { /// Base class that provides reusable API for final implementations. diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs index 21911c6af9..49d3090b78 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/BenchmarkActionFactory_Implementations.cs @@ -1,7 +1,8 @@ -using BenchmarkDotNet.Portability; +using BenchmarkDotNet.Attributes.CompilerServices; +using BenchmarkDotNet.Engines; +using Perfolizer.Horology; using System; using System.Reflection; -using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit @@ -11,9 +12,10 @@ namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit */ // DONTTOUCH: Be VERY CAREFUL when changing the code. - // Please, ensure that the implementation is in sync with content of BenchmarkProgram.txt + // Please, ensure that the implementation is in sync with content of BenchmarkType.txt internal static partial class BenchmarkActionFactory { + [AggressivelyOptimizeMethods] internal sealed class BenchmarkActionVoid : BenchmarkActionBase { private readonly Action callback; @@ -23,30 +25,41 @@ public BenchmarkActionVoid(object? instance, MethodInfo? method, int unrollFacto { callback = CreateWorkloadOrOverhead(instance, method); unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = callback; + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask InvokeOnce() { - for (long i = 0; i < repeatCount; i++) + callback(); + return new(); + } + + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) + { + var startedClock = clock.Start(); + while (--invokeCount >= 0) { unrolledCallback(); } + return new ValueTask(startedClock.GetElapsed()); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + var startedClock = clock.Start(); + while (--invokeCount >= 0) { callback(); } + return new ValueTask(startedClock.GetElapsed()); } + + public override void Complete() { } } + [AggressivelyOptimizeMethods] internal unsafe class BenchmarkActionVoidPointer : BenchmarkActionBase { private delegate void* PointerFunc(); @@ -58,30 +71,41 @@ public BenchmarkActionVoidPointer(object? instance, MethodInfo? method, int unro { callback = CreateWorkload(instance, method); unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = () => callback(); + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask InvokeOnce() { - for (long i = 0; i < repeatCount; i++) + callback(); + return new(); + } + + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) + { + var startedClock = clock.Start(); + while (--invokeCount >= 0) { unrolledCallback(); } + return new ValueTask(startedClock.GetElapsed()); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + var startedClock = clock.Start(); + while (--invokeCount >= 0) { callback(); } + return new ValueTask(startedClock.GetElapsed()); } + + public override void Complete() { } } + [AggressivelyOptimizeMethods] internal unsafe class BenchmarkActionByRef : BenchmarkActionBase #if NET9_0_OR_GREATER where T : allows ref struct @@ -96,30 +120,41 @@ public BenchmarkActionByRef(object? instance, MethodInfo? method, int unrollFact { callback = CreateWorkload(instance, method); unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = () => callback(); + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask InvokeOnce() + { + callback(); + return new(); + } + + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + var startedClock = clock.Start(); + while (--invokeCount >= 0) { unrolledCallback(); } + return new ValueTask(startedClock.GetElapsed()); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + var startedClock = clock.Start(); + while (--invokeCount >= 0) { callback(); } + return new ValueTask(startedClock.GetElapsed()); } + + public override void Complete() { } } + [AggressivelyOptimizeMethods] internal unsafe class BenchmarkActionByRefReadonly : BenchmarkActionBase #if NET9_0_OR_GREATER where T : allows ref struct @@ -134,30 +169,41 @@ public BenchmarkActionByRefReadonly(object? instance, MethodInfo? method, int un { callback = CreateWorkload(instance, method); unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = () => callback(); + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask InvokeOnce() + { + callback(); + return new(); + } + + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + var startedClock = clock.Start(); + while (--invokeCount >= 0) { unrolledCallback(); } + return new ValueTask(startedClock.GetElapsed()); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + var startedClock = clock.Start(); + while (--invokeCount >= 0) { callback(); } + return new ValueTask(startedClock.GetElapsed()); } + + public override void Complete() { } } + [AggressivelyOptimizeMethods] internal class BenchmarkAction : BenchmarkActionBase #if NET9_0_OR_GREATER where T : allows ref struct @@ -170,208 +216,324 @@ public BenchmarkAction(object? instance, MethodInfo? method, int unrollFactor) { callback = CreateWorkload>(instance, method); unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = () => callback(); + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask InvokeOnce() { - for (long i = 0; i < repeatCount; i++) + callback(); + return new(); + } + + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) + { + var startedClock = clock.Start(); + while (--invokeCount >= 0) { unrolledCallback(); } + return new ValueTask(startedClock.GetElapsed()); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + var startedClock = clock.Start(); + while (--invokeCount >= 0) { callback(); } + return new ValueTask(startedClock.GetElapsed()); } + + public override void Complete() { } } + [AggressivelyOptimizeMethods] internal class BenchmarkActionTask : BenchmarkActionBase { - private readonly Func startTaskCallback; - private readonly Action callback; - private readonly Action unrolledCallback; + private readonly Func callback; + private readonly int unrollFactor; + private WorkloadContinuerAndValueTaskSource? workloadContinuerAndValueTaskSource; + private IClock clock; + private long invokeCount; - public BenchmarkActionTask(object? instance, MethodInfo? method, int unrollFactor) + public BenchmarkActionTask(object? instance, MethodInfo method, int unrollFactor) { - if (method == null) - { - callback = CreateWorkloadOrOverhead(instance, method); - } - else - { - startTaskCallback = CreateWorkload>(instance, method); - callback = ExecuteBlocking; - } - unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = callback; + callback = CreateWorkload>(instance, method); + this.unrollFactor = unrollFactor; + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - // must be kept in sync with TaskDeclarationsProvider.TargetMethodDelegate - private void ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke()); + private async ValueTask InvokeOnce() + => await callback(); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) + => WorkloadActionNoUnroll(invokeCount * unrollFactor, clock); + + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + this.invokeCount = invokeCount; + this.clock = clock; + if (workloadContinuerAndValueTaskSource == null) { - unrolledCallback(); + workloadContinuerAndValueTaskSource = new(); + StartWorkload(); } + return workloadContinuerAndValueTaskSource.Continue(); + } + + private async void StartWorkload() + { + await WorkloadCore(); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private async Task WorkloadCore() { - for (long i = 0; i < repeatCount; i++) + try { - callback(); + while (true) + { + await workloadContinuerAndValueTaskSource; + if (workloadContinuerAndValueTaskSource.IsCompleted) + { + return; + } + + var startedClock = clock.Start(); + while (--invokeCount >= 0) + { + await callback(); + } + workloadContinuerAndValueTaskSource.SetResult(startedClock.GetElapsed()); + } + } + catch (Exception e) + { + workloadContinuerAndValueTaskSource.SetException(e); } } + + public override void Complete() + => workloadContinuerAndValueTaskSource?.Complete(); } + [AggressivelyOptimizeMethods] internal class BenchmarkActionTask : BenchmarkActionBase { - private readonly Func> startTaskCallback; - private readonly Action callback; - private readonly Action unrolledCallback; + private readonly Func> callback; + private readonly int unrollFactor; + private WorkloadContinuerAndValueTaskSource? workloadContinuerAndValueTaskSource; + private IClock clock; + private long invokeCount; public BenchmarkActionTask(object? instance, MethodInfo? method, int unrollFactor) { - if (method == null) - { - callback = CreateWorkloadOrOverhead(instance, method); - } - else - { - startTaskCallback = CreateWorkload>>(instance, method); - callback = ExecuteBlocking; - } - unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = callback; + callback = CreateWorkload>>(instance, method); + this.unrollFactor = unrollFactor; + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - // must be kept in sync with TaskDeclarationsProvider.TargetMethodDelegate - private void ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke()); + private async ValueTask InvokeOnce() + => await callback(); + + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) + => WorkloadActionNoUnroll(invokeCount * unrollFactor, clock); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + this.invokeCount = invokeCount; + this.clock = clock; + if (workloadContinuerAndValueTaskSource == null) { - unrolledCallback(); + workloadContinuerAndValueTaskSource = new(); + StartWorkload(); } + return workloadContinuerAndValueTaskSource.Continue(); + } + + private async void StartWorkload() + { + await WorkloadCore(); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private async Task WorkloadCore() { - for (long i = 0; i < repeatCount; i++) + try { - callback(); + while (true) + { + await workloadContinuerAndValueTaskSource; + if (workloadContinuerAndValueTaskSource.IsCompleted) + { + return default; + } + + var startedClock = clock.Start(); + while (--invokeCount >= 0) + { + await callback(); + } + workloadContinuerAndValueTaskSource.SetResult(startedClock.GetElapsed()); + } + } + catch (Exception e) + { + workloadContinuerAndValueTaskSource.SetException(e); + return default; } } + + public override void Complete() + => workloadContinuerAndValueTaskSource?.Complete(); } + [AggressivelyOptimizeMethods] internal class BenchmarkActionValueTask : BenchmarkActionBase { - private readonly Func startTaskCallback; - private readonly Action callback; - private readonly Action unrolledCallback; + private readonly Func callback; + private readonly int unrollFactor; + private WorkloadContinuerAndValueTaskSource? workloadContinuerAndValueTaskSource; + private IClock clock; + private long invokeCount; public BenchmarkActionValueTask(object? instance, MethodInfo? method, int unrollFactor) { - if (method == null) - { - callback = CreateWorkloadOrOverhead(instance, method); - } - else - { - startTaskCallback = CreateWorkload>(instance, method); - callback = ExecuteBlocking; - } - unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = callback; + callback = CreateWorkload>(instance, method); + this.unrollFactor = unrollFactor; + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - // must be kept in sync with TaskDeclarationsProvider.TargetMethodDelegate - private void ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke()); + private async ValueTask InvokeOnce() + => await callback(); + + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) + => WorkloadActionNoUnroll(invokeCount * unrollFactor, clock); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + this.invokeCount = invokeCount; + this.clock = clock; + if (workloadContinuerAndValueTaskSource == null) { - unrolledCallback(); + workloadContinuerAndValueTaskSource = new(); + StartWorkload(); } + return workloadContinuerAndValueTaskSource.Continue(); + } + + private async void StartWorkload() + { + await WorkloadCore(); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private async ValueTask WorkloadCore() { - for (long i = 0; i < repeatCount; i++) + try { - callback(); + while (true) + { + await workloadContinuerAndValueTaskSource; + if (workloadContinuerAndValueTaskSource.IsCompleted) + { + return; + } + + var startedClock = clock.Start(); + while (--invokeCount >= 0) + { + await callback(); + } + workloadContinuerAndValueTaskSource.SetResult(startedClock.GetElapsed()); + } + } + catch (Exception e) + { + workloadContinuerAndValueTaskSource.SetException(e); } } + + public override void Complete() + => workloadContinuerAndValueTaskSource?.Complete(); } + [AggressivelyOptimizeMethods] internal class BenchmarkActionValueTask : BenchmarkActionBase { - private readonly Func> startTaskCallback; - private readonly Action callback; - private readonly Action unrolledCallback; + private readonly Func> callback; + private readonly int unrollFactor; + private WorkloadContinuerAndValueTaskSource? workloadContinuerAndValueTaskSource; + private IClock clock; + private long invokeCount; public BenchmarkActionValueTask(object? instance, MethodInfo? method, int unrollFactor) { - if (method == null) - { - callback = CreateWorkloadOrOverhead(instance, method); - } - else - { - startTaskCallback = CreateWorkload>>(instance, method); - callback = ExecuteBlocking; - } - unrolledCallback = Unroll(callback, unrollFactor); - InvokeSingle = callback; + callback = CreateWorkload>>(instance, method); + this.unrollFactor = unrollFactor; + InvokeSingle = InvokeOnce; InvokeUnroll = WorkloadActionUnroll; InvokeNoUnroll = WorkloadActionNoUnroll; } - // must be kept in sync with TaskDeclarationsProvider.TargetMethodDelegate - private void ExecuteBlocking() => Helpers.AwaitHelper.GetResult(startTaskCallback.Invoke()); + private async ValueTask InvokeOnce() + => await callback(); + + private ValueTask WorkloadActionUnroll(long invokeCount, IClock clock) + => WorkloadActionNoUnroll(invokeCount * unrollFactor, clock); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionUnroll(long repeatCount) + private ValueTask WorkloadActionNoUnroll(long invokeCount, IClock clock) { - for (long i = 0; i < repeatCount; i++) + this.invokeCount = invokeCount; + this.clock = clock; + if (workloadContinuerAndValueTaskSource == null) { - unrolledCallback(); + workloadContinuerAndValueTaskSource = new(); + StartWorkload(); } + return workloadContinuerAndValueTaskSource.Continue(); + } + + private async void StartWorkload() + { + await WorkloadCore(); } - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - private void WorkloadActionNoUnroll(long repeatCount) + private async ValueTask WorkloadCore() { - for (long i = 0; i < repeatCount; i++) + try { - callback(); + while (true) + { + await workloadContinuerAndValueTaskSource; + if (workloadContinuerAndValueTaskSource.IsCompleted) + { + return default; + } + + var startedClock = clock.Start(); + while (--invokeCount >= 0) + { + await callback(); + } + workloadContinuerAndValueTaskSource.SetResult(startedClock.GetElapsed()); + } + } + catch (Exception e) + { + workloadContinuerAndValueTaskSource.SetException(e); + return default; } } + + public override void Complete() + => workloadContinuerAndValueTaskSource?.Complete(); } } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs index 10eaa4287b..463ce13ff2 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Extensions; @@ -15,14 +16,15 @@ namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit { internal class InProcessNoEmitExecutor(bool executeOnSeparateThread) : IExecutor { - public ExecuteResult Execute(ExecuteParameters executeParameters) + public async ValueTask ExecuteAsync(ExecuteParameters executeParameters) { var host = new InProcessHost(executeParameters.BenchmarkCase, executeParameters.Logger, executeParameters.Diagnoser); int exitCode = -1; if (executeOnSeparateThread) { - var runThread = new Thread(() => exitCode = ExecuteCore(host, executeParameters)); + var taskCompletionSource = new TaskCompletionSource(); + var runThread = new Thread(async () => taskCompletionSource.SetResult(await ExecuteCore(host, executeParameters))); if (executeParameters.BenchmarkCase.Descriptor.WorkloadMethod.GetCustomAttributes(false).Any() && OsDetector.IsWindows()) @@ -34,10 +36,13 @@ public ExecuteResult Execute(ExecuteParameters executeParameters) runThread.Start(); runThread.Join(); + + // We must await the task after joining the thread or else it can deadlock. + exitCode = await taskCompletionSource.Task; } else { - exitCode = ExecuteCore(host, executeParameters); + exitCode = await ExecuteCore(host, executeParameters); } host.HandleInProcessDiagnoserResults(executeParameters.BenchmarkCase, executeParameters.CompositeInProcessDiagnoser); @@ -45,7 +50,7 @@ public ExecuteResult Execute(ExecuteParameters executeParameters) return ExecuteResult.FromRunResults(host.RunResults, exitCode); } - private int ExecuteCore(IHost host, ExecuteParameters parameters) + private async ValueTask ExecuteCore(IHost host, ExecuteParameters parameters) { int exitCode = -1; var process = Process.GetCurrentProcess(); @@ -65,7 +70,7 @@ private int ExecuteCore(IHost host, ExecuteParameters parameters) process.TrySetAffinity(affinity.Value, parameters.Logger); } - exitCode = InProcessNoEmitRunner.Run(host, parameters); + exitCode = await InProcessNoEmitRunner.Run(host, parameters); } catch (Exception ex) { diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs index 8988d62e8e..e92f7fee57 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Environments; using BenchmarkDotNet.Exporters; @@ -19,7 +20,7 @@ namespace BenchmarkDotNet.Toolchains.InProcess.NoEmit internal class InProcessNoEmitRunner { [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Runnable))] - public static int Run(IHost host, ExecuteParameters parameters) + public static async ValueTask Run(IHost host, ExecuteParameters parameters) { // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! @@ -37,7 +38,7 @@ public static int Run(IHost host, ExecuteParameters parameters) var methodInfo = type.GetMethod(nameof(Runnable.RunCore), BindingFlags.Public | BindingFlags.Static) ?? throw new InvalidOperationException($"Bug: method {nameof(Runnable.RunCore)} in {inProcessRunnableTypeName} not found."); - methodInfo.Invoke(null, [host, parameters]); + await (ValueTask) methodInfo.Invoke(null, [host, parameters]); return 0; } @@ -104,11 +105,11 @@ internal static void FillMembers(object instance, BenchmarkCase benchmarkCase) [UsedImplicitly] private static class Runnable { - public static void RunCore(IHost host, ExecuteParameters parameters) + public static async ValueTask RunCore(IHost host, ExecuteParameters parameters) { var benchmarkCase = parameters.BenchmarkCase; var target = benchmarkCase.Descriptor; - var job = benchmarkCase.Job; // TODO: filter job (same as SourceCodePresenter does)? + var job = new Job().Apply(benchmarkCase.Job).Freeze(); int unrollFactor = benchmarkCase.Job.ResolveValue(RunMode.UnrollFactorCharacteristic, EnvironmentResolver.Instance); // DONTTOUCH: these should be allocated together @@ -156,7 +157,12 @@ public static void RunCore(IHost host, ExecuteParameters parameters) OverheadActionNoUnroll = overheadAction.InvokeNoUnroll, OverheadActionUnroll = overheadAction.InvokeUnroll, GlobalSetupAction = globalSetupAction.InvokeSingle, - GlobalCleanupAction = globalCleanupAction.InvokeSingle, + GlobalCleanupAction = () => + { + workloadAction.Complete(); + overheadAction.Complete(); + return globalCleanupAction.InvokeSingle(); + }, IterationSetupAction = iterationSetupAction.InvokeSingle, IterationCleanupAction = iterationCleanupAction.InvokeSingle, TargetJob = job, @@ -166,10 +172,10 @@ public static void RunCore(IHost host, ExecuteParameters parameters) InProcessDiagnoserHandler = compositeInProcessDiagnoserHandler }; - var results = job + var results = await job .ResolveValue(InfrastructureMode.EngineFactoryCharacteristic, InfrastructureResolver.Instance) .Create(engineParameters) - .Run(); + .RunAsync(); host.ReportResults(results); // printing costs memory, do this after runs compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterEngine); diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitToolchain.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitToolchain.cs index 94fd847f02..fac2d39f50 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitToolchain.cs @@ -1,6 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Characteristics; +using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Running; using BenchmarkDotNet.Validators; using JetBrains.Annotations; @@ -25,8 +26,21 @@ public InProcessNoEmitToolchain(InProcessNoEmitSettings settings) Executor = new InProcessNoEmitExecutor(settings.ExecuteOnSeparateThread); } - public IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) => - InProcessValidator.Validate(benchmarkCase); + public IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + { + foreach (var error in InProcessValidator.Validate(benchmarkCase)) + { + yield return error; + } + + if (benchmarkCase.Descriptor.WorkloadMethod.ReturnType.HasAttribute() == true) + { + yield return new ValidationError(false, + $"{nameof(InProcessNoEmitToolchain)} does not support overriding the async caller type via [AsyncCallerType]. It will be ignored.", + benchmarkCase); + } + + } /// Name of the toolchain. /// The name of the toolchain. diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs index f58eb786a5..aba98bb2c6 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs @@ -15,23 +15,22 @@ using System.Diagnostics; using System.IO; using System.Text; +using System.Threading.Tasks; namespace BenchmarkDotNet.Toolchains.MonoWasm { internal class WasmExecutor : IExecutor { - public ExecuteResult Execute(ExecuteParameters executeParameters) + public ValueTask ExecuteAsync(ExecuteParameters executeParameters) { string exePath = executeParameters.BuildResult.ArtifactsPaths.ExecutablePath; - if (!File.Exists(exePath)) - { - return ExecuteResult.CreateFailed(); - } - - return Execute(executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, executeParameters.BuildResult.ArtifactsPaths, + var executeResult = !File.Exists(exePath) + ? ExecuteResult.CreateFailed() + : Execute(executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, executeParameters.BuildResult.ArtifactsPaths, executeParameters.Diagnoser, executeParameters.CompositeInProcessDiagnoser, executeParameters.Resolver, executeParameters.LaunchIndex, executeParameters.DiagnoserRunMode); + return new ValueTask(executeResult); } private static ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, ArtifactsPaths artifactsPaths, diff --git a/src/BenchmarkDotNet/Toolchains/Roslyn/Generator.cs b/src/BenchmarkDotNet/Toolchains/Roslyn/Generator.cs index 3ba7054f7a..93e96febbb 100644 --- a/src/BenchmarkDotNet/Toolchains/Roslyn/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/Roslyn/Generator.cs @@ -4,7 +4,6 @@ using System.Reflection; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Extensions; -using BenchmarkDotNet.Portability; using BenchmarkDotNet.Running; using JetBrains.Annotations; @@ -17,14 +16,13 @@ protected override string GetBuildArtifactsDirectoryPath(BuildPartition buildPar => Path.GetDirectoryName(buildPartition.AssemblyLocation); [PublicAPI] - protected override string[] GetArtifactsToCleanup(ArtifactsPaths artifactsPaths) - => new[] - { - artifactsPaths.ProgramCodePath, - artifactsPaths.AppConfigPath, - artifactsPaths.BuildScriptFilePath, - artifactsPaths.ExecutablePath - }; + protected override string[] GetArtifactsToCleanup(ArtifactsPaths artifactsPaths) => + [ + artifactsPaths.ProgramCodePath, + artifactsPaths.AppConfigPath, + artifactsPaths.BuildScriptFilePath, + artifactsPaths.ExecutablePath + ]; protected override void GenerateBuildScript(BuildPartition buildPartition, ArtifactsPaths artifactsPaths) { @@ -54,11 +52,12 @@ internal static IEnumerable GetAllReferences(BenchmarkCase benchmarkCa .GetReferencedAssemblies() .Select(Assembly.Load) .Concat( - new[] - { - benchmarkCase.Descriptor.Type.GetTypeInfo().Assembly, // this assembly does not has to have a reference to BenchmarkDotNet (e.g. custom framework for benchmarking that internally uses BenchmarkDotNet - typeof(BenchmarkCase).Assembly // BenchmarkDotNet - }) + [ + benchmarkCase.Descriptor.Type.GetTypeInfo().Assembly, // this assembly does not has to have a reference to BenchmarkDotNet (e.g. custom framework for benchmarking that internally uses BenchmarkDotNet + typeof(BenchmarkCase).Assembly, // BenchmarkDotNet + typeof(System.Threading.Tasks.ValueTask).Assembly, // TaskExtensions + typeof(Perfolizer.Horology.IClock).Assembly // Perfolizer + ]) .Distinct(); } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/AllSetupAndCleanupTest.cs b/tests/BenchmarkDotNet.IntegrationTests/AllSetupAndCleanupTest.cs index 36ea5688ad..73db064aea 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/AllSetupAndCleanupTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/AllSetupAndCleanupTest.cs @@ -96,10 +96,10 @@ public class AllSetupAndCleanupAttributeBenchmarksTask private int cleanupCounter; [IterationSetup] - public void IterationSetup() => Console.WriteLine(IterationSetupCalled + " (" + ++setupCounter + ")"); + public Task IterationSetup() => Console.Out.WriteLineAsync(IterationSetupCalled + " (" + ++setupCounter + ")"); [IterationCleanup] - public void IterationCleanup() => Console.WriteLine(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); + public Task IterationCleanup() => Console.Out.WriteLineAsync(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); [GlobalSetup] public Task GlobalSetup() => Console.Out.WriteLineAsync(GlobalSetupCalled); @@ -117,10 +117,20 @@ public class AllSetupAndCleanupAttributeBenchmarksGenericTask private int cleanupCounter; [IterationSetup] - public void IterationSetup() => Console.WriteLine(IterationSetupCalled + " (" + ++setupCounter + ")"); + public async Task IterationSetup() + { + await Console.Out.WriteLineAsync(IterationSetupCalled + " (" + ++setupCounter + ")"); + + return 42; + } [IterationCleanup] - public void IterationCleanup() => Console.WriteLine(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); + public async Task IterationCleanup() + { + await Console.Out.WriteLineAsync(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); + + return 42; + } [GlobalSetup] public async Task GlobalSetup() @@ -148,10 +158,10 @@ public class AllSetupAndCleanupAttributeBenchmarksValueTask private int cleanupCounter; [IterationSetup] - public void IterationSetup() => Console.WriteLine(IterationSetupCalled + " (" + ++setupCounter + ")"); + public ValueTask IterationSetup() => new ValueTask(Console.Out.WriteLineAsync(IterationSetupCalled + " (" + ++setupCounter + ")")); [IterationCleanup] - public void IterationCleanup() => Console.WriteLine(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); + public ValueTask IterationCleanup() => new ValueTask(Console.Out.WriteLineAsync(IterationCleanupCalled + " (" + ++cleanupCounter + ")")); [GlobalSetup] public ValueTask GlobalSetup() => new ValueTask(Console.Out.WriteLineAsync(GlobalSetupCalled)); @@ -169,10 +179,20 @@ public class AllSetupAndCleanupAttributeBenchmarksGenericValueTask private int cleanupCounter; [IterationSetup] - public void IterationSetup() => Console.WriteLine(IterationSetupCalled + " (" + ++setupCounter + ")"); + public async ValueTask IterationSetup() + { + await Console.Out.WriteLineAsync(IterationSetupCalled + " (" + ++setupCounter + ")"); + + return 42; + } [IterationCleanup] - public void IterationCleanup() => Console.WriteLine(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); + public async ValueTask IterationCleanup() + { + await Console.Out.WriteLineAsync(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); + + return 42; + } [GlobalSetup] public async ValueTask GlobalSetup() @@ -201,10 +221,20 @@ public class AllSetupAndCleanupAttributeBenchmarksValueTaskSource private int cleanupCounter; [IterationSetup] - public void IterationSetup() => Console.WriteLine(IterationSetupCalled + " (" + ++setupCounter + ")"); + public ValueTask IterationSetup() + { + valueTaskSource.Reset(); + Console.Out.WriteLineAsync(IterationSetupCalled + " (" + ++setupCounter + ")").ContinueWith(_ => valueTaskSource.SetResult(42)); + return new ValueTask(valueTaskSource, valueTaskSource.Token); + } [IterationCleanup] - public void IterationCleanup() => Console.WriteLine(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); + public ValueTask IterationCleanup() + { + valueTaskSource.Reset(); + Console.Out.WriteLineAsync(IterationCleanupCalled + " (" + ++cleanupCounter + ")").ContinueWith(_ => valueTaskSource.SetResult(42)); + return new ValueTask(valueTaskSource, valueTaskSource.Token); + } [GlobalSetup] public ValueTask GlobalSetup() @@ -233,10 +263,20 @@ public class AllSetupAndCleanupAttributeBenchmarksGenericValueTaskSource private int cleanupCounter; [IterationSetup] - public void IterationSetup() => Console.WriteLine(IterationSetupCalled + " (" + ++setupCounter + ")"); + public ValueTask IterationSetup() + { + valueTaskSource.Reset(); + Console.Out.WriteLineAsync(IterationSetupCalled + " (" + ++setupCounter + ")").ContinueWith(_ => valueTaskSource.SetResult(42)); + return new ValueTask(valueTaskSource, valueTaskSource.Token); + } [IterationCleanup] - public void IterationCleanup() => Console.WriteLine(IterationCleanupCalled + " (" + ++cleanupCounter + ")"); + public ValueTask IterationCleanup() + { + valueTaskSource.Reset(); + Console.Out.WriteLineAsync(IterationCleanupCalled + " (" + ++cleanupCounter + ")").ContinueWith(_ => valueTaskSource.SetResult(42)); + return new ValueTask(valueTaskSource, valueTaskSource.Token); + } [GlobalSetup] public ValueTask GlobalSetup() diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkTestExecutor.cs b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkTestExecutor.cs index e835065b27..57bd930114 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkTestExecutor.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkTestExecutor.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using BenchmarkDotNet.IntegrationTests.Xunit; using BenchmarkDotNet.Reports; +using System.Threading.Tasks; namespace BenchmarkDotNet.IntegrationTests { @@ -36,10 +37,8 @@ protected BenchmarkTestExecutor(ITestOutputHelper output) /// Optional custom config to be used instead of the default /// Optional: disable validation (default = true/enabled) /// The summary from the benchmark run - public Reports.Summary CanExecute(IConfig? config = null, bool fullValidation = true) - { - return CanExecute(typeof(TBenchmark), config, fullValidation); - } + public Summary CanExecute(IConfig? config = null, bool fullValidation = true) + => CanExecute(typeof(TBenchmark), config, fullValidation); /// /// Runs Benchmarks with the most simple config (SingleRunFastConfig) @@ -50,7 +49,36 @@ public Reports.Summary CanExecute(IConfig? config = null, bool fullV /// Optional custom config to be used instead of the default /// Optional: disable validation (default = true/enabled) /// The summary from the benchmark run - public Reports.Summary CanExecute(Type type, IConfig? config = null, bool fullValidation = true) + public Summary CanExecute(Type type, IConfig? config = null, bool fullValidation = true) + { + // Make sure we ALWAYS combine the Config (default or passed in) with any Config applied to the Type/Class + var summary = BenchmarkRunner.Run(type, GetMinimalConfig(config)); + + if (fullValidation) + { + ValidateSummary(summary); + } + + return summary; + } + + public ValueTask CanExecuteAsync(IConfig? config = null, bool fullValidation = true) + => CanExecuteAsync(typeof(TBenchmark), config, fullValidation); + + public async ValueTask CanExecuteAsync(Type type, IConfig? config = null, bool fullValidation = true) + { + // Make sure we ALWAYS combine the Config (default or passed in) with any Config applied to the Type/Class + var summary = await BenchmarkRunner.RunAsync(type, GetMinimalConfig(config)); + + if (fullValidation) + { + ValidateSummary(summary); + } + + return summary; + } + + private IConfig GetMinimalConfig(IConfig? config) { // Add logging, so the Benchmark execution is in the TestRunner output (makes Debugging easier) if (config == null) @@ -64,30 +92,27 @@ public Reports.Summary CanExecute(Type type, IConfig? config = null, bool fullVa if (!config.GetColumnProviders().Any()) config = config.AddColumnProvider(DefaultColumnProviders.Instance); - // Make sure we ALWAYS combine the Config (default or passed in) with any Config applied to the Type/Class - var summary = BenchmarkRunner.Run(type, config); - - if (fullValidation) - { - Assert.False(summary.HasCriticalValidationErrors, "The \"Summary\" should have NOT \"HasCriticalValidationErrors\""); + return config!; + } - Assert.True(summary.Reports.Any(), "The \"Summary\" should contain at least one \"BenchmarkReport\" in the \"Reports\" collection"); + private static void ValidateSummary(Summary summary) + { + Assert.False(summary.HasCriticalValidationErrors, "The \"Summary\" should have NOT \"HasCriticalValidationErrors\""); - summary.CheckPlatformLinkerIssues(); + Assert.True(summary.Reports.Any(), "The \"Summary\" should contain at least one \"BenchmarkReport\" in the \"Reports\" collection"); - Assert.True(summary.Reports.All(r => r.BuildResult.IsBuildSuccess), - "The following benchmarks have failed to build: " + - string.Join(", ", summary.Reports.Where(r => !r.BuildResult.IsBuildSuccess).Select(r => r.BenchmarkCase.DisplayInfo))); + summary.CheckPlatformLinkerIssues(); - Assert.True(summary.Reports.All(r => r.ExecuteResults != null), - "The following benchmarks don't have any execution results: " + - string.Join(", ", summary.Reports.Where(r => r.ExecuteResults == null).Select(r => r.BenchmarkCase.DisplayInfo))); + Assert.True(summary.Reports.All(r => r.BuildResult.IsBuildSuccess), + "The following benchmarks have failed to build: " + + string.Join(", ", summary.Reports.Where(r => !r.BuildResult.IsBuildSuccess).Select(r => r.BenchmarkCase.DisplayInfo))); - Assert.True(summary.Reports.All(r => r.ExecuteResults.All(er => er.IsSuccess)), - "All reports should have succeeded to execute"); - } + Assert.True(summary.Reports.All(r => r.ExecuteResults != null), + "The following benchmarks don't have any execution results: " + + string.Join(", ", summary.Reports.Where(r => r.ExecuteResults == null).Select(r => r.BenchmarkCase.DisplayInfo))); - return summary; + Assert.True(summary.Reports.All(r => r.ExecuteResults.All(er => er.IsSuccess)), + "All reports should have succeeded to execute"); } protected IConfig CreateSimpleConfig(OutputLogger? logger = null, Job? job = null) diff --git a/tests/BenchmarkDotNet.IntegrationTests/CodeGenTests.cs b/tests/BenchmarkDotNet.IntegrationTests/CodeGenTests.cs new file mode 100644 index 0000000000..d8b1fdff1c --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/CodeGenTests.cs @@ -0,0 +1,148 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Tests.XUnit; +using BenchmarkDotNet.Toolchains; +using BenchmarkDotNet.Toolchains.InProcess.Emit; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.IntegrationTests; + +public class CodeGenTests(ITestOutputHelper output) : BenchmarkTestExecutor(output) +{ + public static IEnumerable GetToolchains() => + [ + [Job.Default.GetToolchain()], + [InProcessEmitToolchain.Default] + ]; + + [TheoryEnvSpecific(".Net Framework JIT is not tiered", EnvRequirement.DotNetCoreOnly)] + [MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)] + public void GeneratedBenchmarkTypeMethodsAreAggressivelyOptimized(IToolchain toolchain) + { + var config = CreateSimpleConfig(job: Job.Dry.WithToolchain(toolchain)); + CanExecute(config); + } + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public class BenchmarkManyTypes + { + private static void AssertAggressiveOptimization() + { + var runnableType = GetRunnableType(); + AssertMethodsAggressivelyOptimized(runnableType); + + static Type GetRunnableType() + { + var stacktrace = new StackTrace(false); + for (int i = 0; i < stacktrace.FrameCount; i++) + { + var benchmarkType = stacktrace.GetFrame(i).GetMethod().DeclaringType; + do + { + if (benchmarkType.Name.StartsWith("Runnable_")) + { + return benchmarkType; + } + benchmarkType = benchmarkType.DeclaringType; + } + while (benchmarkType != null); + } + Assert.Fail("Runnable type not found"); + return null; // unreachable + } + + static void AssertMethodsAggressivelyOptimized(Type type) + { + foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + if (method.MethodImplementationFlags.HasFlag(MethodImplAttributes.NoOptimization)) + { + Assert.False( + method.MethodImplementationFlags.HasFlag(Portability.CodeGenHelper.AggressiveOptimizationOptionForEmit), + $"Method is aggressively optimized: {method}" + ); + } + else + { + Assert.True( + method.MethodImplementationFlags.HasFlag(Portability.CodeGenHelper.AggressiveOptimizationOptionForEmit), + $"Method is not aggressively optimized: {method}" + ); + } + } + + foreach (var nestedType in type.GetNestedTypes()) + { + AssertMethodsAggressivelyOptimized(nestedType); + } + } + } + + [Benchmark] + public void ReturnVoid() => AssertAggressiveOptimization(); + + [Benchmark] + public async Task ReturnTaskAsync() => AssertAggressiveOptimization(); + + [Benchmark] + public async ValueTask ReturnValueTaskAsync() => AssertAggressiveOptimization(); + + [Benchmark] + public string ReturnRefType() + { + AssertAggressiveOptimization(); + return default; + } + + [Benchmark] + public decimal ReturnValueType() + { + AssertAggressiveOptimization(); + return default; + } + + [Benchmark] + public async Task ReturnTaskOfTAsync() + { + AssertAggressiveOptimization(); + return default; + } + + [Benchmark] + public ValueTask ReturnValueTaskOfT() + { + AssertAggressiveOptimization(); + return default; + } + + private int intField; + + [Benchmark] + public ref int ReturnByRefType() + { + AssertAggressiveOptimization(); + return ref intField; + } + + [Benchmark] + public ref readonly int ReturnByRefReadonlyType() + { + AssertAggressiveOptimization(); + return ref intField; + } + + [Benchmark] + public unsafe void* ReturnVoidPointerType() + { + AssertAggressiveOptimization(); + return default; + } + } +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/CustomAwaitable.cs b/tests/BenchmarkDotNet.IntegrationTests/CustomAwaitable.cs new file mode 100644 index 0000000000..b7902e100a --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/CustomAwaitable.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.CompilerServices; + +namespace BenchmarkDotNet.IntegrationTests; + +public sealed class CustomAwaitable : ICriticalNotifyCompletion +{ + public CustomAwaitable GetAwaiter() => this; + + public void OnCompleted(Action continuation) => continuation(); + + public void UnsafeOnCompleted(Action continuation) => continuation(); + + public bool IsCompleted => true; + + public void GetResult() { } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/CustomEngineTests.cs b/tests/BenchmarkDotNet.IntegrationTests/CustomEngineTests.cs index e5afe4e0df..161ee7932d 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/CustomEngineTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/CustomEngineTests.cs @@ -6,8 +6,8 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Reports; using Perfolizer.Mathematics.OutlierDetection; +using System.Threading.Tasks; namespace BenchmarkDotNet.IntegrationTests { @@ -56,9 +56,9 @@ public IEngine Create(EngineParameters engineParameters) public class CustomEngine(EngineParameters engineParameters) : IEngine { - public RunResults Run() + public async ValueTask RunAsync() { - engineParameters.GlobalSetupAction.Invoke(); + await engineParameters.GlobalSetupAction.Invoke(); Console.WriteLine(EngineRunMessage); try { @@ -73,7 +73,7 @@ public RunResults Run() } finally { - engineParameters.GlobalCleanupAction.Invoke(); + await engineParameters.GlobalCleanupAction.Invoke(); } } } diff --git a/tests/BenchmarkDotNet.IntegrationTests/CustomTask.cs b/tests/BenchmarkDotNet.IntegrationTests/CustomTask.cs new file mode 100644 index 0000000000..d496bfb928 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/CustomTask.cs @@ -0,0 +1,109 @@ +using System; +using System.Runtime.CompilerServices; +using System.Security; +using System.Threading.Tasks; + +namespace BenchmarkDotNet.IntegrationTests; + +[AsyncMethodBuilder(typeof(AsyncCustomTaskMethodBuilder))] +public readonly struct CustomTask(ValueTask valueTask) +{ + public CustomTaskAwaiter GetAwaiter() => new(valueTask.GetAwaiter()); +} + +public readonly struct CustomTaskAwaiter(ValueTaskAwaiter valueTaskAwaiter) : ICriticalNotifyCompletion +{ + public bool IsCompleted => valueTaskAwaiter.IsCompleted; + + public void GetResult() => valueTaskAwaiter.GetResult(); + + public void OnCompleted(Action continuation) => valueTaskAwaiter.OnCompleted(continuation); + + public void UnsafeOnCompleted(Action continuation) => valueTaskAwaiter.UnsafeOnCompleted(continuation); +} + +public struct AsyncCustomTaskMethodBuilder +{ + public static int InUseCounter { get; private set; } + + private AsyncValueTaskMethodBuilder _methodBuilder; + + public static AsyncCustomTaskMethodBuilder Create() + { + ++InUseCounter; + return default; + } + + public CustomTask Task => new(_methodBuilder.Task); + + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + => _methodBuilder.Start(ref stateMachine); + + public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine); + + public void SetResult() + { + --InUseCounter; + _methodBuilder.SetResult(); + } + + public void SetException(Exception exception) + { + --InUseCounter; + _methodBuilder.SetException(exception); + } + + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + => _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine); + + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + => _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); +} + +public struct AsyncWrapperTaskMethodBuilder +{ + public static int InUseCounter { get; private set; } + + private AsyncTaskMethodBuilder _methodBuilder; + + public static AsyncWrapperTaskMethodBuilder Create() + { + ++InUseCounter; + return default; + } + + public Task Task => _methodBuilder.Task; + + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + => _methodBuilder.Start(ref stateMachine); + + public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine); + + public void SetResult() + { + --InUseCounter; + _methodBuilder.SetResult(); + } + + public void SetException(Exception exception) + { + --InUseCounter; + _methodBuilder.SetException(exception); + } + + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + => _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine); + + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + => _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/CustomTaskAndAwaitableTests.cs b/tests/BenchmarkDotNet.IntegrationTests/CustomTaskAndAwaitableTests.cs new file mode 100644 index 0000000000..fe2045a1f7 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/CustomTaskAndAwaitableTests.cs @@ -0,0 +1,233 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Toolchains; +using BenchmarkDotNet.Toolchains.InProcess.Emit; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.IntegrationTests; + +public class CustomTaskAndAwaitableTests(ITestOutputHelper output) : BenchmarkTestExecutor(output) +{ + public static TheoryData GetToolchains() => new( + [ + Job.Default.GetToolchain(), + InProcessEmitToolchain.Default + ]); + + public class BenchmarkCustomAwaitableSimple + { + [GlobalSetup] + public CustomAwaitable GlobalSetup() => new(); + + [GlobalCleanup] + public CustomAwaitable GlobalCleanup() => new(); + + [IterationSetup] + public CustomAwaitable IterationSetup() => new(); + + [IterationCleanup] + public CustomAwaitable IterationCleanup() => new(); + + [Benchmark] + public CustomAwaitable Benchmark() => new(); + } + + [Theory] + [MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)] + public void ReturnCustomAwaitableType(IToolchain toolchain) + { + var config = CreateSimpleConfig(job: Job.Dry.WithToolchain(toolchain)); + CanExecute(config); + } + + public class BenchmarkCustomAwaitableOverrideCaller + { + [GlobalSetup] + public CustomAwaitable GlobalSetup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [GlobalCleanup] + public CustomAwaitable GlobalCleanup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [IterationSetup] + public CustomAwaitable IterationSetup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [IterationCleanup] + public CustomAwaitable IterationCleanup() + { + Assert.Equal(1, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [Benchmark] + [AsyncCallerType(typeof(CustomTask))] + public CustomAwaitable Benchmark() + { + Assert.Equal(1, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + } + + [Theory] + [MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)] + public void ReturnCustomAwaitableTypeAndOverrideCaller(IToolchain toolchain) + { + var config = CreateSimpleConfig(job: Job.Dry.WithToolchain(toolchain)); + CanExecute(config); + } + + public class BenchmarkCustomTask + { + [GlobalSetup] + public CustomTask GlobalSetup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [GlobalCleanup] + public CustomTask GlobalCleanup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [IterationSetup] + public CustomTask IterationSetup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [IterationCleanup] + public CustomTask IterationCleanup() + { + Assert.Equal(1, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [Benchmark] + public async CustomTask Benchmark() + { + Assert.Equal(2, AsyncCustomTaskMethodBuilder.InUseCounter); + await new ValueTask(); + } + } + + [Theory] + [MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)] + public void ReturnCustomTask(IToolchain toolchain) + { + var config = CreateSimpleConfig(job: Job.Dry.WithToolchain(toolchain)); + CanExecute(config); + } + + public class BenchmarkCustomTaskOverrideCaller + { + [GlobalSetup] + public CustomTask GlobalSetup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [GlobalCleanup] + public CustomTask GlobalCleanup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [IterationSetup] + public CustomTask IterationSetup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [IterationCleanup] + public CustomTask IterationCleanup() + { + Assert.Equal(0, AsyncCustomTaskMethodBuilder.InUseCounter); + return new(); + } + + [Benchmark] + [AsyncCallerType(typeof(ValueTask))] + public async CustomTask Benchmark() + { + Assert.Equal(1, AsyncCustomTaskMethodBuilder.InUseCounter); + await new ValueTask(); + } + } + + [Theory] + [MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)] + public void ReturnCustomTaskAndOverrideCaller(IToolchain toolchain) + { + var config = CreateSimpleConfig(job: Job.Dry.WithToolchain(toolchain)); + CanExecute(config); + } + + // [AsyncMethodBuilder] on methods is C# 10 feature, but it also requires the attribute be allowed on methods, which only works in .Net 5+. + // It can technically be poly-filled to work, but it's not worth the extra complexity to make it work for the generated project. +#if NET5_0_OR_GREATER + public class BenchmarkCustomBuilder + { + [GlobalSetup] + public void GlobalSetup() + { + Assert.Equal(0, AsyncWrapperTaskMethodBuilder.InUseCounter); + } + + [GlobalCleanup] + public void GlobalCleanup() + { + Assert.Equal(0, AsyncWrapperTaskMethodBuilder.InUseCounter); + } + + [IterationSetup] + public void IterationSetup() + { + Assert.Equal(0, AsyncWrapperTaskMethodBuilder.InUseCounter); + } + + [IterationCleanup] + public void IterationCleanup() + { + Assert.Equal(1, AsyncWrapperTaskMethodBuilder.InUseCounter); + } + + [Benchmark] + [AsyncMethodBuilder(typeof(AsyncWrapperTaskMethodBuilder))] + public async Task Benchmark() + { + Assert.Equal(2, AsyncWrapperTaskMethodBuilder.InUseCounter); + await new ValueTask(); + } + } + + [Theory] + [MemberData(nameof(GetToolchains), DisableDiscoveryEnumeration = true)] + public void ReturnCustomBuilder(IToolchain toolchain) + { + var config = CreateSimpleConfig(job: Job.Dry.WithToolchain(toolchain)); + CanExecute(config); + } +#endif +} diff --git a/tests/BenchmarkDotNet.IntegrationTests/InProcess.EmitTests/NaiveRunnableEmitDiff.cs b/tests/BenchmarkDotNet.IntegrationTests/InProcess.EmitTests/NaiveRunnableEmitDiff.cs index ea0ea4a6e2..810d7cbd44 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/InProcess.EmitTests/NaiveRunnableEmitDiff.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/InProcess.EmitTests/NaiveRunnableEmitDiff.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes.CompilerServices; using BenchmarkDotNet.Loggers; using Mono.Cecil; using Mono.Cecil.Cil; @@ -10,23 +12,23 @@ namespace BenchmarkDotNet.IntegrationTests.InProcess.EmitTests { public class NaiveRunnableEmitDiff { - private static readonly HashSet IgnoredTypeNames = new HashSet() - { + private static readonly HashSet IgnoredTypeNames = + [ "BenchmarkDotNet.Autogenerated.UniqueProgramName", "BenchmarkDotNet.Autogenerated.DirtyAssemblyResolveHelper", // not required to be used in the InProcess toolchains (it's already used in the host process) - "System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute", // Conditionally added in runtimes older than .Net 7. - }; + "System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute", // Poly-filled type added in old runtimes. + ]; - private static readonly HashSet IgnoredAttributeTypeNames = new HashSet() - { - "System.Runtime.CompilerServices.CompilerGeneratedAttribute" - }; + private static readonly HashSet IgnoredAttributeTypeNames = + [ + typeof(AggressivelyOptimizeMethodsAttribute).FullName + ]; - private static readonly HashSet IgnoredRunnableMethodNames = new HashSet() - { + private static readonly HashSet IgnoredRunnableMethodNames = + [ "Run", ".ctor" - }; + ]; private static readonly IReadOnlyDictionary AltOpCodes = new Dictionary() { @@ -200,7 +202,16 @@ private static void Diff(CustomAttribute left, CustomAttribute right, ICustomAtt if (!AreSameTypeIgnoreNested(attArg1.Type, attArg2.Type)) throw new InvalidOperationException($"No matching attribute for {left.AttributeType} ({owner})"); - if (!Equals(attArg1.Value, attArg2.Value)) + object leftValue = attArg1.Value; + object rightValue = attArg2.Value; + if (left.AttributeType.FullName == typeof(AsyncStateMachineAttribute).FullName) + { + // We can't compare type arguments of AsyncStateMachineAttribute because the type is generated, + // we just have to compare their ToString. + leftValue = attArg1.Value.ToString(); + rightValue = attArg2.Value.ToString(); + } + if (!Equals(leftValue, rightValue)) throw new InvalidOperationException($"No matching attribute for {left.AttributeType} ({owner})"); } } @@ -341,7 +352,9 @@ private static void DiffDefinition(MethodDefinition method1, MethodDefinition me if (method1.Attributes != method2.Attributes) throw new InvalidOperationException($"No matching method for {method1}"); - if (method1.ImplAttributes != method2.ImplAttributes) + // Roslyn toolchain doesn't apply AggressiveOptimization, and doesn't need to because .Net Framework doesn't have tiered JIT. + // TODO: Don't exclude AggressiveOptimization if/when we compare IL in .Net Core. + if ((method1.ImplAttributes & ~MethodImplAttributes.AggressiveOptimization) != (method2.ImplAttributes & ~MethodImplAttributes.AggressiveOptimization)) throw new InvalidOperationException($"No matching method for {method1}"); if (method1.Parameters.Count != method2.Parameters.Count) diff --git a/tests/BenchmarkDotNet.IntegrationTests/InProcess.EmitTests/Runnable_0.cs b/tests/BenchmarkDotNet.IntegrationTests/InProcess.EmitTests/Runnable_0.cs deleted file mode 100644 index 83eb22ec54..0000000000 --- a/tests/BenchmarkDotNet.IntegrationTests/InProcess.EmitTests/Runnable_0.cs +++ /dev/null @@ -1,33 +0,0 @@ -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation; -using BenchmarkDotNet.Toolchains.Parameters; - -// ReSharper disable once CheckNamespace -namespace BenchmarkDotNet.Autogenerated.ReplaceMe -{ - // Stub for diff for RunMethod - // Used as a template for EmitRunMethod() - internal class Runnable_0 - { - public static void Run(IHost host, ExecuteParameters parameters) - { - var instance = new Runnable_0(); - - var (job, engineParameters, engineFactory) = RunnableReuse.PrepareForRun(instance, host, parameters); - - if (job == null) - return; - - var results = engineFactory.Create(engineParameters).Run(); - host.ReportResults(results); // printing costs memory, do this after runs - - instance.__TrickTheJIT__(); // compile the method for disassembler, but without actual run of the benchmark ;) - engineParameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterEngine); - } - - public void __TrickTheJIT__() - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs b/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs index 7c04a611d0..ce8e7634e1 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs @@ -194,6 +194,13 @@ public ValueTask InvokeOnceValueTaskOfT() return new ValueTask(DecimalResult); } + [Benchmark] + public CustomAwaitable InvokeOnceCustomAwaitable() + { + Interlocked.Increment(ref Counter); + return new(); + } + [Benchmark] public static void InvokeOnceStaticVoid() { @@ -242,6 +249,13 @@ public static ValueTask InvokeOnceStaticValueTaskOfT() Interlocked.Increment(ref Counter); return new ValueTask(DecimalResult); } + + [Benchmark] + public static CustomAwaitable InvokeOnceStaticCustomAwaitable() + { + Interlocked.Increment(ref Counter); + return new(); + } } [Theory] @@ -249,6 +263,7 @@ public static ValueTask InvokeOnceStaticValueTaskOfT() [InlineData(typeof(GlobalSetupCleanupTask))] [InlineData(typeof(GlobalSetupCleanupValueTask))] [InlineData(typeof(GlobalSetupCleanupValueTaskSource))] + [InlineData(typeof(GlobalSetupCleanupCustomAwaitable))] public void InProcessEmitToolchainSupportsSetupAndCleanup(Type benchmarkType) { var logger = new OutputLogger(Output); @@ -365,6 +380,29 @@ public void InvokeOnceVoid() } } + public class GlobalSetupCleanupCustomAwaitable + { + [GlobalSetup] + public static CustomAwaitable GlobalSetup() + { + Interlocked.Increment(ref Counters.SetupCounter); + return new(); + } + + [GlobalCleanup] + public CustomAwaitable GlobalCleanup() + { + Interlocked.Increment(ref Counters.CleanupCounter); + return new(); + } + + [Benchmark] + public void InvokeOnceVoid() + { + Interlocked.Increment(ref Counters.BenchmarkCounter); + } + } + #if NET8_0_OR_GREATER [Fact] diff --git a/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs b/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs index a4ec290c15..57777f8afe 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/InProcessTest.cs @@ -14,8 +14,10 @@ using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; using BenchmarkDotNet.Tests.Loggers; +using BenchmarkDotNet.Tests.Mocks; using BenchmarkDotNet.Toolchains.InProcess.NoEmit; using JetBrains.Annotations; +using Perfolizer.Horology; using Xunit; using Xunit.Abstractions; @@ -34,41 +36,43 @@ public InProcessTest(ITestOutputHelper output) : base(output) private const int UnrollFactor = 16; [Fact] - public void BenchmarkActionGlobalSetupSupported() => TestInvoke(x => BenchmarkAllCases.GlobalSetup(), UnrollFactor); + public async Task BenchmarkActionGlobalSetupSupported() => await TestInvoke(x => BenchmarkAllCases.GlobalSetup(), UnrollFactor); [Fact] - public void BenchmarkActionGlobalCleanupSupported() => TestInvoke(x => x.GlobalCleanup(), UnrollFactor); + public async Task BenchmarkActionGlobalCleanupSupported() => await TestInvoke(x => x.GlobalCleanup(), UnrollFactor); [Fact] - public void BenchmarkActionVoidSupported() => TestInvoke(x => x.InvokeOnceVoid(), UnrollFactor); + public async Task BenchmarkActionVoidSupported() => await TestInvoke(x => x.InvokeOnceVoid(), UnrollFactor); [Fact] - public void BenchmarkActionTaskSupported() => TestInvoke(x => x.InvokeOnceTaskAsync(), UnrollFactor); + public async Task BenchmarkActionTaskSupported() => await TestInvoke(x => x.InvokeOnceTaskAsync(), UnrollFactor); [Fact] - public void BenchmarkActionValueTaskSupported() => TestInvoke(x => x.InvokeOnceValueTaskAsync(), UnrollFactor); + public async Task BenchmarkActionValueTaskSupported() => await TestInvoke(x => x.InvokeOnceValueTaskAsync(), UnrollFactor); [Fact] - public void BenchmarkActionRefTypeSupported() => TestInvoke(x => x.InvokeOnceRefType(), UnrollFactor); + public async Task BenchmarkActionRefTypeSupported() => await TestInvoke(x => x.InvokeOnceRefType(), UnrollFactor); [Fact] - public void BenchmarkActionValueTypeSupported() => TestInvoke(x => x.InvokeOnceValueType(), UnrollFactor); + public async Task BenchmarkActionValueTypeSupported() => await TestInvoke(x => x.InvokeOnceValueType(), UnrollFactor); [Fact] - public void BenchmarkActionTaskOfTSupported() => TestInvoke(x => x.InvokeOnceTaskOfTAsync(), UnrollFactor); + public async Task BenchmarkActionTaskOfTSupported() => await TestInvoke(x => x.InvokeOnceTaskOfTAsync(), UnrollFactor); [Fact] - public void BenchmarkActionValueTaskOfTSupported() => TestInvoke(x => x.InvokeOnceValueTaskOfT(), UnrollFactor); + public async Task BenchmarkActionValueTaskOfTSupported() => await TestInvoke(x => x.InvokeOnceValueTaskOfT(), UnrollFactor); +#pragma warning disable xUnit1031 // Do not use blocking task operations in test method [Fact] - public unsafe void BenchmarkActionVoidPointerSupported() => TestInvoke(x => x.InvokeOnceVoidPointerType(), UnrollFactor); + public unsafe void BenchmarkActionVoidPointerSupported() => TestInvoke(x => x.InvokeOnceVoidPointerType(), UnrollFactor).GetAwaiter().GetResult(); +#pragma warning restore xUnit1031 // Do not use blocking task operations in test method // Can't use ref returns in expression, so pass the MethodInfo directly instead. [Fact] - public void BenchmarkActionByRefTypeSupported() => TestInvoke(typeof(BenchmarkAllCases).GetMethod(nameof(BenchmarkAllCases.InvokeOnceByRefType)), UnrollFactor); + public async Task BenchmarkActionByRefTypeSupported() => await TestInvoke(typeof(BenchmarkAllCases).GetMethod(nameof(BenchmarkAllCases.InvokeOnceByRefType)), UnrollFactor); [Fact] - public void BenchmarkActionByRefReadonlyValueTypeSupported() => TestInvoke(typeof(BenchmarkAllCases).GetMethod(nameof(BenchmarkAllCases.InvokeOnceByRefReadonlyType)), UnrollFactor); + public async Task BenchmarkActionByRefReadonlyValueTypeSupported() => await TestInvoke(typeof(BenchmarkAllCases).GetMethod(nameof(BenchmarkAllCases.InvokeOnceByRefReadonlyType)), UnrollFactor); [Fact] public void BenchmarkDifferentPlatformReturnsValidationError() @@ -89,77 +93,79 @@ public void BenchmarkDifferentPlatformReturnsValidationError() } [AssertionMethod] - private void TestInvoke(Expression> methodCall, int unrollFactor) + private async Task TestInvoke(Expression> methodCall, int unrollFactor) { var targetMethod = ((MethodCallExpression)methodCall.Body).Method; var descriptor = new Descriptor(typeof(BenchmarkAllCases), targetMethod, targetMethod, targetMethod); // Run mode var action = BenchmarkActionFactory.CreateWorkload(descriptor, new BenchmarkAllCases(), unrollFactor); - TestInvoke(action, unrollFactor, false); + await TestInvoke(action, unrollFactor, false); // Idle mode action = BenchmarkActionFactory.CreateOverhead(descriptor, new BenchmarkAllCases(), unrollFactor); - TestInvoke(action, unrollFactor, true); + await TestInvoke(action, unrollFactor, true); // GlobalSetup/GlobalCleanup action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkAllCases()); - TestInvoke(action, 1, false); + await TestInvoke(action, 1, false); action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkAllCases()); - TestInvoke(action, 1, false); + await TestInvoke(action, 1, false); // GlobalSetup/GlobalCleanup (empty) descriptor = new Descriptor(typeof(BenchmarkAllCases), targetMethod); action = BenchmarkActionFactory.CreateGlobalSetup(descriptor, new BenchmarkAllCases()); - TestInvoke(action, unrollFactor, true); + await TestInvoke(action, unrollFactor, true); action = BenchmarkActionFactory.CreateGlobalCleanup(descriptor, new BenchmarkAllCases()); - TestInvoke(action, unrollFactor, true); + await TestInvoke(action, unrollFactor, true); } [AssertionMethod] - private void TestInvoke(Expression> methodCall, int unrollFactor) + private async Task TestInvoke(Expression> methodCall, int unrollFactor) { var targetMethod = ((MethodCallExpression)methodCall.Body).Method; - TestInvoke(targetMethod, unrollFactor); + await TestInvoke(targetMethod, unrollFactor); } [AssertionMethod] - private void TestInvoke(MethodInfo targetMethod, int unrollFactor) + private async Task TestInvoke(MethodInfo targetMethod, int unrollFactor) { var descriptor = new Descriptor(typeof(BenchmarkAllCases), targetMethod); // Run mode var action = BenchmarkActionFactory.CreateWorkload(descriptor, new BenchmarkAllCases(), unrollFactor); - TestInvoke(action, unrollFactor, false); + await TestInvoke(action, unrollFactor, false); // Idle mode action = BenchmarkActionFactory.CreateOverhead(descriptor, new BenchmarkAllCases(), unrollFactor); - TestInvoke(action, unrollFactor, true); + await TestInvoke(action, unrollFactor, true); } [AssertionMethod] - private void TestInvoke(BenchmarkAction benchmarkAction, int unrollFactor, bool isIdle) + private async Task TestInvoke(BenchmarkAction benchmarkAction, int unrollFactor, bool isIdle) { try { BenchmarkAllCases.Counter = 0; + IClock clock = new MockClock(Frequency.MHz); + if (isIdle) { - benchmarkAction.InvokeSingle(); + await benchmarkAction.InvokeSingle(); Assert.Equal(0, BenchmarkAllCases.Counter); - benchmarkAction.InvokeUnroll(0); + await benchmarkAction.InvokeUnroll(0, clock); Assert.Equal(0, BenchmarkAllCases.Counter); - benchmarkAction.InvokeUnroll(11); + await benchmarkAction.InvokeUnroll(11, clock); Assert.Equal(0, BenchmarkAllCases.Counter); } else { - benchmarkAction.InvokeSingle(); + await benchmarkAction.InvokeSingle(); Assert.Equal(1, BenchmarkAllCases.Counter); - benchmarkAction.InvokeUnroll(0); + await benchmarkAction.InvokeUnroll(0, clock); Assert.Equal(1, BenchmarkAllCases.Counter); - benchmarkAction.InvokeUnroll(11); + await benchmarkAction.InvokeUnroll(11, clock); Assert.Equal(BenchmarkAllCases.Counter, 1 + unrollFactor * 11); } } diff --git a/tests/BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs b/tests/BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs new file mode 100644 index 0000000000..8bb7972fd4 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs @@ -0,0 +1,79 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Tests.XUnit; +using BenchmarkDotNet.Toolchains; +using BenchmarkDotNet.Toolchains.InProcess.Emit; +using BenchmarkDotNet.Toolchains.InProcess.NoEmit; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.IntegrationTests; + +public class RunAsyncTests(ITestOutputHelper output) : BenchmarkTestExecutor(output) +{ + public static TheoryData GetInProcessToolchains() => new( + [ + new InProcessEmitToolchain(new() { ExecuteOnSeparateThread = false }), + new InProcessNoEmitToolchain(new() { ExecuteOnSeparateThread = false }), + ]); + + private void ExecuteAndAssert(IToolchain toolchain, bool expectsAsync) + { + using var context = BenchmarkSynchronizationContext.CreateAndSetCurrent(); + var config = CreateSimpleConfig(job: Job.Dry.WithToolchain(toolchain)); + var valueTask = CanExecuteAsync(config); + Assert.NotEqual(expectsAsync, valueTask.IsCompleted); + context.ExecuteUntilComplete(valueTask); + Assert.True(valueTask.IsCompletedSuccessfully); + } + + [Fact] + public void OutOfProcessAsyncBenchmarksRunSync() + => ExecuteAndAssert(Job.Default.GetToolchain(), false); + + [Theory] + [MemberData(nameof(GetInProcessToolchains), DisableDiscoveryEnumeration = true)] + public void InProcessSyncBenchmarksRunSync(IToolchain toolchain) + => ExecuteAndAssert(toolchain, false); + + [Theory] + [MemberData(nameof(GetInProcessToolchains), DisableDiscoveryEnumeration = true)] + public void InProcessAsyncBenchmarksRunAsync(IToolchain toolchain) + => ExecuteAndAssert(toolchain, true); + + public class BenchmarkSync + { + [GlobalSetup] public void GlobalSetup() { } + [GlobalCleanup] public void GlobalCleanup() { } + [IterationSetup] public void IterationSetup() { } + [IterationCleanup] public void IterationCleanup() { } + + [Benchmark] public void ReturnVoid() { } + [Benchmark] public object ReturnObject() => new(); + } + + public class BenchmarkAsync + { + [GlobalSetup] public async Task GlobalSetup() => await Task.Yield(); + + [GlobalCleanup] public async Task GlobalCleanup() => await Task.Yield(); + [IterationSetup] public async Task IterationSetup() => await Task.Yield(); + [IterationCleanup] public async Task IterationCleanup() => await Task.Yield(); + + [Benchmark] public async Task AsyncTask() => await Task.Yield(); + + [Benchmark] + public async ValueTask AsyncValueTaskObject() + { + await Task.Yield(); + return new(); + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/ToolchainTest.cs b/tests/BenchmarkDotNet.IntegrationTests/ToolchainTest.cs index f8cb9686f3..0dfb4f8349 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/ToolchainTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/ToolchainTest.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; @@ -25,7 +26,7 @@ public GenerateResult GenerateProject(BuildPartition buildPartition, ILogger log { logger.WriteLine("Generating"); Done = true; - return new GenerateResult(null, true, null, Array.Empty()); + return new GenerateResult(null, true, null, []); } } @@ -45,11 +46,11 @@ private class MyExecutor : IExecutor { public bool Done { get; private set; } - public ExecuteResult Execute(ExecuteParameters executeParameters) + public ValueTask ExecuteAsync(ExecuteParameters executeParameters) { executeParameters.Logger.WriteLine("Executing"); Done = true; - return new ExecuteResult(true, 0, default, Array.Empty(), Array.Empty(), Array.Empty(), executeParameters.LaunchIndex); + return new(new ExecuteResult(true, 0, default, [], [], [], executeParameters.LaunchIndex)); } } diff --git a/tests/BenchmarkDotNet.Tests/Engine/EngineMethodImplTests.cs b/tests/BenchmarkDotNet.Tests/Engine/EngineMethodImplTests.cs new file mode 100644 index 0000000000..8c9be9c1c7 --- /dev/null +++ b/tests/BenchmarkDotNet.Tests/Engine/EngineMethodImplTests.cs @@ -0,0 +1,30 @@ +using System; +using System.Reflection; +using Xunit; + +namespace BenchmarkDotNet.Tests.Engine; + +public class EngineMethodImplTests +{ + [Fact] + public void AllEngineMethodsAreAggressivelyOptimized() + { + AssertMethodsAggressivelyOptimized(typeof(Engines.Engine)); + + static void AssertMethodsAggressivelyOptimized(Type type) + { + foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + Assert.True( + method.MethodImplementationFlags.HasFlag(BenchmarkDotNet.Portability.CodeGenHelper.AggressiveOptimizationOptionForEmit), + $"Method is not aggressively optimized: {method}" + ); + } + + foreach (var nestedType in type.GetNestedTypes()) + { + AssertMethodsAggressivelyOptimized(nestedType); + } + } + } +} diff --git a/tests/BenchmarkDotNet.Tests/Engine/EnumerateStagesTests.cs b/tests/BenchmarkDotNet.Tests/Engine/EnumerateStagesTests.cs index 1c61f5eee1..98ef0d1034 100644 --- a/tests/BenchmarkDotNet.Tests/Engine/EnumerateStagesTests.cs +++ b/tests/BenchmarkDotNet.Tests/Engine/EnumerateStagesTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Reports; @@ -172,17 +173,18 @@ public void MediumTimeConsumingBenchmarksStartPilotFrom2AndIncrementItWithEveryS private EngineParameters CreateEngineParameters(Job job) { var host = new NoAcknowledgementConsoleHost(); + Func> emptyAction = (_, _) => new(default(ClockSpan)); return new() { - GlobalSetupAction = () => { }, - GlobalCleanupAction = () => { }, + GlobalSetupAction = () => new(), + GlobalCleanupAction = () => new(), Host = host, - OverheadActionUnroll = _ => { }, - OverheadActionNoUnroll = _ => { }, - IterationCleanupAction = () => { }, - IterationSetupAction = () => { }, - WorkloadActionUnroll = _ => { }, - WorkloadActionNoUnroll = _ => { }, + OverheadActionUnroll = emptyAction, + OverheadActionNoUnroll = emptyAction, + IterationCleanupAction = () => new(), + IterationSetupAction = () => new(), + WorkloadActionUnroll = emptyAction, + WorkloadActionNoUnroll = emptyAction, TargetJob = job, InProcessDiagnoserHandler = new([], host, Diagnosers.RunMode.None, null) }; diff --git a/tests/BenchmarkDotNet.Tests/Mocks/MockEngine.cs b/tests/BenchmarkDotNet.Tests/Mocks/MockEngine.cs index a4800c43af..7cf40144fb 100644 --- a/tests/BenchmarkDotNet.Tests/Mocks/MockEngine.cs +++ b/tests/BenchmarkDotNet.Tests/Mocks/MockEngine.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Running; -using JetBrains.Annotations; using Perfolizer.Horology; using Xunit.Abstractions; @@ -22,17 +22,18 @@ internal MockEngine(ITestOutputHelper output, Job job, Func> emptyAction = (_, _) => new(default(ClockSpan)); Parameters = new EngineParameters { TargetJob = job, - WorkloadActionUnroll = _ => { }, - WorkloadActionNoUnroll = _ => { }, - OverheadActionUnroll = _ => { }, - OverheadActionNoUnroll = _ => { }, - GlobalSetupAction = () => { }, - GlobalCleanupAction = () => { }, - IterationSetupAction = () => { }, - IterationCleanupAction = () => { }, + WorkloadActionUnroll = emptyAction, + WorkloadActionNoUnroll = emptyAction, + OverheadActionUnroll = emptyAction, + OverheadActionNoUnroll = emptyAction, + GlobalSetupAction = () => new(), + GlobalCleanupAction = () => new(), + IterationSetupAction = () => new(), + IterationCleanupAction = () => new(), }; } @@ -46,7 +47,7 @@ private Measurement RunIteration(IterationData data) return measurement; } - public RunResults Run() => default; + public ValueTask RunAsync() => new(default(RunResults)); internal List Run(EngineStage stage) { diff --git a/tests/BenchmarkDotNet.Tests/Mocks/Toolchain/MockToolchain.cs b/tests/BenchmarkDotNet.Tests/Mocks/Toolchain/MockToolchain.cs index 365a55cb88..0cf0cd7c3a 100644 --- a/tests/BenchmarkDotNet.Tests/Mocks/Toolchain/MockToolchain.cs +++ b/tests/BenchmarkDotNet.Tests/Mocks/Toolchain/MockToolchain.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; +using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Reports; @@ -12,24 +12,21 @@ namespace BenchmarkDotNet.Tests.Mocks.Toolchain { - public class MockToolchain : IToolchain + public class MockToolchain(Func> measurer) : IToolchain { - public MockToolchain(Func> measurer) - => Executor = new MockExecutor(measurer); - public string Name => nameof(MockToolchain); public IGenerator Generator => new MockGenerator(); public IBuilder Builder => new MockBuilder(); - public IExecutor Executor { get; private set; } + public IExecutor Executor { get; private set; } = new MockExecutor(measurer); public bool IsInProcess => false; - public IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) => ImmutableArray.Empty; + public IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) => []; public override string ToString() => GetType().Name; private class MockGenerator : IGenerator { public GenerateResult GenerateProject(BuildPartition buildPartition, ILogger logger, string rootArtifactsFolderPath) - => GenerateResult.Success(ArtifactsPaths.Empty, ImmutableArray.Empty); + => GenerateResult.Success(ArtifactsPaths.Empty, []); } private class MockBuilder : IBuilder @@ -37,13 +34,9 @@ private class MockBuilder : IBuilder public BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger) => BuildResult.Success(generateResult); } - private class MockExecutor : IExecutor + private class MockExecutor(Func> measurer) : IExecutor { - private readonly Func> measurer; - - public MockExecutor(Func> measurer) => this.measurer = measurer; - - public ExecuteResult Execute(ExecuteParameters executeParameters) => new(measurer(executeParameters.BenchmarkCase)); + public ValueTask ExecuteAsync(ExecuteParameters executeParameters) => new(new ExecuteResult(measurer(executeParameters.BenchmarkCase))); } } } \ No newline at end of file From 224461704854f4ec61bb58e2310e0655908d025e Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 5 Jan 2026 03:49:07 -0500 Subject: [PATCH 02/33] Async validation --- .editorconfig | 4 + BenchmarkDotNet.slnx | 3 + .../ConcurrencyVisualizerProfiler.cs | 2 +- .../EtwProfiler.cs | 4 +- .../JitDiagnoser.cs | 2 +- .../NativeMemoryProfiler.cs | 2 +- src/BenchmarkDotNet/BenchmarkDotNet.csproj | 3 +- .../Diagnosers/CompositeDiagnoser.cs | 4 +- .../Diagnosers/EventPipeProfiler.cs | 2 +- .../Diagnosers/ExceptionDiagnoser.cs | 2 +- src/BenchmarkDotNet/Diagnosers/IDiagnoser.cs | 2 +- .../Diagnosers/MemoryDiagnoser.cs | 3 +- .../Diagnosers/PerfCollectProfiler.cs | 2 +- .../Diagnosers/SnapshotProfilerBase.cs | 2 +- .../Diagnosers/ThreadingDiagnoser.cs | 2 +- .../Diagnosers/UnresolvedDiagnoser.cs | 6 +- .../Disassemblers/DisassemblyDiagnoser.cs | 2 +- src/BenchmarkDotNet/Helpers/AwaitHelper.cs | 108 ----------------- .../Helpers/DynamicAwaitHelper.cs | 61 ++++++++++ src/BenchmarkDotNet/Running/BenchmarkCase.cs | 2 +- .../Running/BenchmarkRunnerClean.cs | 39 +++--- .../Toolchains/CoreRun/CoreRunToolchain.cs | 2 +- .../CsProj/CsProjClassicNetToolchain.cs | 4 +- .../Toolchains/CsProj/CsProjCoreToolchain.cs | 4 +- src/BenchmarkDotNet/Toolchains/IToolchain.cs | 2 +- .../InProcess/InProcessValidator.cs | 4 +- .../NoEmit/InProcessNoEmitToolchain.cs | 4 +- .../Toolchains/Mono/MonoAotToolchain.cs | 4 +- .../MonoAotLLVM/MonoAotLLVMToolChain.cs | 4 +- .../Toolchains/MonoWasm/WasmToolchain.cs | 4 +- .../NativeAot/NativeAotToolchain.cs | 4 +- .../Toolchains/Roslyn/RoslynToolchain.cs | 4 +- src/BenchmarkDotNet/Toolchains/Toolchain.cs | 2 +- .../Toolchains/ToolchainExtensions.cs | 2 +- .../Validators/BaselineValidator.cs | 2 +- .../Validators/CompilationValidator.cs | 13 +- .../Validators/CompositeValidator.cs | 4 +- .../Validators/ConfigValidator.cs | 2 +- .../Validators/DeferredExecutionValidator.cs | 6 +- .../Validators/DiagnosersValidator.cs | 5 +- .../Validators/DotNetSdkValidator.cs | 2 +- .../Validators/ExecutionValidator.cs | 7 +- .../Validators/ExecutionValidatorBase.cs | 42 +++---- .../Validators/GenericBenchmarksValidator.cs | 5 +- src/BenchmarkDotNet/Validators/IValidator.cs | 2 +- .../Validators/JitOptimizationsValidator.cs | 2 +- .../Validators/ParamsAllValuesValidator.cs | 5 +- .../Validators/ParamsValidator.cs | 7 +- .../Validators/ReturnValueValidator.cs | 13 +- .../Validators/RunModeValidator.cs | 2 +- .../Validators/RuntimeValidator.cs | 8 +- .../Validators/SetupCleanupValidator.cs | 4 +- .../Validators/ShadowCopyValidator.cs | 6 +- .../Diagnosers/MockInProcessDiagnoser.cs | 3 +- .../EventProcessorTests.cs | 4 +- .../JitOptimizationsTests.cs | 11 +- .../ValidatorsTest.cs | 2 +- .../Attributes/ParamsAllValuesVerifyTests.cs | 8 +- .../Exporters/MarkdownExporterVerifyTests.cs | 8 +- .../Mocks/Toolchain/MockToolchain.cs | 3 +- .../Validators/CompilationValidatorTests.cs | 22 ++-- .../DeferredExecutionValidatorTests.cs | 8 +- .../Validators/ExecutionValidatorTests.cs | 112 +++++++++--------- .../Validators/ParamsValidatorTests.cs | 71 +++++------ .../Validators/ReturnValueValidatorTests.cs | 71 +++++------ .../Validators/RuntimeValidatorTests.cs | 13 +- .../Validators/SetupCleanupValidatorTests.cs | 13 +- 67 files changed, 381 insertions(+), 406 deletions(-) create mode 100644 .editorconfig delete mode 100644 src/BenchmarkDotNet/Helpers/AwaitHelper.cs create mode 100644 src/BenchmarkDotNet/Helpers/DynamicAwaitHelper.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..6b553286b8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CS1998: Async method lacks 'await' operators and will run synchronously +dotnet_diagnostic.CS1998.severity = suggestion diff --git a/BenchmarkDotNet.slnx b/BenchmarkDotNet.slnx index 1d2b3755ad..332c23c455 100644 --- a/BenchmarkDotNet.slnx +++ b/BenchmarkDotNet.slnx @@ -8,6 +8,9 @@ + + + diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/ConcurrencyVisualizerProfiler.cs b/src/BenchmarkDotNet.Diagnostics.Windows/ConcurrencyVisualizerProfiler.cs index acb6280162..f414df0151 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/ConcurrencyVisualizerProfiler.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/ConcurrencyVisualizerProfiler.cs @@ -75,7 +75,7 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) public IEnumerable ProcessResults(DiagnoserResults results) => etwProfiler.ProcessResults(results); - public IEnumerable Validate(ValidationParameters validationParameters) => etwProfiler.Validate(validationParameters); + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => etwProfiler.ValidateAsync(validationParameters); private static EtwProfilerConfig CreateDefaultConfig() { diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfiler.cs b/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfiler.cs index 57f57d9e1a..8e0f905d3f 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfiler.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfiler.cs @@ -57,8 +57,8 @@ public EtwProfiler(EtwProfilerConfig config) public RunMode GetRunMode(BenchmarkCase benchmarkCase) => runMode; - public IEnumerable Validate(ValidationParameters validationParameters) - => HardwareCounters.Validate(validationParameters, mandatory: false); + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) + => HardwareCounters.Validate(validationParameters, mandatory: false).ToAsyncEnumerable(); public void Handle(HostSignal signal, DiagnoserActionParameters parameters) { diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs index 0d06faf75b..eb72a6e723 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/JitDiagnoser.cs @@ -32,7 +32,7 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) public virtual IEnumerable ProcessResults(DiagnoserResults results) => Array.Empty(); - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { if (!OsDetector.IsWindows()) { diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/NativeMemoryProfiler.cs b/src/BenchmarkDotNet.Diagnostics.Windows/NativeMemoryProfiler.cs index ece842c36f..8ee6e04077 100644 --- a/src/BenchmarkDotNet.Diagnostics.Windows/NativeMemoryProfiler.cs +++ b/src/BenchmarkDotNet.Diagnostics.Windows/NativeMemoryProfiler.cs @@ -56,7 +56,7 @@ public IEnumerable ProcessResults(DiagnoserResults results) return new NativeMemoryLogParser(traceFilePath, results.BenchmarkCase, logger, results.BuildResult.ArtifactsPaths.ProgramName).Parse(); } - public IEnumerable Validate(ValidationParameters validationParameters) => etwProfiler.Validate(validationParameters); + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => etwProfiler.ValidateAsync(validationParameters); private static EtwProfilerConfig CreateDefaultConfig() { diff --git a/src/BenchmarkDotNet/BenchmarkDotNet.csproj b/src/BenchmarkDotNet/BenchmarkDotNet.csproj index e55863fa45..4c36bb3e95 100644 --- a/src/BenchmarkDotNet/BenchmarkDotNet.csproj +++ b/src/BenchmarkDotNet/BenchmarkDotNet.csproj @@ -5,7 +5,7 @@ BenchmarkDotNet netstandard2.0;net6.0;net8.0;net9.0;net10.0 true - $(NoWarn);1701;1702;1705;1591;3005;NU1702;CS3001;CS3003 + $(NoWarn);1701;1702;1705;1591;3005;NU1510;NU1702;CS3001;CS3003 BenchmarkDotNet BenchmarkDotNet BenchmarkDotNet @@ -28,6 +28,7 @@ + diff --git a/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs index e1b49a6ff3..bed433788d 100644 --- a/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs @@ -55,8 +55,8 @@ public void DisplayResults(ILogger logger) } } - public IEnumerable Validate(ValidationParameters validationParameters) - => diagnosers.SelectMany(diagnoser => diagnoser.Validate(validationParameters)); + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) + => diagnosers.ToAsyncEnumerable().SelectMany(diagnoser => diagnoser.ValidateAsync(validationParameters)); } public sealed class CompositeInProcessDiagnoser(IReadOnlyList inProcessDiagnosers) diff --git a/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs b/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs index 2f60a99805..c4627240b6 100644 --- a/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs +++ b/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs @@ -56,7 +56,7 @@ public EventPipeProfiler(EventPipeProfile profile = EventPipeProfile.CpuSampling public RunMode GetRunMode(BenchmarkCase benchmarkCase) => performExtraBenchmarksRun ? RunMode.ExtraRun : RunMode.NoOverhead; - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { foreach (var benchmark in validationParameters.Benchmarks) { diff --git a/src/BenchmarkDotNet/Diagnosers/ExceptionDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/ExceptionDiagnoser.cs index 967efe1822..00516ad3dd 100644 --- a/src/BenchmarkDotNet/Diagnosers/ExceptionDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/ExceptionDiagnoser.cs @@ -49,7 +49,7 @@ public IEnumerable ProcessResults(DiagnoserResults diagnoserResults) } } - public IEnumerable Validate(ValidationParameters validationParameters) => []; + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => AsyncEnumerable.Empty(); void IInProcessDiagnoser.DeserializeResults(BenchmarkCase benchmarkCase, string serializedResults) => results.Add(benchmarkCase, long.Parse(serializedResults)); diff --git a/src/BenchmarkDotNet/Diagnosers/IDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/IDiagnoser.cs index d28c63f9ee..475720ede3 100644 --- a/src/BenchmarkDotNet/Diagnosers/IDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/IDiagnoser.cs @@ -26,7 +26,7 @@ public interface IDiagnoser void DisplayResults(ILogger logger); - IEnumerable Validate(ValidationParameters validationParameters); + IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters); } public interface IConfigurableDiagnoser : IDiagnoser diff --git a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs index e9c9fd7ae5..73d7ee16fd 100644 --- a/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/MemoryDiagnoser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Engines; @@ -27,7 +28,7 @@ public class MemoryDiagnoser : IDiagnoser public IEnumerable Exporters => Array.Empty(); public IEnumerable Analysers => Array.Empty(); public void DisplayResults(ILogger logger) { } - public IEnumerable Validate(ValidationParameters validationParameters) => Array.Empty(); + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => AsyncEnumerable.Empty(); // the action takes places in other process, and the values are gathered by Engine public void Handle(HostSignal signal, DiagnoserActionParameters parameters) { } diff --git a/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs b/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs index cb745092ba..30bbbaf7a9 100644 --- a/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs +++ b/src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs @@ -52,7 +52,7 @@ public class PerfCollectProfiler : IProfiler public RunMode GetRunMode(BenchmarkCase benchmarkCase) => config.RunMode; - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { if (!OsDetector.IsLinux()) { diff --git a/src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs b/src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs index 26e9192fa3..6fddd1d94a 100644 --- a/src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs +++ b/src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs @@ -67,7 +67,7 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) } } - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { var runtimeMonikers = validationParameters.Benchmarks.Select(b => b.Job.Environment.GetRuntime().RuntimeMoniker).Distinct(); foreach (var runtimeMoniker in runtimeMonikers) diff --git a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs index fd1d87e3f9..b078cdea0d 100644 --- a/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/ThreadingDiagnoser.cs @@ -52,7 +52,7 @@ public IEnumerable ProcessResults(DiagnoserResults diagnoserResults) } } - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { foreach (var benchmark in validationParameters.Benchmarks) { diff --git a/src/BenchmarkDotNet/Diagnosers/UnresolvedDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/UnresolvedDiagnoser.cs index 43cc910559..e7a44ea785 100644 --- a/src/BenchmarkDotNet/Diagnosers/UnresolvedDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/UnresolvedDiagnoser.cs @@ -28,8 +28,10 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) { } public void DisplayResults(ILogger logger) => logger.WriteLineError(GetErrorMessage()); - public IEnumerable Validate(ValidationParameters validationParameters) - => new[] { new ValidationError(false, GetErrorMessage()) }; + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) + { + yield return new ValidationError(false, GetErrorMessage()); + } private string GetErrorMessage() => $@"Unable to resolve {unresolved.Name} diagnoser using dynamic assembly loading. {(RuntimeInformation.IsFullFramework || OsDetector.IsWindows() diff --git a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs index 80c404f5d1..ee787d7fda 100644 --- a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs +++ b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs @@ -116,7 +116,7 @@ public void DisplayResults(ILogger logger) ? "Disassembled benchmarks got exported to \".\\BenchmarkDotNet.Artifacts\\results\\*asm.md\"" : "No benchmarks were disassembled"); - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { var currentPlatform = RuntimeInformation.GetCurrentPlatform(); if (!(currentPlatform is Platform.X64 or Platform.X86 or Platform.Arm64)) diff --git a/src/BenchmarkDotNet/Helpers/AwaitHelper.cs b/src/BenchmarkDotNet/Helpers/AwaitHelper.cs deleted file mode 100644 index 469f6c772e..0000000000 --- a/src/BenchmarkDotNet/Helpers/AwaitHelper.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace BenchmarkDotNet.Helpers -{ - internal static class AwaitHelper - { - private class ValueTaskWaiter - { - // We use thread static field so that each thread uses its own individual callback and reset event. - [ThreadStatic] - private static ValueTaskWaiter ts_current; - internal static ValueTaskWaiter Current => ts_current ??= new ValueTaskWaiter(); - - // We cache the callback to prevent allocations for memory diagnoser. - private readonly Action awaiterCallback; - private readonly ManualResetEventSlim resetEvent; - - private ValueTaskWaiter() - { - resetEvent = new(); - awaiterCallback = resetEvent.Set; - } - - internal void Wait(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion - { - resetEvent.Reset(); - awaiter.UnsafeOnCompleted(awaiterCallback); - - // The fastest way to wait for completion is to spin a bit before waiting on the event. This is the same logic that Task.GetAwaiter().GetResult() uses. - var spinner = new SpinWait(); - while (!resetEvent.IsSet) - { - if (spinner.NextSpinWillYield) - { - resetEvent.Wait(); - return; - } - spinner.SpinOnce(); - } - } - } - - // we use GetAwaiter().GetResult() because it's fastest way to obtain the result in blocking way, - // and will eventually throw actual exception, not aggregated one - public static void GetResult(Task task) => task.GetAwaiter().GetResult(); - - public static T GetResult(Task task) => task.GetAwaiter().GetResult(); - - // ValueTask can be backed by an IValueTaskSource that only supports asynchronous awaits, - // so we have to hook up a callback instead of calling .GetAwaiter().GetResult() like we do for Task. - // The alternative is to convert it to Task using .AsTask(), but that causes allocations which we must avoid for memory diagnoser. - public static void GetResult(ValueTask task) - { - // Don't continue on the captured context, as that may result in a deadlock if the user runs this in-process. - var awaiter = task.ConfigureAwait(false).GetAwaiter(); - if (!awaiter.IsCompleted) - { - ValueTaskWaiter.Current.Wait(awaiter); - } - awaiter.GetResult(); - } - - public static T GetResult(ValueTask task) - { - // Don't continue on the captured context, as that may result in a deadlock if the user runs this in-process. - var awaiter = task.ConfigureAwait(false).GetAwaiter(); - if (!awaiter.IsCompleted) - { - ValueTaskWaiter.Current.Wait(awaiter); - } - return awaiter.GetResult(); - } - - internal static MethodInfo GetGetResultMethod(Type taskType) - { - if (!taskType.IsGenericType) - { - return typeof(AwaitHelper).GetMethod(nameof(GetResult), BindingFlags.Public | BindingFlags.Static, null, [taskType], null); - } - - Type compareType = taskType.GetGenericTypeDefinition() == typeof(ValueTask<>) ? typeof(ValueTask<>) - : typeof(Task).IsAssignableFrom(taskType.GetGenericTypeDefinition()) ? typeof(Task<>) - : null; - if (compareType == null) - { - return null; - } - var resultType = taskType - .GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance) - .ReturnType - .GetMethod(nameof(TaskAwaiter.GetResult), BindingFlags.Public | BindingFlags.Instance) - .ReturnType; - return typeof(AwaitHelper).GetMethods(BindingFlags.Public | BindingFlags.Static) - .First(m => - { - if (m.Name != nameof(GetResult)) return false; - Type paramType = m.GetParameters().First().ParameterType; - return paramType.IsGenericType && paramType.GetGenericTypeDefinition() == compareType; - }) - .MakeGenericMethod([resultType]); - } - } -} diff --git a/src/BenchmarkDotNet/Helpers/DynamicAwaitHelper.cs b/src/BenchmarkDotNet/Helpers/DynamicAwaitHelper.cs new file mode 100644 index 0000000000..0a32511ea5 --- /dev/null +++ b/src/BenchmarkDotNet/Helpers/DynamicAwaitHelper.cs @@ -0,0 +1,61 @@ +using BenchmarkDotNet.Extensions; +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace BenchmarkDotNet.Helpers; + +internal static class DynamicAwaitHelper +{ + internal static async ValueTask<(bool hasResult, object? result)> GetOrAwaitResult(object? value) + { + var valueType = value?.GetType(); + if (valueType?.IsAwaitable() != true) + { + return (true, value); + } + + var getAwaiterMethod = valueType.GetMethod(nameof(Task.GetAwaiter), BindingFlags.Public | BindingFlags.Instance); + var awaiterType = getAwaiterMethod.ReturnType; + var getResultMethod = awaiterType.GetMethod(nameof(TaskAwaiter.GetResult), BindingFlags.Public | BindingFlags.Instance); + var result = await new DynamicAwaitable(getAwaiterMethod, awaiterType, getResultMethod, value); + return (getResultMethod.ReturnType != typeof(void), result); + } + + private readonly struct DynamicAwaitable(MethodInfo getAwaiterMethod, Type awaiterType, MethodInfo getResultMethod, object awaitable) + { + public DynamicAwaiter GetAwaiter() + => new(awaiterType, getResultMethod, getAwaiterMethod.Invoke(awaitable, null)); + } + + private readonly struct DynamicAwaiter(Type awaiterType, MethodInfo getResultMethod, object awaiter) : ICriticalNotifyCompletion + { + public bool IsCompleted + => awaiterType.GetProperty(nameof(TaskAwaiter.IsCompleted), BindingFlags.Public | BindingFlags.Instance).GetMethod.Invoke(awaiter, null) is true; + + public object GetResult() + => getResultMethod.Invoke(awaiter, null); + + public void OnCompleted(Action continuation) + => OnCompletedCore(typeof(INotifyCompletion), nameof(INotifyCompletion.OnCompleted), continuation); + + public void UnsafeOnCompleted(Action continuation) + => OnCompletedCore(typeof(ICriticalNotifyCompletion), nameof(ICriticalNotifyCompletion.UnsafeOnCompleted), continuation); + + private void OnCompletedCore(Type interfaceType, string methodName, Action continuation) + { + var onCompletedMethod = interfaceType.GetMethod(methodName); + var map = awaiterType.GetInterfaceMap(interfaceType); + + for (int i = 0; i < map.InterfaceMethods.Length; i++) + { + if (map.InterfaceMethods[i] == onCompletedMethod) + { + map.TargetMethods[i].Invoke(awaiter, [continuation]); + return; + } + } + } + } +} diff --git a/src/BenchmarkDotNet/Running/BenchmarkCase.cs b/src/BenchmarkDotNet/Running/BenchmarkCase.cs index 2a8f749a62..1a42cc36e0 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkCase.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkCase.cs @@ -30,7 +30,7 @@ internal BenchmarkCase(Descriptor descriptor, Job job, ParameterInstances parame public Runtime GetRuntime() => Job.Environment.HasValue(EnvironmentMode.RuntimeCharacteristic) ? Job.Environment.Runtime - : RuntimeInformation.GetTargetOrCurrentRuntime(Descriptor.WorkloadMethod.DeclaringType.Assembly); + : RuntimeInformation.GetTargetOrCurrentRuntime(Descriptor.Type.Assembly); public void Dispose() => Parameters.Dispose(); diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs index df83c8b961..827dd340d5 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs @@ -63,9 +63,9 @@ internal static async ValueTask Run(BenchmarkRunInfo[] benchmarkRunIn compositeLogger.WriteLineInfo("// Validating benchmarks:"); - var (supportedBenchmarks, validationErrors) = GetSupportedBenchmarks(benchmarkRunInfos, resolver); + var (supportedBenchmarks, validationErrors) = await GetSupportedBenchmarks(benchmarkRunInfos, resolver); - validationErrors.AddRange(Validate(supportedBenchmarks)); + validationErrors.AddRange(await Validate(supportedBenchmarks)); foreach (var validationError in validationErrors) eventProcessor.OnValidationError(validationError); @@ -307,7 +307,7 @@ internal static async ValueTask Run(BenchmarkRunInfo[] benchmarkRunIn logFilePath, runEnd.GetTimeSpan() - runStart.GetTimeSpan(), cultureInfo, - Validate(benchmarkRunInfo), // validate them once again, but don't print the output + [.. await Validate(benchmarkRunInfo)], // validate them once again, but don't print the output [.. config.GetColumnHidingRules()] ), benchmarksToRunCount @@ -380,15 +380,11 @@ private static void PrintSummary(ILogger logger, ImmutableConfig config, Summary logger.WriteLineHeader("// ***** BenchmarkRunner: End *****"); } - private static ImmutableArray Validate(params BenchmarkRunInfo[] benchmarks) - { - var validationErrors = new List(); - - foreach (var benchmarkRunInfo in benchmarks) - validationErrors.AddRange(benchmarkRunInfo.Config.GetCompositeValidator().Validate(new ValidationParameters(benchmarkRunInfo.BenchmarksCases, benchmarkRunInfo.Config))); - - return validationErrors.ToImmutableArray(); - } + private static async ValueTask> Validate(params BenchmarkRunInfo[] benchmarks) + => await benchmarks + .ToAsyncEnumerable() + .SelectMany(benchmark => benchmark.Config.GetCompositeValidator().ValidateAsync(new ValidationParameters(benchmark.BenchmarksCases, benchmark.Config))) + .ToArrayAsync(); private static Dictionary BuildInParallel(ILogger logger, string rootArtifactsFolderPath, BuildPartition[] buildPartitions, in StartedClock globalChronometer, EventProcessor eventProcessor) { @@ -656,15 +652,15 @@ private static async ValueTask RunExecute(ILogger logger, Benchma private static void LogTotalTime(ILogger logger, TimeSpan time, int executedBenchmarksCount, string message = "Total time") => logger.WriteLineStatistic($"{message}: {time.ToFormattedTotalTime(DefaultCultureInfo.Instance)}, executed benchmarks: {executedBenchmarksCount}"); - private static (BenchmarkRunInfo[], List) GetSupportedBenchmarks(BenchmarkRunInfo[] benchmarkRunInfos, IResolver resolver) + private static async ValueTask<(BenchmarkRunInfo[], List)> GetSupportedBenchmarks(BenchmarkRunInfo[] benchmarkRunInfos, IResolver resolver) { - List validationErrors = new(); + List validationErrors = []; List runInfos = new(benchmarkRunInfos.Length); if (benchmarkRunInfos.Length == 0) { validationErrors.Add(new ValidationError(true, $"No benchmarks were found.")); - return (Array.Empty(), validationErrors); + return ([], validationErrors); } foreach (var benchmarkRunInfo in benchmarkRunInfos) @@ -675,19 +671,20 @@ private static (BenchmarkRunInfo[], List) GetSupportedBenchmark continue; } - var validBenchmarks = benchmarkRunInfo.BenchmarksCases - .Where(benchmark => + var validBenchmarks = await benchmarkRunInfo.BenchmarksCases + .ToAsyncEnumerable() + .Where(async (benchmark, _) => { - var errors = benchmark.GetToolchain() - .Validate(benchmark, resolver) - .ToArray(); + var errors = await benchmark.GetToolchain() + .ValidateAsync(benchmark, resolver) + .ToArrayAsync(); validationErrors.AddRange(errors); return !errors.Any(error => error.IsCritical); }) - .ToArray(); + .ToArrayAsync(); runInfos.Add( new BenchmarkRunInfo( diff --git a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs index 43337cdd4a..92bc916672 100644 --- a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs @@ -58,7 +58,7 @@ public CoreRunToolchain(FileInfo coreRun, bool createCopy = true, public override string ToString() => Name; - public IEnumerable Validate(BenchmarkCase benchmark, IResolver resolver) + public async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmark, IResolver resolver) { if (!SourceCoreRun.Exists) { diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs index 2761bd7c2c..f066607ef9 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs @@ -38,9 +38,9 @@ private CsProjClassicNetToolchain(string targetFrameworkMoniker, string name, st public static IToolchain From(string targetFrameworkMoniker, string? packagesPath = null, string? customDotNetCliPath = null) => new CsProjClassicNetToolchain(targetFrameworkMoniker, targetFrameworkMoniker, packagesPath, customDotNetCliPath); - public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public override async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { - foreach (var validationError in base.Validate(benchmarkCase, resolver)) + await foreach (var validationError in base.ValidateAsync(benchmarkCase, resolver)) { yield return validationError; } diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs index 3930a1ac9e..8627ca11a0 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs @@ -44,9 +44,9 @@ public static IToolchain From(NetCoreAppSettings settings) new DotNetCliExecutor(settings.CustomDotNetCliPath), settings.CustomDotNetCliPath); - public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public override async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { - foreach (var validationError in base.Validate(benchmarkCase, resolver)) + await foreach (var validationError in base.ValidateAsync(benchmarkCase, resolver)) { yield return validationError; } diff --git a/src/BenchmarkDotNet/Toolchains/IToolchain.cs b/src/BenchmarkDotNet/Toolchains/IToolchain.cs index 7085b820fe..5f8e3da7b2 100644 --- a/src/BenchmarkDotNet/Toolchains/IToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/IToolchain.cs @@ -14,6 +14,6 @@ public interface IToolchain IExecutor Executor { get; } bool IsInProcess { get; } - IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver); + IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/InProcessValidator.cs b/src/BenchmarkDotNet/Toolchains/InProcess/InProcessValidator.cs index e78dcde038..91cf7ea6c1 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/InProcessValidator.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/InProcessValidator.cs @@ -91,7 +91,7 @@ job.Infrastructure.Toolchain is InProcessEmitToolchain /// The instance of validator that DOES fail on error. public static readonly IValidator FailOnError = new InProcessValidator(true); - public static IEnumerable Validate(BenchmarkCase benchmarkCase) + public static async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase) { foreach (var validationError in ValidateJob(benchmarkCase.Job, true)) { @@ -142,7 +142,7 @@ private InProcessValidator(bool failOnErrors) /// Proofs that benchmarks' jobs match the environment. /// The validation parameters. /// Enumerable of validation errors. - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { foreach (var benchmarkWithArguments in validationParameters.Benchmarks.Where(benchmark => benchmark.HasArguments && benchmark.GetToolchain() is InProcessNoEmitToolchain)) yield return new ValidationError(true, "Arguments are not supported by the InProcessNoEmitToolchain, see #687 for more details", benchmarkWithArguments); diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitToolchain.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitToolchain.cs index fac2d39f50..e8887d9ec7 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitToolchain.cs @@ -26,9 +26,9 @@ public InProcessNoEmitToolchain(InProcessNoEmitSettings settings) Executor = new InProcessNoEmitExecutor(settings.ExecuteOnSeparateThread); } - public IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { - foreach (var error in InProcessValidator.Validate(benchmarkCase)) + await foreach (var error in InProcessValidator.ValidateAsync(benchmarkCase)) { yield return error; } diff --git a/src/BenchmarkDotNet/Toolchains/Mono/MonoAotToolchain.cs b/src/BenchmarkDotNet/Toolchains/Mono/MonoAotToolchain.cs index b1e4d44624..154a555c07 100644 --- a/src/BenchmarkDotNet/Toolchains/Mono/MonoAotToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Mono/MonoAotToolchain.cs @@ -20,9 +20,9 @@ public class MonoAotToolchain : Toolchain { } - public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public override async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { - foreach (var validationError in base.Validate(benchmarkCase, resolver)) + await foreach (var validationError in base.ValidateAsync(benchmarkCase, resolver)) { yield return validationError; } diff --git a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMToolChain.cs b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMToolChain.cs index 06ef8ea8c6..f565a4f922 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMToolChain.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMToolChain.cs @@ -29,9 +29,9 @@ public static IToolchain From(NetCoreAppSettings netCoreAppSettings) new Executor(), netCoreAppSettings.CustomDotNetCliPath); - public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public override async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { - foreach (var validationError in base.Validate(benchmarkCase, resolver)) + await foreach (var validationError in base.ValidateAsync(benchmarkCase, resolver)) { yield return validationError; } diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs index 71ad2ade2b..530039653c 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmToolchain.cs @@ -20,9 +20,9 @@ private WasmToolchain(string name, IGenerator generator, IBuilder builder, IExec CustomDotNetCliPath = customDotNetCliPath; } - public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public override async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { - foreach (var validationError in base.Validate(benchmarkCase, resolver)) + await foreach (var validationError in base.ValidateAsync(benchmarkCase, resolver)) { yield return validationError; } diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs index 26ef06a1a7..3657835eef 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs @@ -80,9 +80,9 @@ internal NativeAotToolchain(string displayName, public static string GetExtraArguments(string runtimeIdentifier) => $"-r {runtimeIdentifier}"; - public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public override async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { - foreach (var error in base.Validate(benchmarkCase, resolver)) + await foreach (var error in base.ValidateAsync(benchmarkCase, resolver)) { yield return error; } diff --git a/src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs b/src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs index b52caab5d9..381298d91a 100644 --- a/src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs @@ -22,9 +22,9 @@ public class RoslynToolchain : Toolchain } [PublicAPI] - public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public override async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { - foreach (var validationError in base.Validate(benchmarkCase, resolver)) + await foreach (var validationError in base.ValidateAsync(benchmarkCase, resolver)) { yield return validationError; } diff --git a/src/BenchmarkDotNet/Toolchains/Toolchain.cs b/src/BenchmarkDotNet/Toolchains/Toolchain.cs index 1427a9e1b8..5edf9caccf 100644 --- a/src/BenchmarkDotNet/Toolchains/Toolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Toolchain.cs @@ -29,7 +29,7 @@ public Toolchain(string name, IGenerator generator, IBuilder builder, IExecutor Executor = executor; } - public virtual IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public virtual async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { var runtime = benchmarkCase.Job.ResolveValue(EnvironmentMode.RuntimeCharacteristic, resolver); var jit = benchmarkCase.Job.ResolveValue(EnvironmentMode.JitCharacteristic, resolver); diff --git a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs index a53cab3022..54033153f5 100644 --- a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs +++ b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs @@ -47,7 +47,7 @@ internal static IToolchain GetToolchain(this Runtime runtime, Descriptor? descri case ClrRuntime clrRuntime: bool UseRoslyn() => !isRuntimeExplicit - || runtime.MsBuildMoniker == ClrRuntime.GetTargetOrCurrentVersion(descriptor?.WorkloadMethod.DeclaringType?.Assembly).MsBuildMoniker; + || runtime.MsBuildMoniker == ClrRuntime.GetTargetOrCurrentVersion(descriptor?.Type.Assembly).MsBuildMoniker; if (!preferMsBuildToolchains && RuntimeInformation.IsFullFramework && UseRoslyn()) return RoslynToolchain.Instance; diff --git a/src/BenchmarkDotNet/Validators/BaselineValidator.cs b/src/BenchmarkDotNet/Validators/BaselineValidator.cs index 0068c1517d..25cf3b3fa5 100644 --- a/src/BenchmarkDotNet/Validators/BaselineValidator.cs +++ b/src/BenchmarkDotNet/Validators/BaselineValidator.cs @@ -16,7 +16,7 @@ private BaselineValidator() { } public bool TreatsWarningsAsErrors => true; // it is a must! - public IEnumerable Validate(ValidationParameters input) + public async IAsyncEnumerable ValidateAsync(ValidationParameters input) { var allBenchmarks = input.Benchmarks.ToImmutableArray(); var orderProvider = input.Config.Orderer ?? DefaultOrderer.Instance; diff --git a/src/BenchmarkDotNet/Validators/CompilationValidator.cs b/src/BenchmarkDotNet/Validators/CompilationValidator.cs index d763f48092..13e323e4a5 100644 --- a/src/BenchmarkDotNet/Validators/CompilationValidator.cs +++ b/src/BenchmarkDotNet/Validators/CompilationValidator.cs @@ -7,7 +7,6 @@ using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains; using Microsoft.CodeAnalysis.CSharp; -using BenchmarkDotNet.Attributes; #nullable enable @@ -25,13 +24,13 @@ private CompilationValidator() { } public bool TreatsWarningsAsErrors => true; - public IEnumerable Validate(ValidationParameters validationParameters) + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => ValidateCSharpNaming(validationParameters.Benchmarks) - .Union(ValidateClassModifiers((validationParameters.Benchmarks)) - .Union(ValidateAccessModifiers(validationParameters.Benchmarks)) - .Union(ValidateBindingModifiers(validationParameters.Benchmarks)) - .Union(ValidateMethodImpl(validationParameters.Benchmarks)) - ); + .Union(ValidateClassModifiers(validationParameters.Benchmarks)) + .Union(ValidateAccessModifiers(validationParameters.Benchmarks)) + .Union(ValidateBindingModifiers(validationParameters.Benchmarks)) + .Union(ValidateMethodImpl(validationParameters.Benchmarks)) + .ToAsyncEnumerable(); private static IEnumerable ValidateClassModifiers(IEnumerable benchmarks) { diff --git a/src/BenchmarkDotNet/Validators/CompositeValidator.cs b/src/BenchmarkDotNet/Validators/CompositeValidator.cs index 89b25a68dc..a5017a6ddf 100644 --- a/src/BenchmarkDotNet/Validators/CompositeValidator.cs +++ b/src/BenchmarkDotNet/Validators/CompositeValidator.cs @@ -16,7 +16,7 @@ internal class CompositeValidator : IValidator public bool TreatsWarningsAsErrors => validators.Any(validator => validator.TreatsWarningsAsErrors); - public IEnumerable Validate(ValidationParameters validationParameters) - => validators.SelectMany(validator => validator.Validate(validationParameters)).Distinct(); + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) + => validators.ToAsyncEnumerable().SelectMany(validator => validator.ValidateAsync(validationParameters)).Distinct(); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Validators/ConfigValidator.cs b/src/BenchmarkDotNet/Validators/ConfigValidator.cs index 3b73ae72d4..2e5e0b8c09 100644 --- a/src/BenchmarkDotNet/Validators/ConfigValidator.cs +++ b/src/BenchmarkDotNet/Validators/ConfigValidator.cs @@ -18,7 +18,7 @@ private ConfigValidator() { } public bool TreatsWarningsAsErrors => false; - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { if (validationParameters.Config.GetLoggers().IsEmpty()) { diff --git a/src/BenchmarkDotNet/Validators/DeferredExecutionValidator.cs b/src/BenchmarkDotNet/Validators/DeferredExecutionValidator.cs index fe0ccaa701..b54acffe34 100644 --- a/src/BenchmarkDotNet/Validators/DeferredExecutionValidator.cs +++ b/src/BenchmarkDotNet/Validators/DeferredExecutionValidator.cs @@ -18,7 +18,7 @@ public class DeferredExecutionValidator : IValidator public bool TreatsWarningsAsErrors { get; } - public IEnumerable Validate(ValidationParameters validationParameters) + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => validationParameters.Benchmarks .Where(benchmark => IsDeferredExecution(benchmark.Descriptor.WorkloadMethod.ReturnType)) .Select(benchmark => @@ -26,7 +26,9 @@ public IEnumerable Validate(ValidationParameters validationPara TreatsWarningsAsErrors, $"Benchmark {benchmark.Descriptor.Type.Name}.{benchmark.Descriptor.WorkloadMethod.Name} returns a deferred execution result ({benchmark.Descriptor.WorkloadMethod.ReturnType.GetCorrectCSharpTypeName(false, false)}). " + "You need to either change the method declaration to return a materialized result or consume it on your own. You can use .Consume() extension method to do that.", - benchmark)); + benchmark) + ) + .ToAsyncEnumerable(); private bool IsDeferredExecution(Type returnType) { diff --git a/src/BenchmarkDotNet/Validators/DiagnosersValidator.cs b/src/BenchmarkDotNet/Validators/DiagnosersValidator.cs index 7c6f9abb40..ce38f98cca 100644 --- a/src/BenchmarkDotNet/Validators/DiagnosersValidator.cs +++ b/src/BenchmarkDotNet/Validators/DiagnosersValidator.cs @@ -13,10 +13,11 @@ private DiagnosersValidator() public bool TreatsWarningsAsErrors => true; - public IEnumerable Validate(ValidationParameters validationParameters) + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => validationParameters .Config .GetDiagnosers() - .SelectMany(diagnoser => diagnoser.Validate(validationParameters)); + .ToAsyncEnumerable() + .SelectMany(diagnoser => diagnoser.ValidateAsync(validationParameters)); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Validators/DotNetSdkValidator.cs b/src/BenchmarkDotNet/Validators/DotNetSdkValidator.cs index 8f16547944..d4504b39b0 100644 --- a/src/BenchmarkDotNet/Validators/DotNetSdkValidator.cs +++ b/src/BenchmarkDotNet/Validators/DotNetSdkValidator.cs @@ -37,7 +37,7 @@ public static IEnumerable ValidateFrameworkSdks(BenchmarkCase b { var targetRuntime = benchmark.Job.Environment.HasValue(EnvironmentMode.RuntimeCharacteristic) ? benchmark.Job.Environment.Runtime! - : ClrRuntime.GetTargetOrCurrentVersion(benchmark.Descriptor.WorkloadMethod.DeclaringType!.Assembly); + : ClrRuntime.GetTargetOrCurrentVersion(benchmark.Descriptor.Type.Assembly); var requiredSdkVersion = targetRuntime.RuntimeMoniker.GetRuntimeVersion(); var installedVersionString = cachedFrameworkSdks.Value.FirstOrDefault(); diff --git a/src/BenchmarkDotNet/Validators/ExecutionValidator.cs b/src/BenchmarkDotNet/Validators/ExecutionValidator.cs index 79f3037126..10db465f57 100644 --- a/src/BenchmarkDotNet/Validators/ExecutionValidator.cs +++ b/src/BenchmarkDotNet/Validators/ExecutionValidator.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Running; namespace BenchmarkDotNet.Validators @@ -12,13 +14,14 @@ public class ExecutionValidator : ExecutionValidatorBase private ExecutionValidator(bool failOnError) : base(failOnError) { } - protected override void ExecuteBenchmarks(object benchmarkTypeInstance, IEnumerable benchmarks, List errors) + protected override async ValueTask ExecuteBenchmarksAsync(object benchmarkTypeInstance, IEnumerable benchmarks, List errors) { foreach (var benchmark in benchmarks) { try { - benchmark.Descriptor.WorkloadMethod.Invoke(benchmarkTypeInstance, null); + var result = benchmark.Descriptor.WorkloadMethod.Invoke(benchmarkTypeInstance, null); + await DynamicAwaitHelper.GetOrAwaitResult(result); } catch (Exception ex) { diff --git a/src/BenchmarkDotNet/Validators/ExecutionValidatorBase.cs b/src/BenchmarkDotNet/Validators/ExecutionValidatorBase.cs index b393d2849a..5ea57e3771 100644 --- a/src/BenchmarkDotNet/Validators/ExecutionValidatorBase.cs +++ b/src/BenchmarkDotNet/Validators/ExecutionValidatorBase.cs @@ -22,7 +22,7 @@ protected ExecutionValidatorBase(bool failOnError) public bool TreatsWarningsAsErrors { get; } - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { var errors = new List(); @@ -43,17 +43,20 @@ public IEnumerable Validate(ValidationParameters validationPara continue; } - if (!TryToCallGlobalSetup(benchmarkTypeInstance, errors)) + if (!await TryToCallGlobalSetup(benchmarkTypeInstance, errors)) { continue; } - ExecuteBenchmarks(benchmarkTypeInstance, typeGroup, errors); + await ExecuteBenchmarksAsync(benchmarkTypeInstance, typeGroup, errors); - TryToCallGlobalCleanup(benchmarkTypeInstance, errors); + await TryToCallGlobalCleanup(benchmarkTypeInstance, errors); } - return errors; + foreach (var error in errors) + { + yield return error; + } } private bool TryCreateBenchmarkTypeInstance(Type type, List errors, [NotNullWhen(true)] out object? instance) @@ -75,17 +78,17 @@ private bool TryCreateBenchmarkTypeInstance(Type type, List err } } - private bool TryToCallGlobalSetup(object benchmarkTypeInstance, List errors) + private async ValueTask TryToCallGlobalSetup(object benchmarkTypeInstance, List errors) { - return TryToCallGlobalMethod(benchmarkTypeInstance, errors); + return await TryToCallGlobalMethod(benchmarkTypeInstance, errors); } - private void TryToCallGlobalCleanup(object benchmarkTypeInstance, List errors) + private async ValueTask TryToCallGlobalCleanup(object benchmarkTypeInstance, List errors) { - TryToCallGlobalMethod(benchmarkTypeInstance, errors); + await TryToCallGlobalMethod(benchmarkTypeInstance, errors); } - private bool TryToCallGlobalMethod(object benchmarkTypeInstance, List errors) + private async ValueTask TryToCallGlobalMethod(object benchmarkTypeInstance, List errors) { var methods = benchmarkTypeInstance .GetType() @@ -93,7 +96,7 @@ private bool TryToCallGlobalMethod(object benchmarkTypeInstance, List methodInfo.GetCustomAttributes(false).OfType().Any()) .ToArray(); - if (!methods.Any()) + if (methods.Length == 0) { return true; } @@ -109,9 +112,9 @@ private bool TryToCallGlobalMethod(object benchmarkTypeInstance, List(object benchmarkTypeInstance, List type.Name.Replace("Attribute", string.Empty); - private void TryToGetTaskResult(object? result) - { - if (result == null) - { - return; - } - - AwaitHelper.GetGetResultMethod(result.GetType()) - ?.Invoke(null, new[] { result }); - } - private bool TryToSetParamsFields(object benchmarkTypeInstance, List errors) { var paramFields = benchmarkTypeInstance @@ -249,6 +241,6 @@ protected static string GetDisplayExceptionMessage(Exception ex) return ex?.Message ?? "Unknown error"; } - protected abstract void ExecuteBenchmarks(object benchmarkTypeInstance, IEnumerable benchmarks, List errors); + protected abstract ValueTask ExecuteBenchmarksAsync(object benchmarkTypeInstance, IEnumerable benchmarks, List errors); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Validators/GenericBenchmarksValidator.cs b/src/BenchmarkDotNet/Validators/GenericBenchmarksValidator.cs index 45ef7b5cc5..4ff667e63c 100644 --- a/src/BenchmarkDotNet/Validators/GenericBenchmarksValidator.cs +++ b/src/BenchmarkDotNet/Validators/GenericBenchmarksValidator.cs @@ -11,7 +11,7 @@ public class GenericBenchmarksValidator : IValidator public bool TreatsWarningsAsErrors => false; - public IEnumerable Validate(ValidationParameters validationParameters) + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => validationParameters .Benchmarks .Select(benchmark => benchmark.Descriptor.Type.Assembly) @@ -19,6 +19,7 @@ public IEnumerable Validate(ValidationParameters validationPara .SelectMany(assembly => assembly.GetRunnableBenchmarks()) .SelectMany(GenericBenchmarksBuilder.BuildGenericsIfNeeded) .Where(result => !result.isSuccess) - .Select(result => new ValidationError(false, $"Generic type {result.result.Name} failed to build due to wrong type argument or arguments count, ignoring.")); + .Select(result => new ValidationError(false, $"Generic type {result.result.Name} failed to build due to wrong type argument or arguments count, ignoring.")) + .ToAsyncEnumerable(); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Validators/IValidator.cs b/src/BenchmarkDotNet/Validators/IValidator.cs index a179cb3bbf..f904cc0c42 100644 --- a/src/BenchmarkDotNet/Validators/IValidator.cs +++ b/src/BenchmarkDotNet/Validators/IValidator.cs @@ -6,6 +6,6 @@ public interface IValidator { bool TreatsWarningsAsErrors { get; } - IEnumerable Validate(ValidationParameters validationParameters); + IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters); } } \ No newline at end of file diff --git a/src/BenchmarkDotNet/Validators/JitOptimizationsValidator.cs b/src/BenchmarkDotNet/Validators/JitOptimizationsValidator.cs index 89ee75b6de..ad9cb2b707 100644 --- a/src/BenchmarkDotNet/Validators/JitOptimizationsValidator.cs +++ b/src/BenchmarkDotNet/Validators/JitOptimizationsValidator.cs @@ -15,7 +15,7 @@ public class JitOptimizationsValidator : IValidator public bool TreatsWarningsAsErrors { get; } - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { foreach (var group in validationParameters.Benchmarks.GroupBy(benchmark => benchmark.Descriptor.Type.GetTypeInfo().Assembly)) { diff --git a/src/BenchmarkDotNet/Validators/ParamsAllValuesValidator.cs b/src/BenchmarkDotNet/Validators/ParamsAllValuesValidator.cs index c1b2563fc2..164b8ed027 100644 --- a/src/BenchmarkDotNet/Validators/ParamsAllValuesValidator.cs +++ b/src/BenchmarkDotNet/Validators/ParamsAllValuesValidator.cs @@ -20,14 +20,15 @@ private ParamsAllValuesValidator() { } private const BindingFlags ReflectionFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - public IEnumerable Validate(ValidationParameters input) => + public IAsyncEnumerable ValidateAsync(ValidationParameters input) => input.Benchmarks .Select(benchmark => benchmark.Descriptor.Type) .Distinct() .SelectMany(type => type.GetTypeMembersWithGivenAttribute(ReflectionFlags)) .Distinct() .Select(member => GetErrorOrDefault(member.ParameterType)) - .WhereNotNull(); + .WhereNotNull() + .ToAsyncEnumerable(); private bool IsBool(Type paramType) => paramType == typeof(bool); private bool IsEnum(Type paramType) => paramType.GetTypeInfo().IsEnum; diff --git a/src/BenchmarkDotNet/Validators/ParamsValidator.cs b/src/BenchmarkDotNet/Validators/ParamsValidator.cs index 37dc645af5..95981601f5 100644 --- a/src/BenchmarkDotNet/Validators/ParamsValidator.cs +++ b/src/BenchmarkDotNet/Validators/ParamsValidator.cs @@ -13,12 +13,13 @@ public class ParamsValidator : IValidator public bool TreatsWarningsAsErrors => true; - public IEnumerable Validate(ValidationParameters input) => input.Benchmarks + public IAsyncEnumerable ValidateAsync(ValidationParameters input) => input.Benchmarks .Select(benchmark => benchmark.Descriptor.Type) .Distinct() - .SelectMany(Validate); + .ToAsyncEnumerable() + .SelectMany(ValidateAsync); - private IEnumerable Validate(Type type) + private async IAsyncEnumerable ValidateAsync(Type type) { const BindingFlags reflectionFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy; diff --git a/src/BenchmarkDotNet/Validators/ReturnValueValidator.cs b/src/BenchmarkDotNet/Validators/ReturnValueValidator.cs index 75c44d04db..2778dc1ee2 100644 --- a/src/BenchmarkDotNet/Validators/ReturnValueValidator.cs +++ b/src/BenchmarkDotNet/Validators/ReturnValueValidator.cs @@ -6,6 +6,7 @@ using System.Reflection; using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Parameters; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.InProcess.NoEmit; @@ -22,7 +23,7 @@ public class ReturnValueValidator : ExecutionValidatorBase private ReturnValueValidator(bool failOnError) : base(failOnError) { } - protected override void ExecuteBenchmarks(object benchmarkTypeInstance, IEnumerable benchmarks, List errors) + protected override async System.Threading.Tasks.ValueTask ExecuteBenchmarksAsync(object benchmarkTypeInstance, IEnumerable benchmarks, List errors) { foreach (var parameterGroup in benchmarks.GroupBy(i => i.Parameters, ParameterInstancesEqualityComparer.Instance)) { @@ -34,10 +35,16 @@ protected override void ExecuteBenchmarks(object benchmarkTypeInstance, IEnumera try { InProcessNoEmitRunner.FillMembers(benchmarkTypeInstance, benchmark); - var result = benchmark.Descriptor.WorkloadMethod.Invoke(benchmarkTypeInstance, null)!; + var result = benchmark.Descriptor.WorkloadMethod.Invoke(benchmarkTypeInstance, null); if (benchmark.Descriptor.WorkloadMethod.ReturnType != typeof(void)) - results.Add((benchmark, result)); + { + (var hasResult, result) = await DynamicAwaitHelper.GetOrAwaitResult(result); + if (hasResult) + { + results.Add((benchmark, result!)); + } + } } catch (Exception ex) { diff --git a/src/BenchmarkDotNet/Validators/RunModeValidator.cs b/src/BenchmarkDotNet/Validators/RunModeValidator.cs index ca11c9732c..a4dd3fd352 100644 --- a/src/BenchmarkDotNet/Validators/RunModeValidator.cs +++ b/src/BenchmarkDotNet/Validators/RunModeValidator.cs @@ -15,7 +15,7 @@ private RunModeValidator() { } public bool TreatsWarningsAsErrors => true; - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { var resolver = new CompositeResolver(EnvironmentResolver.Instance, EngineResolver.Instance); // TODO: use specified resolver. foreach (var benchmark in validationParameters.Benchmarks) diff --git a/src/BenchmarkDotNet/Validators/RuntimeValidator.cs b/src/BenchmarkDotNet/Validators/RuntimeValidator.cs index debadf880e..c32d2b2663 100644 --- a/src/BenchmarkDotNet/Validators/RuntimeValidator.cs +++ b/src/BenchmarkDotNet/Validators/RuntimeValidator.cs @@ -17,7 +17,7 @@ private RuntimeValidator() { } public bool TreatsWarningsAsErrors => false; - public IEnumerable Validate(ValidationParameters input) + public async IAsyncEnumerable ValidateAsync(ValidationParameters input) { var allBenchmarks = input.Benchmarks.ToArray(); var nullRuntimeBenchmarks = allBenchmarks.Where(x => x.Job.Environment.Runtime == null).ToArray(); @@ -25,10 +25,9 @@ public IEnumerable Validate(ValidationParameters input) // There is no validation error if all the runtimes are set or if all the runtimes are null. if (allBenchmarks.Length == nullRuntimeBenchmarks.Length) { - return []; + yield break; } - var errors = new List(); foreach (var benchmark in nullRuntimeBenchmarks.Where(x=> !x.GetToolchain().IsInProcess)) { var job = benchmark.Job; @@ -37,8 +36,7 @@ public IEnumerable Validate(ValidationParameters input) : CharacteristicSetPresenter.Display.ToPresentation(job); // Use job text representation instead for auto generated JobId. var message = $"Job({jobText}) doesn't have a Runtime characteristic. It's recommended to specify runtime by using WithRuntime explicitly."; - errors.Add(new ValidationError(false, message)); + yield return new ValidationError(false, message); } - return errors; } } diff --git a/src/BenchmarkDotNet/Validators/SetupCleanupValidator.cs b/src/BenchmarkDotNet/Validators/SetupCleanupValidator.cs index 5617251a66..60c7871170 100644 --- a/src/BenchmarkDotNet/Validators/SetupCleanupValidator.cs +++ b/src/BenchmarkDotNet/Validators/SetupCleanupValidator.cs @@ -14,7 +14,7 @@ private SetupCleanupValidator() { } public bool TreatsWarningsAsErrors => true; // it is a must! - public IEnumerable Validate(ValidationParameters input) + public IAsyncEnumerable ValidateAsync(ValidationParameters input) { var validationErrors = new List(); @@ -28,7 +28,7 @@ public IEnumerable Validate(ValidationParameters input) validationErrors.AddRange(ValidateAttributes(groupByType.Key.Name, allMethods)); } - return validationErrors; + return validationErrors.ToAsyncEnumerable(); } private IEnumerable ValidateAttributes(string benchmarkClassName, IEnumerable allMethods) where T : TargetedAttribute diff --git a/src/BenchmarkDotNet/Validators/ShadowCopyValidator.cs b/src/BenchmarkDotNet/Validators/ShadowCopyValidator.cs index 4d0db84d6c..d1f0927f91 100644 --- a/src/BenchmarkDotNet/Validators/ShadowCopyValidator.cs +++ b/src/BenchmarkDotNet/Validators/ShadowCopyValidator.cs @@ -13,7 +13,7 @@ private ShadowCopyValidator() { } public bool TreatsWarningsAsErrors => false; - public IEnumerable Validate(ValidationParameters validationParameters) + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => validationParameters .Benchmarks .Select(benchmark => benchmark.Descriptor.Type.GetTypeInfo().Assembly) @@ -22,6 +22,8 @@ public IEnumerable Validate(ValidationParameters validationPara .Select( assembly => new ValidationError( false, - $"Assembly {assembly} is located in temp. If you are running benchmarks from xUnit you need to disable shadow copy. It's not supported by design.")); + $"Assembly {assembly} is located in temp. If you are running benchmarks from xUnit you need to disable shadow copy. It's not supported by design.") + ) + .ToAsyncEnumerable(); } } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/Diagnosers/MockInProcessDiagnoser.cs b/tests/BenchmarkDotNet.IntegrationTests/Diagnosers/MockInProcessDiagnoser.cs index 938d0cbdf5..3766335d25 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/Diagnosers/MockInProcessDiagnoser.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/Diagnosers/MockInProcessDiagnoser.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Validators; using System; using System.Collections.Generic; +using System.Linq; using System.Threading; namespace BenchmarkDotNet.IntegrationTests.Diagnosers; @@ -35,7 +36,7 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) { } public IEnumerable ProcessResults(DiagnoserResults results) => []; - public IEnumerable Validate(ValidationParameters validationParameters) => []; + public IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) => AsyncEnumerable.Empty(); InProcessDiagnoserHandlerData IInProcessDiagnoser.GetHandlerData(BenchmarkCase benchmarkCase) => RunMode == RunMode.None diff --git a/tests/BenchmarkDotNet.IntegrationTests/EventProcessorTests.cs b/tests/BenchmarkDotNet.IntegrationTests/EventProcessorTests.cs index d188be01d4..5a4dd45739 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/EventProcessorTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/EventProcessorTests.cs @@ -208,7 +208,7 @@ public class ErrorAllCasesValidator : IValidator { public bool TreatsWarningsAsErrors => true; - public IEnumerable Validate(ValidationParameters validationParameters) + public async IAsyncEnumerable ValidateAsync(ValidationParameters validationParameters) { foreach (var benchmark in validationParameters.Benchmarks) yield return new ValidationError(false, "Mock Validation", benchmark); @@ -221,7 +221,7 @@ public AllUnsupportedToolchain() : base("AllUnsupported", null, null, null) { } - public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + public override async IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) { yield return new ValidationError(true, "Unsupported Benchmark", benchmarkCase); } diff --git a/tests/BenchmarkDotNet.IntegrationTests/JitOptimizationsTests.cs b/tests/BenchmarkDotNet.IntegrationTests/JitOptimizationsTests.cs index 20c452dbfa..ccb3a9dc9e 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/JitOptimizationsTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/JitOptimizationsTests.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using BenchmarkDotNet.Running; using BenchmarkDotNet.Validators; using Xunit; @@ -17,12 +18,12 @@ public JitOptimizationsTests(ITestOutputHelper output) } [Fact] - public void UserGetsWarningWhenNonOptimizedDllIsReferenced() + public async Task UserGetsWarningWhenNonOptimizedDllIsReferenced() { var benchmarksWithNonOptimizedDll = CreateBenchmarks(typeof(DisabledOptimizations.OptimizationsDisabledInCsproj)); - var warnings = JitOptimizationsValidator.DontFailOnError.Validate(benchmarksWithNonOptimizedDll).ToArray(); - var criticalErrors = JitOptimizationsValidator.FailOnError.Validate(benchmarksWithNonOptimizedDll).ToArray(); + var warnings = await JitOptimizationsValidator.DontFailOnError.ValidateAsync(benchmarksWithNonOptimizedDll).ToArrayAsync(); + var criticalErrors = await JitOptimizationsValidator.FailOnError.ValidateAsync(benchmarksWithNonOptimizedDll).ToArrayAsync(); Assert.NotEmpty(warnings); Assert.True(warnings.All(error => error.IsCritical == false)); @@ -31,11 +32,11 @@ public void UserGetsWarningWhenNonOptimizedDllIsReferenced() } [Fact] - public void UserGetsNoWarningWhenOnlyOptimizedDllAreReferenced() + public async Task UserGetsNoWarningWhenOnlyOptimizedDllAreReferenced() { var benchmarksWithOptimizedDll = CreateBenchmarks(typeof(EnabledOptimizations.OptimizationsEnabledInCsproj)); - var warnings = JitOptimizationsValidator.DontFailOnError.Validate(benchmarksWithOptimizedDll).ToArray(); + var warnings = await JitOptimizationsValidator.DontFailOnError.ValidateAsync(benchmarksWithOptimizedDll).ToArrayAsync(); if (warnings.Any()) { diff --git a/tests/BenchmarkDotNet.IntegrationTests/ValidatorsTest.cs b/tests/BenchmarkDotNet.IntegrationTests/ValidatorsTest.cs index 061d594e3a..4baa408196 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/ValidatorsTest.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/ValidatorsTest.cs @@ -58,7 +58,7 @@ private class FailingValidator : IValidator { public bool TreatsWarningsAsErrors => true; - public IEnumerable Validate(ValidationParameters input) + public async IAsyncEnumerable ValidateAsync(ValidationParameters input) { yield return new ValidationError(true, "It just fails"); } diff --git a/tests/BenchmarkDotNet.Tests/Attributes/ParamsAllValuesVerifyTests.cs b/tests/BenchmarkDotNet.Tests/Attributes/ParamsAllValuesVerifyTests.cs index f5d4081a77..4fe922444c 100644 --- a/tests/BenchmarkDotNet.Tests/Attributes/ParamsAllValuesVerifyTests.cs +++ b/tests/BenchmarkDotNet.Tests/Attributes/ParamsAllValuesVerifyTests.cs @@ -35,7 +35,7 @@ public static TheoryData GetBenchmarkTypes() [Theory] [MemberData(nameof(GetBenchmarkTypes))] - public Task BenchmarkShouldProduceSummary(Type benchmarkType) + public async Task BenchmarkShouldProduceSummary(Type benchmarkType) { Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; @@ -47,15 +47,15 @@ public Task BenchmarkShouldProduceSummary(Type benchmarkType) exporter.ExportToLog(summary, logger); var validator = ParamsAllValuesValidator.FailOnError; - var errors = validator.Validate(new ValidationParameters(summary.BenchmarksCases, summary.BenchmarksCases.First().Config)).ToList(); + var errors = await validator.ValidateAsync(new ValidationParameters(summary.BenchmarksCases, summary.BenchmarksCases.First().Config)).ToArrayAsync(); logger.WriteLine(); - logger.WriteLine("Errors: " + errors.Count); + logger.WriteLine("Errors: " + errors.Length); foreach (var error in errors) logger.WriteLineError("* " + error.Message); var settings = VerifyHelper.Create(); settings.UseTextForParameters(benchmarkType.Name); - return Verifier.Verify(logger.GetLog(), settings); + await Verifier.Verify(logger.GetLog(), settings); } public void Dispose() => Thread.CurrentThread.CurrentCulture = initCulture; diff --git a/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs b/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs index 9912fcfb1f..7198fab335 100644 --- a/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs +++ b/tests/BenchmarkDotNet.Tests/Exporters/MarkdownExporterVerifyTests.cs @@ -33,7 +33,7 @@ public static TheoryData GetGroupBenchmarkTypes() [Theory] [MemberData(nameof(GetGroupBenchmarkTypes))] - public Task GroupExporterTest(Type benchmarkType) + public async Task GroupExporterTest(Type benchmarkType) { Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; @@ -45,15 +45,15 @@ public Task GroupExporterTest(Type benchmarkType) exporter.ExportToLog(summary, logger); var validator = BaselineValidator.FailOnError; - var errors = validator.Validate(new ValidationParameters(summary.BenchmarksCases, summary.BenchmarksCases.First().Config)).ToList(); + var errors = await validator.ValidateAsync(new ValidationParameters(summary.BenchmarksCases, summary.BenchmarksCases.First().Config)).ToArrayAsync(); logger.WriteLine(); - logger.WriteLine("Errors: " + errors.Count); + logger.WriteLine("Errors: " + errors.Length); foreach (var error in errors) logger.WriteLineError("* " + error.Message); var settings = VerifyHelper.Create(); settings.UseTextForParameters(benchmarkType.Name); - return Verifier.Verify(logger.GetLog(), settings); + await Verifier.Verify(logger.GetLog(), settings); } public void Dispose() => Thread.CurrentThread.CurrentCulture = initCulture; diff --git a/tests/BenchmarkDotNet.Tests/Mocks/Toolchain/MockToolchain.cs b/tests/BenchmarkDotNet.Tests/Mocks/Toolchain/MockToolchain.cs index 0cf0cd7c3a..af327f401a 100644 --- a/tests/BenchmarkDotNet.Tests/Mocks/Toolchain/MockToolchain.cs +++ b/tests/BenchmarkDotNet.Tests/Mocks/Toolchain/MockToolchain.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Loggers; @@ -19,7 +20,7 @@ public class MockToolchain(Func> measurer) : IT public IBuilder Builder => new MockBuilder(); public IExecutor Executor { get; private set; } = new MockExecutor(measurer); public bool IsInProcess => false; - public IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) => []; + public IAsyncEnumerable ValidateAsync(BenchmarkCase benchmarkCase, IResolver resolver) => AsyncEnumerable.Empty(); public override string ToString() => GetType().Name; diff --git a/tests/BenchmarkDotNet.Tests/Validators/CompilationValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/CompilationValidatorTests.cs index 683e258bbe..1a78b96fdc 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/CompilationValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/CompilationValidatorTests.cs @@ -8,13 +8,14 @@ using BenchmarkDotNet.Validators; using Xunit; using System.Reflection; +using System.Threading.Tasks; namespace BenchmarkDotNet.Tests.Validators { public class CompilationValidatorTests { [Fact] - public void BenchmarkedMethodNameMustNotContainWhitespaces() + public async Task BenchmarkedMethodNameMustNotContainWhitespaces() { Delegate method = BuildDummyMethod("Has Some Whitespaces"); @@ -30,7 +31,7 @@ public void BenchmarkedMethodNameMustNotContainWhitespaces() ) }, config); - var errors = CompilationValidator.FailOnError.Validate(parameters).Select(e => e.Message); + var errors = await CompilationValidator.FailOnError.ValidateAsync(parameters).Select(e => e.Message).ToArrayAsync(); Assert.Contains(errors, s => s.Equals( @@ -38,7 +39,7 @@ public void BenchmarkedMethodNameMustNotContainWhitespaces() } [Fact] - public void BenchmarkedMethodNameMustNotUseCsharpKeywords() + public async Task BenchmarkedMethodNameMustNotUseCsharpKeywords() { Delegate method = BuildDummyMethod("typeof"); @@ -53,7 +54,7 @@ public void BenchmarkedMethodNameMustNotUseCsharpKeywords() config) }, config); - var errors = CompilationValidator.FailOnError.Validate(parameters).Select(e => e.Message); + var errors = await CompilationValidator.FailOnError.ValidateAsync(parameters).Select(e => e.Message).ToArrayAsync(); Assert.Contains(errors, s => s.Equals( @@ -76,9 +77,9 @@ public void BenchmarkedMethodNameMustNotUseCsharpKeywords() [InlineData(typeof(OuterClass.InternalNestedClass), true)] [InlineData(typeof(BenchMarkPublicClass.InternalNestedClass), true)] /* Generics Remaining */ - public void Benchmark_Class_Modifers_Must_Be_Public(Type type, bool hasErrors) + public async Task Benchmark_Class_Modifers_Must_Be_Public(Type type, bool hasErrors) { - var validationErrors = CompilationValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(type)); + var validationErrors = await CompilationValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(type)).ToArrayAsync(); Assert.Equal(hasErrors, validationErrors.Any()); } @@ -86,9 +87,9 @@ public void Benchmark_Class_Modifers_Must_Be_Public(Type type, bool hasErrors) [Theory] [InlineData(typeof(BenchmarkClassWithStaticMethod), true)] [InlineData(typeof(BenchmarkClass), false)] - public void Benchmark_Class_Methods_Must_Be_Non_Static(Type type, bool hasErrors) + public async Task Benchmark_Class_Methods_Must_Be_Non_Static(Type type, bool hasErrors) { - var validationErrors = CompilationValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(type)); + var validationErrors = await CompilationValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(type)).ToArrayAsync(); Assert.Equal(hasErrors, validationErrors.Any()); } @@ -104,14 +105,13 @@ public void Benchmark_Class_Methods_Must_Be_Non_Static(Type type, bool hasErrors [InlineData(typeof(PrivateProtectedNestedClass), true)] [InlineData(typeof(ProtectedInternalClass), true)] [InlineData(typeof(ProtectedInternalClass.ProtectedInternalNestedClass), true)] - public void Benchmark_Class_Generic_Argument_Must_Be_Public(Type type, bool hasErrors) + public async Task Benchmark_Class_Generic_Argument_Must_Be_Public(Type type, bool hasErrors) { // Arrange var constructed = typeof(BenchmarkClass<>).MakeGenericType(type); // Act - var validationErrors = CompilationValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(constructed)) - .ToList(); + var validationErrors = await CompilationValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(constructed)).ToArrayAsync(); // Assert Assert.Equal(hasErrors, validationErrors.Any()); diff --git a/tests/BenchmarkDotNet.Tests/Validators/DeferredExecutionValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/DeferredExecutionValidatorTests.cs index 36a33dc85e..b80b8ab72b 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/DeferredExecutionValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/DeferredExecutionValidatorTests.cs @@ -73,11 +73,11 @@ public class ReturningLazyOfInt [InlineData(typeof(ReturningIQueryable))] [InlineData(typeof(ReturningIQueryableOfInt))] [InlineData(typeof(ReturningLazyOfInt))] - public void DeferredExecutionMeansError(Type returningDeferredExecutionResult) + public async Task DeferredExecutionMeansError(Type returningDeferredExecutionResult) { var benchmarks = BenchmarkConverter.TypeToBenchmarks(returningDeferredExecutionResult); - var validationErrors = DeferredExecutionValidator.FailOnError.Validate(benchmarks).ToArray(); + var validationErrors = await DeferredExecutionValidator.FailOnError.ValidateAsync(benchmarks).ToArrayAsync(); Assert.Equal(5, validationErrors.Count(error => error.IsCritical)); } @@ -107,11 +107,11 @@ public class ReturningDictionary [Theory] [InlineData(typeof(ReturningArray))] [InlineData(typeof(ReturningDictionary))] - public void MaterializedCollectionsAreOk(Type returningMaterializedResult) + public async Task MaterializedCollectionsAreOk(Type returningMaterializedResult) { var benchmarks = BenchmarkConverter.TypeToBenchmarks(returningMaterializedResult); - var validationErrors = DeferredExecutionValidator.FailOnError.Validate(benchmarks).ToArray(); + var validationErrors = await DeferredExecutionValidator.FailOnError.ValidateAsync(benchmarks).ToArrayAsync(); Assert.Empty(validationErrors); } diff --git a/tests/BenchmarkDotNet.Tests/Validators/ExecutionValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/ExecutionValidatorTests.cs index 9b7d8526ea..9408623023 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/ExecutionValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/ExecutionValidatorTests.cs @@ -13,9 +13,9 @@ namespace BenchmarkDotNet.Tests.Validators public class ExecutionValidatorTests { [Fact] - public void FailingConstructorsAreDiscovered() + public async Task FailingConstructorsAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(FailingConstructor))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(FailingConstructor))).ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.StartsWith("Unable to create instance of FailingConstructor", validationErrors.Single().Message); @@ -31,9 +31,9 @@ public void NonThrowing() { } } [Fact] - public void FailingGlobalSetupsAreDiscovered() + public async Task FailingGlobalSetupsAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(FailingGlobalSetup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(FailingGlobalSetup))).ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.StartsWith("Failed to execute [GlobalSetup]", validationErrors.Single().Message); @@ -50,9 +50,9 @@ public void NonThrowing() { } } [Fact] - public void FailingGlobalCleanupsAreDiscovered() + public async Task FailingGlobalCleanupsAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(FailingGlobalCleanup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(FailingGlobalCleanup))).ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.StartsWith("Failed to execute [GlobalCleanup]", validationErrors.Single().Message); @@ -69,9 +69,9 @@ public void NonThrowing() { } } [Fact] - public void MultipleGlobalSetupsAreDiscovered() + public async Task MultipleGlobalSetupsAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(MultipleGlobalSetups))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(MultipleGlobalSetups))).ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.StartsWith("Only single [GlobalSetup] method is allowed per type", validationErrors.Single().Message); @@ -90,9 +90,9 @@ public void NonThrowing() { } } [Fact] - public void MultipleGlobalCleanupsAreDiscovered() + public async Task MultipleGlobalCleanupsAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(MultipleGlobalCleanups))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(MultipleGlobalCleanups))).ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.StartsWith("Only single [GlobalCleanup] method is allowed per type", validationErrors.Single().Message); @@ -111,10 +111,10 @@ public void NonThrowing() { } } [Fact] - public void VirtualGlobalSetupsAreSupported() + public async Task VirtualGlobalSetupsAreSupported() { Assert.False(OverridesGlobalSetup.WasCalled); - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(OverridesGlobalSetup))); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(OverridesGlobalSetup))).ToArrayAsync(); Assert.True(OverridesGlobalSetup.WasCalled); Assert.Empty(validationErrors); @@ -138,10 +138,10 @@ public class OverridesGlobalSetup : BaseClassWithThrowingGlobalSetup } [Fact] - public void VirtualGlobalCleanupsAreSupported() + public async Task VirtualGlobalCleanupsAreSupported() { Assert.False(OverridesGlobalCleanup.WasCalled); - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(OverridesGlobalCleanup))); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(OverridesGlobalCleanup))).ToArrayAsync(); Assert.True(OverridesGlobalCleanup.WasCalled); Assert.Empty(validationErrors); @@ -165,9 +165,9 @@ public class OverridesGlobalCleanup : BaseClassWithThrowingGlobalCleanup } [Fact] - public void NonFailingGlobalSetupsAreOmitted() + public async Task NonFailingGlobalSetupsAreOmitted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(GlobalSetupThatRequiresParamsToBeSetFirst))); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(GlobalSetupThatRequiresParamsToBeSetFirst))).ToArrayAsync(); Assert.Empty(validationErrors); } @@ -190,9 +190,9 @@ public void NonThrowing() { } } [Fact] - public void NonFailingGlobalCleanupsAreOmitted() + public async Task NonFailingGlobalCleanupsAreOmitted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(GlobalCleanupThatRequiresParamsToBeSetFirst))); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(GlobalCleanupThatRequiresParamsToBeSetFirst))).ToArrayAsync(); Assert.Empty(validationErrors); } @@ -215,11 +215,11 @@ public void NonThrowing() { } } [Fact] - public void MissingParamsAttributeThatMakesGlobalSetupsFailAreDiscovered() + public async Task MissingParamsAttributeThatMakesGlobalSetupsFailAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError - .Validate(BenchmarkConverter.TypeToBenchmarks(typeof(FailingGlobalSetupWhichShouldHaveHadParamsForField))) - .ToList(); + var validationErrors = await ExecutionValidator.FailOnError + .ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(FailingGlobalSetupWhichShouldHaveHadParamsForField))) + .ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.StartsWith("Failed to execute [GlobalSetup]", validationErrors.Single().Message); @@ -242,11 +242,11 @@ public void NonThrowing() { } } [Fact] - public void MissingParamsAttributeThatMakesGlobalCleanupsFailAreDiscovered() + public async Task MissingParamsAttributeThatMakesGlobalCleanupsFailAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError - .Validate(BenchmarkConverter.TypeToBenchmarks(typeof(FailingGlobalCleanupWhichShouldHaveHadParamsForField))) - .ToList(); + var validationErrors = await ExecutionValidator.FailOnError + .ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(FailingGlobalCleanupWhichShouldHaveHadParamsForField))) + .ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.StartsWith("Failed to execute [GlobalCleanup]", validationErrors.Single().Message); @@ -269,11 +269,11 @@ public void NonThrowing() { } } [Fact] - public void NonPublicFieldsWithParamsAreDiscovered() + public async Task NonPublicFieldsWithParamsAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError - .Validate(BenchmarkConverter.TypeToBenchmarks(typeof(NonPublicFieldWithParams))) - .ToList(); + var validationErrors = await ExecutionValidator.FailOnError + .ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(NonPublicFieldWithParams))) + .ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.StartsWith("Fields marked with [Params] must be public", validationErrors.Single().Message); @@ -310,9 +310,9 @@ public void NonThrowing() { } } [Fact] - public void NonFailingBenchmarksAreOmitted() + public async Task NonFailingBenchmarksAreOmitted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(NonFailingBenchmark))); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(NonFailingBenchmark))).ToArrayAsync(); Assert.Empty(validationErrors); } @@ -324,9 +324,9 @@ public void NonThrowing() { } } [Fact] - public void FailingBenchmarksAreDiscovered() + public async Task FailingBenchmarksAreDiscovered() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(FailingBenchmark))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(FailingBenchmark))).ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.Contains(validationErrors, error => error.Message.Contains("This benchmark throws")); @@ -339,9 +339,9 @@ public class FailingBenchmark } [Fact] - public void MultipleParamsDoNotMultiplyGlobalSetup() + public async Task MultipleParamsDoNotMultiplyGlobalSetup() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(MultipleParamsAndSingleGlobalSetup))); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(MultipleParamsAndSingleGlobalSetup))).ToArrayAsync(); Assert.Empty(validationErrors); } @@ -360,9 +360,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncTaskGlobalSetupIsExecuted() + public async Task AsyncTaskGlobalSetupIsExecuted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncTaskGlobalSetup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncTaskGlobalSetup))).ToArrayAsync(); Assert.True(AsyncTaskGlobalSetup.WasCalled); Assert.Empty(validationErrors); @@ -385,9 +385,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncTaskGlobalCleanupIsExecuted() + public async Task AsyncTaskGlobalCleanupIsExecuted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncTaskGlobalCleanup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncTaskGlobalCleanup))).ToArrayAsync(); Assert.True(AsyncTaskGlobalCleanup.WasCalled); Assert.Empty(validationErrors); @@ -410,9 +410,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncGenericTaskGlobalSetupIsExecuted() + public async Task AsyncGenericTaskGlobalSetupIsExecuted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericTaskGlobalSetup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericTaskGlobalSetup))).ToArrayAsync(); Assert.True(AsyncGenericTaskGlobalSetup.WasCalled); Assert.Empty(validationErrors); @@ -437,9 +437,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncGenericTaskGlobalCleanupIsExecuted() + public async Task AsyncGenericTaskGlobalCleanupIsExecuted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericTaskGlobalCleanup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericTaskGlobalCleanup))).ToArrayAsync(); Assert.True(AsyncGenericTaskGlobalCleanup.WasCalled); Assert.Empty(validationErrors); @@ -464,9 +464,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncValueTaskGlobalSetupIsExecuted() + public async Task AsyncValueTaskGlobalSetupIsExecuted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncValueTaskGlobalSetup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncValueTaskGlobalSetup))).ToArrayAsync(); Assert.True(AsyncValueTaskGlobalSetup.WasCalled); Assert.Empty(validationErrors); @@ -489,9 +489,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncValueTaskGlobalCleanupIsExecuted() + public async Task AsyncValueTaskGlobalCleanupIsExecuted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncValueTaskGlobalCleanup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncValueTaskGlobalCleanup))).ToArrayAsync(); Assert.True(AsyncValueTaskGlobalCleanup.WasCalled); Assert.Empty(validationErrors); @@ -514,9 +514,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncGenericValueTaskGlobalSetupIsExecuted() + public async Task AsyncGenericValueTaskGlobalSetupIsExecuted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericValueTaskGlobalSetup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericValueTaskGlobalSetup))).ToArrayAsync(); Assert.True(AsyncGenericValueTaskGlobalSetup.WasCalled); Assert.Empty(validationErrors); @@ -541,9 +541,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncGenericValueTaskGlobalCleanupIsExecuted() + public async Task AsyncGenericValueTaskGlobalCleanupIsExecuted() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericValueTaskGlobalCleanup))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericValueTaskGlobalCleanup))).ToArrayAsync(); Assert.True(AsyncGenericValueTaskGlobalCleanup.WasCalled); Assert.Empty(validationErrors); @@ -583,9 +583,9 @@ private class ValueTaskSource : IValueTaskSource, IValueTaskSource } [Fact] - public void AsyncValueTaskBackedByIValueTaskSourceIsAwaitedProperly() + public async Task AsyncValueTaskBackedByIValueTaskSourceIsAwaitedProperly() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncValueTaskSource))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncValueTaskSource))).ToArrayAsync(); Assert.True(AsyncValueTaskSource.WasCalled); Assert.Empty(validationErrors); @@ -614,9 +614,9 @@ public void NonThrowing() { } } [Fact] - public void AsyncGenericValueTaskBackedByIValueTaskSourceIsAwaitedProperly() + public async Task AsyncGenericValueTaskBackedByIValueTaskSourceIsAwaitedProperly() { - var validationErrors = ExecutionValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericValueTaskSource))).ToList(); + var validationErrors = await ExecutionValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(AsyncGenericValueTaskSource))).ToArrayAsync(); Assert.True(AsyncGenericValueTaskSource.WasCalled); Assert.Empty(validationErrors); diff --git a/tests/BenchmarkDotNet.Tests/Validators/ParamsValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/ParamsValidatorTests.cs index c4b4987176..8960cc2a57 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/ParamsValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/ParamsValidatorTests.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using BenchmarkDotNet.Validators; @@ -22,13 +23,13 @@ public ParamsValidatorTests(ITestOutputHelper output) this.output = output; } - private void Check(params string[] messageParts) + private async ValueTask Check(params string[] messageParts) { var typeToBenchmarks = BenchmarkConverter.TypeToBenchmarks(typeof(T)); Assert.NotEmpty(typeToBenchmarks.BenchmarksCases); - var validationErrors = ParamsValidator.FailOnError.Validate(typeToBenchmarks).ToList(); - output.WriteLine("Number of validation errors: " + validationErrors.Count); + var validationErrors = await ParamsValidator.FailOnError.ValidateAsync(typeToBenchmarks).ToArrayAsync(); + output.WriteLine("Number of validation errors: " + validationErrors.Length); foreach (var error in validationErrors) output.WriteLine("* " + error.Message); @@ -41,35 +42,35 @@ private void Check(params string[] messageParts) private const string Pa = "[ParamsAllValues]"; private const string Ps = "[ParamsSource]"; - [Fact] public void Const1Test() => Check(nameof(Const1.Input), "constant", P); - [Fact] public void Const2Test() => Check(nameof(Const2.Input), "constant", Pa); - [Fact] public void Const3Test() => Check(nameof(Const3.Input), "constant", Ps); - [Fact] public void StaticReadonly1Test() => Check(nameof(StaticReadonly1.Input), "readonly", P); - [Fact] public void StaticReadonly2Test() => Check(nameof(StaticReadonly2.Input), "readonly", Pa); - [Fact] public void StaticReadonly3Test() => Check(nameof(StaticReadonly3.Input), "readonly", Ps); - [Fact] public void NonStaticReadonly1Test() => Check(nameof(NonStaticReadonly1.Input), "readonly", P); - [Fact] public void NonStaticReadonly2Test() => Check(nameof(NonStaticReadonly2.Input), "readonly", Pa); - [Fact] public void NonStaticReadonly3Test() => Check(nameof(NonStaticReadonly3.Input), "readonly", Ps); - [Fact] public void FieldMultiple1Test() => Check(nameof(FieldMultiple1.Input), "single attribute", P, Pa); - [Fact] public void FieldMultiple2Test() => Check(nameof(FieldMultiple2.Input), "single attribute", P, Ps); - [Fact] public void FieldMultiple3Test() => Check(nameof(FieldMultiple3.Input), "single attribute", Pa, Ps); - [Fact] public void FieldMultiple4Test() => Check(nameof(FieldMultiple4.Input), "single attribute", P, Pa, Ps); - [Fact] public void PropMultiple1Test() => Check(nameof(PropMultiple1.Input), "single attribute", P, Pa); - [Fact] public void PropMultiple2Test() => Check(nameof(PropMultiple2.Input), "single attribute", P, Ps); - [Fact] public void PropMultiple3Test() => Check(nameof(PropMultiple3.Input), "single attribute", Pa, Ps); - [Fact] public void PropMultiple4Test() => Check(nameof(PropMultiple4.Input), "single attribute", P, Pa, Ps); - [Fact] public void PrivateSetter1Test() => Check(nameof(PrivateSetter1.Input), "setter is not public", P); - [Fact] public void PrivateSetter2Test() => Check(nameof(PrivateSetter2.Input), "setter is not public", Pa); - [Fact] public void PrivateSetter3Test() => Check(nameof(PrivateSetter3.Input), "setter is not public", Ps); - [Fact] public void NoSetter1Test() => Check(nameof(NoSetter1.Input), "no setter", P); - [Fact] public void NoSetter2Test() => Check(nameof(NoSetter2.Input), "no setter", Pa); - [Fact] public void NoSetter3Test() => Check(nameof(NoSetter3.Input), "no setter", Ps); - [Fact] public void InternalField1Test() => Check(nameof(InternalField1.Input), "it's not public", P); - [Fact] public void InternalField2Test() => Check(nameof(InternalField2.Input), "it's not public", Pa); - [Fact] public void InternalField3Test() => Check(nameof(InternalField3.Input), "it's not public", Ps); - [Fact] public void InternalProp1Test() => Check(nameof(InternalProp1.Input), "setter is not public", P); - [Fact] public void InternalProp2Test() => Check(nameof(InternalProp2.Input), "setter is not public", Pa); - [Fact] public void InternalProp3Test() => Check(nameof(InternalProp3.Input), "setter is not public", Ps); + [Fact] public async Task Const1Test() => await Check(nameof(Const1.Input), "constant", P); + [Fact] public async Task Const2Test() => await Check(nameof(Const2.Input), "constant", Pa); + [Fact] public async Task Const3Test() => await Check(nameof(Const3.Input), "constant", Ps); + [Fact] public async Task StaticReadonly1Test() => await Check(nameof(StaticReadonly1.Input), "readonly", P); + [Fact] public async Task StaticReadonly2Test() => await Check(nameof(StaticReadonly2.Input), "readonly", Pa); + [Fact] public async Task StaticReadonly3Test() => await Check(nameof(StaticReadonly3.Input), "readonly", Ps); + [Fact] public async Task NonStaticReadonly1Test() => await Check(nameof(NonStaticReadonly1.Input), "readonly", P); + [Fact] public async Task NonStaticReadonly2Test() => await Check(nameof(NonStaticReadonly2.Input), "readonly", Pa); + [Fact] public async Task NonStaticReadonly3Test() => await Check(nameof(NonStaticReadonly3.Input), "readonly", Ps); + [Fact] public async Task FieldMultiple1Test() => await Check(nameof(FieldMultiple1.Input), "single attribute", P, Pa); + [Fact] public async Task FieldMultiple2Test() => await Check(nameof(FieldMultiple2.Input), "single attribute", P, Ps); + [Fact] public async Task FieldMultiple3Test() => await Check(nameof(FieldMultiple3.Input), "single attribute", Pa, Ps); + [Fact] public async Task FieldMultiple4Test() => await Check(nameof(FieldMultiple4.Input), "single attribute", P, Pa, Ps); + [Fact] public async Task PropMultiple1Test() => await Check(nameof(PropMultiple1.Input), "single attribute", P, Pa); + [Fact] public async Task PropMultiple2Test() => await Check(nameof(PropMultiple2.Input), "single attribute", P, Ps); + [Fact] public async Task PropMultiple3Test() => await Check(nameof(PropMultiple3.Input), "single attribute", Pa, Ps); + [Fact] public async Task PropMultiple4Test() => await Check(nameof(PropMultiple4.Input), "single attribute", P, Pa, Ps); + [Fact] public async Task PrivateSetter1Test() => await Check(nameof(PrivateSetter1.Input), "setter is not public", P); + [Fact] public async Task PrivateSetter2Test() => await Check(nameof(PrivateSetter2.Input), "setter is not public", Pa); + [Fact] public async Task PrivateSetter3Test() => await Check(nameof(PrivateSetter3.Input), "setter is not public", Ps); + [Fact] public async Task NoSetter1Test() => await Check(nameof(NoSetter1.Input), "no setter", P); + [Fact] public async Task NoSetter2Test() => await Check(nameof(NoSetter2.Input), "no setter", Pa); + [Fact] public async Task NoSetter3Test() => await Check(nameof(NoSetter3.Input), "no setter", Ps); + [Fact] public async Task InternalField1Test() => await Check(nameof(InternalField1.Input), "it's not public", P); + [Fact] public async Task InternalField2Test() => await Check(nameof(InternalField2.Input), "it's not public", Pa); + [Fact] public async Task InternalField3Test() => await Check(nameof(InternalField3.Input), "it's not public", Ps); + [Fact] public async Task InternalProp1Test() => await Check(nameof(InternalProp1.Input), "setter is not public", P); + [Fact] public async Task InternalProp2Test() => await Check(nameof(InternalProp2.Input), "setter is not public", Pa); + [Fact] public async Task InternalProp3Test() => await Check(nameof(InternalProp3.Input), "setter is not public", Ps); public class Base { @@ -323,9 +324,9 @@ public class PropMultiple4 : Base #if NET5_0_OR_GREATER - [Fact] public void InitOnly1Test() => Check(nameof(InitOnly1.Input), "init-only", P); - [Fact] public void InitOnly2Test() => Check(nameof(InitOnly2.Input), "init-only", Pa); - [Fact] public void InitOnly3Test() => Check(nameof(InitOnly3.Input), "init-only", Ps); + [Fact] public async Task InitOnly1Test() => await Check(nameof(InitOnly1.Input), "init-only", P); + [Fact] public async Task InitOnly2Test() => await Check(nameof(InitOnly2.Input), "init-only", Pa); + [Fact] public async Task InitOnly3Test() => await Check(nameof(InitOnly3.Input), "init-only", Ps); #pragma warning disable BDN1206 public class InitOnly1 : Base diff --git a/tests/BenchmarkDotNet.Tests/Validators/ReturnValueValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/ReturnValueValidatorTests.cs index 40acc04fdc..72f85d4bd4 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/ReturnValueValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/ReturnValueValidatorTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using BenchmarkDotNet.Validators; @@ -14,9 +15,9 @@ public class ReturnValueValidatorTests private const string ErrorMessagePrefix = "Inconsistent benchmark return values"; [Fact] - public void ThrowingBenchmarksAreDiscovered() + public async Task ThrowingBenchmarksAreDiscovered() { - var validationErrors = ReturnValueValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(ThrowingBenchmark))).ToList(); + var validationErrors = await ReturnValueValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(ThrowingBenchmark))).ToArrayAsync(); Assert.Single(validationErrors); Assert.Contains("Oops, sorry", validationErrors.Single().Message); @@ -29,9 +30,9 @@ public class ThrowingBenchmark } [Fact] - public void InconsistentReturnValuesAreDiscovered() + public async Task InconsistentReturnValuesAreDiscovered() { - var validationErrors = AssertInconsistent(); + var validationErrors = await AssertInconsistent(); Assert.Single(validationErrors); } @@ -45,9 +46,9 @@ public class InconsistentResults } [Fact] - public void NoDuplicateResultsArePrinted() + public async Task NoDuplicateResultsArePrinted() { - var validationErrors = AssertInconsistent(); + var validationErrors = await AssertInconsistent(); Assert.Single(validationErrors); var allInstancesOfFoo = Regex.Matches(validationErrors.Single().Message, @"\bFoo\b"); @@ -65,8 +66,8 @@ public class InconsistentResultsWithMultipleJobs } [Fact] - public void ConsistentReturnValuesAreOmitted() - => AssertConsistent(); + public async Task ConsistentReturnValuesAreOmitted() + => await AssertConsistent(); public class ConsistentResults { @@ -78,8 +79,8 @@ public class ConsistentResults } [Fact] - public void BenchmarksWithOnlyVoidMethodsAreOmitted() - => AssertConsistent(); + public async Task BenchmarksWithOnlyVoidMethodsAreOmitted() + => await AssertConsistent(); public class VoidMethods { @@ -91,8 +92,8 @@ public void Bar() { } } [Fact] - public void VoidMethodsAreIgnored() - => AssertConsistent(); + public async Task VoidMethodsAreIgnored() + => await AssertConsistent(); public class ConsistentResultsWithVoidMethod { @@ -107,8 +108,8 @@ public void Baz() { } } [Fact] - public void ConsistentReturnValuesInParameterGroupAreOmitted() - => AssertConsistent(); + public async Task ConsistentReturnValuesInParameterGroupAreOmitted() + => await AssertConsistent(); public class ConsistentResultsPerParameterGroup { @@ -123,10 +124,10 @@ public class ConsistentResultsPerParameterGroup } [Fact] - public void InconsistentReturnValuesInParameterGroupAreDetected() + public async Task InconsistentReturnValuesInParameterGroupAreDetected() { - var validationErrors = AssertInconsistent(); - Assert.Equal(2, validationErrors.Count); + var validationErrors = await AssertInconsistent(); + Assert.Equal(2, validationErrors.Length); } public class InconsistentResultsPerParameterGroup @@ -142,8 +143,8 @@ public class InconsistentResultsPerParameterGroup } [Fact] - public void ConsistentCollectionsAreOmitted() - => AssertConsistent(); + public async Task ConsistentCollectionsAreOmitted() + => await AssertConsistent(); public class ConsistentCollectionReturnType { @@ -155,8 +156,8 @@ public class ConsistentCollectionReturnType } [Fact] - public void InconsistentCollectionsAreDetected() - => AssertInconsistent(); + public async Task InconsistentCollectionsAreDetected() + => await AssertInconsistent(); public class InconsistentCollectionReturnType { @@ -168,8 +169,8 @@ public class InconsistentCollectionReturnType } [Fact] - public void ConsistentDictionariesAreOmitted() - => AssertConsistent(); + public async Task ConsistentDictionariesAreOmitted() + => await AssertConsistent(); public class ConsistentDictionaryReturnType { @@ -181,8 +182,8 @@ public class ConsistentDictionaryReturnType } [Fact] - public void InconsistentDictionariesAreDetected() - => AssertInconsistent(); + public async Task InconsistentDictionariesAreDetected() + => await AssertInconsistent(); public class InconsistentDictionaryReturnType { @@ -194,8 +195,8 @@ public class InconsistentDictionaryReturnType } [Fact] - public void ConsistentCustomEquatableImplementationIsOmitted() - => AssertConsistent(); + public async Task ConsistentCustomEquatableImplementationIsOmitted() + => await AssertConsistent(); public class ConsistentCustomEquatableReturnType { @@ -207,8 +208,8 @@ public class ConsistentCustomEquatableReturnType } [Fact] - public void InconsistentCustomEquatableImplementationIsDetected() - => AssertInconsistent(); + public async Task InconsistentCustomEquatableImplementationIsDetected() + => await AssertInconsistent(); public class InconsistentCustomEquatableReturnType { @@ -238,8 +239,8 @@ public class CustomEquatableB : IEquatable } [Fact] - public void ConsistentBenchmarksAlteringParameterAreOmitted() - => AssertConsistent(); + public async Task ConsistentBenchmarksAlteringParameterAreOmitted() + => await AssertConsistent(); public class ConsistentAlterParam { @@ -253,16 +254,16 @@ public class ConsistentAlterParam public int Bar() => ++Value; } - private static void AssertConsistent() + private static async Task AssertConsistent() { - var validationErrors = ReturnValueValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(TBenchmark))).ToList(); + var validationErrors = await ReturnValueValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(TBenchmark))).ToArrayAsync(); Assert.Empty(validationErrors); } - private static List AssertInconsistent() + private static async Task AssertInconsistent() { - var validationErrors = ReturnValueValidator.FailOnError.Validate(BenchmarkConverter.TypeToBenchmarks(typeof(TBenchmark))).ToList(); + var validationErrors = await ReturnValueValidator.FailOnError.ValidateAsync(BenchmarkConverter.TypeToBenchmarks(typeof(TBenchmark))).ToArrayAsync(); Assert.NotEmpty(validationErrors); Assert.All(validationErrors, error => Assert.StartsWith(ErrorMessagePrefix, error.Message)); diff --git a/tests/BenchmarkDotNet.Tests/Validators/RuntimeValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/RuntimeValidatorTests.cs index a89333ea31..2e01098498 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/RuntimeValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/RuntimeValidatorTests.cs @@ -6,6 +6,7 @@ using BenchmarkDotNet.Toolchains.CsProj; using BenchmarkDotNet.Validators; using System.Linq; +using System.Threading.Tasks; using Xunit; namespace BenchmarkDotNet.Tests.Validators; @@ -13,7 +14,7 @@ namespace BenchmarkDotNet.Tests.Validators; public class RuntimeValidatorTests { [Fact] - public void SameRuntime_Should_Success() + public async Task SameRuntime_Should_Success() { // Arrange var config = new TestConfig1().CreateImmutableConfig(); @@ -21,14 +22,14 @@ public void SameRuntime_Should_Success() var parameters = new ValidationParameters(runInfo.BenchmarksCases, config); // Act - var errors = RuntimeValidator.DontFailOnError.Validate(parameters).Select(e => e.Message).ToArray(); + var errors = await RuntimeValidator.DontFailOnError.ValidateAsync(parameters).Select(e => e.Message).ToArrayAsync(); // Assert Assert.Empty(errors); } [Fact] - public void NullRuntimeMixed_Should_Failed() + public async Task NullRuntimeMixed_Should_Failed() { // Arrange var config = new TestConfig2().CreateImmutableConfig(); @@ -36,7 +37,7 @@ public void NullRuntimeMixed_Should_Failed() var parameters = new ValidationParameters(runInfo.BenchmarksCases, config); // Act - var errors = RuntimeValidator.DontFailOnError.Validate(parameters).Select(e => e.Message).ToArray(); + var errors = await RuntimeValidator.DontFailOnError.ValidateAsync(parameters).Select(e => e.Message).ToArrayAsync(); // Assert { @@ -50,7 +51,7 @@ public void NullRuntimeMixed_Should_Failed() } [Fact] - public void NotNullRuntimeOnly_Should_Success() + public async Task NotNullRuntimeOnly_Should_Success() { // Arrange var config = new TestConfig3().CreateImmutableConfig(); @@ -58,7 +59,7 @@ public void NotNullRuntimeOnly_Should_Success() var parameters = new ValidationParameters(runInfo.BenchmarksCases, config); // Act - var errors = RuntimeValidator.DontFailOnError.Validate(parameters).Select(e => e.Message).ToArray(); + var errors = await RuntimeValidator.DontFailOnError.ValidateAsync(parameters).Select(e => e.Message).ToArrayAsync(); // Assert Assert.Empty(errors); diff --git a/tests/BenchmarkDotNet.Tests/Validators/SetupCleanupValidatorTests.cs b/tests/BenchmarkDotNet.Tests/Validators/SetupCleanupValidatorTests.cs index 43d6b51f5c..3dfc466f00 100644 --- a/tests/BenchmarkDotNet.Tests/Validators/SetupCleanupValidatorTests.cs +++ b/tests/BenchmarkDotNet.Tests/Validators/SetupCleanupValidatorTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using BenchmarkDotNet.Validators; @@ -16,10 +17,10 @@ [Benchmark] public void Benchmark() { } } [Fact] - public void InvalidGlobalSetupTooManyBlankTargets() + public async Task InvalidGlobalSetupTooManyBlankTargets() { - var validationErrors = SetupCleanupValidator.FailOnError.Validate( - BenchmarkConverter.TypeToBenchmarks(typeof(BlankTargetClass))).ToArray(); + var validationErrors = await SetupCleanupValidator.FailOnError.ValidateAsync( + BenchmarkConverter.TypeToBenchmarks(typeof(BlankTargetClass))).ToArrayAsync(); var count = validationErrors.Count(v => v.IsCritical && v.Message.Contains("[GlobalSetupAttribute]") && v.Message.Contains("Blank")); @@ -35,10 +36,10 @@ [Benchmark] public void Benchmark() { } } [Fact] - public void InvalidGlobalSetupTooManyExplicitTargets() + public async Task InvalidGlobalSetupTooManyExplicitTargets() { - var validationErrors = SetupCleanupValidator.FailOnError.Validate( - BenchmarkConverter.TypeToBenchmarks(typeof(ExplicitTargetClass))).ToArray(); + var validationErrors = await SetupCleanupValidator.FailOnError.ValidateAsync( + BenchmarkConverter.TypeToBenchmarks(typeof(ExplicitTargetClass))).ToArrayAsync(); var count = validationErrors.Count(v => v.IsCritical && v.Message.Contains("[GlobalSetupAttribute]") && v.Message.Contains("Target = Benchmark")); From 6f71ff4a4175ac79332dd1357bb00021bad38ebc Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 24 Jan 2026 22:09:46 -0500 Subject: [PATCH 03/33] Fix `CS0169` and `Could not load file or assembly Microsoft.Bcl.AsyncInterfaces` --- .../BenchmarkDotNet.Samples.csproj | 1 + src/BenchmarkDotNet/Code/CodeGenerator.cs | 54 ++++++++++++++++--- .../Code/DeclarationsProvider.cs | 17 +++--- .../Templates/BenchmarkType.txt | 10 +--- 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj b/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj index 9df0af8dc9..2411c721a6 100644 --- a/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj +++ b/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj @@ -26,6 +26,7 @@ + diff --git a/src/BenchmarkDotNet/Code/CodeGenerator.cs b/src/BenchmarkDotNet/Code/CodeGenerator.cs index 035a95c1ff..4c72542ba9 100644 --- a/src/BenchmarkDotNet/Code/CodeGenerator.cs +++ b/src/BenchmarkDotNet/Code/CodeGenerator.cs @@ -32,13 +32,16 @@ internal static string Generate(BuildPartition buildPartition, CodeGenBenchmarkR { var benchmark = buildInfo.BenchmarkCase; - string benchmarkTypeCode = GetDeclarationsProvider(benchmark) + var declarationsProvider = GetDeclarationsProvider(benchmark); + var extraFields = declarationsProvider.GetExtraFields(); + + string benchmarkTypeCode = declarationsProvider .ReplaceTemplate(new SmartStringBuilder(ResourceHelper.LoadTemplate("BenchmarkType.txt"))) .Replace("$ID$", buildInfo.Id.ToString()) .Replace("$JobSetDefinition$", GetJobsSetDefinition(benchmark)) .Replace("$ParamsContent$", GetParamsContent(benchmark)) .Replace("$ArgumentsDefinition$", GetArgumentsDefinition(benchmark)) - .Replace("$DeclareArgumentFields$", GetDeclareArgumentFields(benchmark)) + .Replace("$DeclareFieldsContainer$", GetDeclareFieldsContainer(benchmark, buildInfo.Id, extraFields)) .Replace("$InitializeArgumentFields$", GetInitializeArgumentFields(benchmark)) .Replace("$EngineFactoryType$", GetEngineFactoryTypeName(benchmark)) .Replace("$RunExtraIteration$", buildInfo.Config.HasExtraIterationDiagnoser(benchmark) ? "true" : "false") @@ -144,11 +147,48 @@ private static string GetArgumentsDefinition(BenchmarkCase benchmarkCase) benchmarkCase.Descriptor.WorkloadMethod.GetParameters() .Select((parameter, index) => $"{GetParameterModifier(parameter)} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index}")); - private static string GetDeclareArgumentFields(BenchmarkCase benchmarkCase) - => string.Join( - Environment.NewLine, - benchmarkCase.Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"public {GetFieldType(parameter.ParameterType, benchmarkCase.Parameters.GetArgument(parameter.Name!)).GetCorrectCSharpTypeName()} __argField{index};")); + private static string GetDeclareFieldsContainer(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, string[] extraFields) + { + var fields = benchmarkCase.Descriptor.WorkloadMethod.GetParameters() + .Select((parameter, index) => $"public {GetFieldType(parameter.ParameterType, benchmarkCase.Parameters.GetArgument(parameter.Name!)).GetCorrectCSharpTypeName()} __argField{index};") + .Concat(extraFields) + .ToArray(); + + // Prevent CS0169 + if (fields.Length == 0) + { + return string.Empty; + } + + // Wrapper struct is necessary because of error CS4004: Cannot await in an unsafe context + var sb = new StringBuilder(); + sb.AppendLine(""" + [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)] + private unsafe struct FieldsContainer + { + """); + foreach (var field in fields) + { + sb.AppendLine($" {field}"); + } + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine($" private global::BenchmarkDotNet.Autogenerated.Runnable_{benchmarkId.Value}.FieldsContainer __fieldsContainer;"); + return sb.ToString(); + } + + /* + + [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)] + private unsafe struct FieldsContainer + { + $DeclareArgumentFields$ + $ExtraFields$ + } + + private global::BenchmarkDotNet.Autogenerated.Runnable_$ID$.FieldsContainer __fieldsContainer; + + */ private static string GetInitializeArgumentFields(BenchmarkCase benchmarkCase) => string.Join( diff --git a/src/BenchmarkDotNet/Code/DeclarationsProvider.cs b/src/BenchmarkDotNet/Code/DeclarationsProvider.cs index 285a70221b..2a37d9b943 100644 --- a/src/BenchmarkDotNet/Code/DeclarationsProvider.cs +++ b/src/BenchmarkDotNet/Code/DeclarationsProvider.cs @@ -26,6 +26,8 @@ internal abstract class DeclarationsProvider(BenchmarkCase benchmark) protected BenchmarkCase Benchmark { get; } = benchmark; protected Descriptor Descriptor => Benchmark.Descriptor; + public abstract string[] GetExtraFields(); + public SmartStringBuilder ReplaceTemplate(SmartStringBuilder smartStringBuilder) { Replace(smartStringBuilder, Descriptor.GlobalSetupMethod, "$GlobalSetupModifiers$", "$GlobalSetupImpl$", false); @@ -98,6 +100,8 @@ protected string GetPassArgumentsDirect() internal class SyncDeclarationsProvider(BenchmarkCase benchmark) : DeclarationsProvider(benchmark) { + public override string[] GetExtraFields() => []; + protected override string PrependExtraGlobalCleanupImpl(string impl) => impl; protected override SmartStringBuilder ReplaceCore(SmartStringBuilder smartStringBuilder) @@ -152,7 +156,6 @@ protected override SmartStringBuilder ReplaceCore(SmartStringBuilder smartString """; return smartStringBuilder - .Replace("$ExtraFields$", string.Empty) .Replace("$CoreImpl$", coreImpl); } @@ -177,6 +180,13 @@ private string GetPassArguments() internal class AsyncDeclarationsProvider(BenchmarkCase benchmark) : DeclarationsProvider(benchmark) { + public override string[] GetExtraFields() => + [ + $"public {typeof(WorkloadContinuerAndValueTaskSource).GetCorrectCSharpTypeName()} workloadContinuerAndValueTaskSource;", + $"public {typeof(IClock).GetCorrectCSharpTypeName()} clock;", + "public long invokeCount;" + ]; + protected override string PrependExtraGlobalCleanupImpl(string impl) => $""" this.__fieldsContainer.workloadContinuerAndValueTaskSource?.Complete(); @@ -263,11 +273,6 @@ private async void __StartWorkload() """; return smartStringBuilder - .Replace("$ExtraFields$", $""" - public {typeof(WorkloadContinuerAndValueTaskSource).GetCorrectCSharpTypeName()} workloadContinuerAndValueTaskSource; - public global::Perfolizer.Horology.IClock clock; - public long invokeCount; - """) .Replace("$CoreImpl$", coreImpl); } diff --git a/src/BenchmarkDotNet/Templates/BenchmarkType.txt b/src/BenchmarkDotNet/Templates/BenchmarkType.txt index 21a4b7b02b..46a0c4e307 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkType.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkType.txt @@ -68,15 +68,7 @@ $ParamsContent$ } - // Necessary because of error CS4004: Cannot await in an unsafe context - [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)] - private unsafe struct FieldsContainer - { - $DeclareArgumentFields$ - $ExtraFields$ - } - - private global::BenchmarkDotNet.Autogenerated.Runnable_$ID$.FieldsContainer __fieldsContainer; + $DeclareFieldsContainer$ private $GlobalSetupModifiers$ global::System.Threading.Tasks.ValueTask __GlobalSetup() { From 4094e330f3179d2b692fabcb31160dc23c40b8c6 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sun, 25 Jan 2026 00:00:10 -0500 Subject: [PATCH 04/33] Add thread-safety and wait-blocking to BenchmarkSynchronizationContext. --- .../BenchmarkSynchronizationContext.cs | 166 +++++++++++++++--- .../Templates/BenchmarkProgram.txt | 7 +- 2 files changed, 143 insertions(+), 30 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs index b53b99de64..a5e8276c30 100644 --- a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs +++ b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs @@ -10,12 +10,40 @@ namespace BenchmarkDotNet.Engines; // Used to ensure async continuations are posted back to the same thread that the benchmark process was started on. [UsedImplicitly] [EditorBrowsable(EditorBrowsableState.Never)] -public sealed class BenchmarkSynchronizationContext : SynchronizationContext, IDisposable +public readonly ref struct BenchmarkSynchronizationContext : IDisposable { - private readonly SynchronizationContext previousContext; + private readonly BenchmarkDotNetSynchronizationContext context; + + private BenchmarkSynchronizationContext(BenchmarkDotNetSynchronizationContext context) + { + this.context = context; + } + + public static BenchmarkSynchronizationContext CreateAndSetCurrent() + { + var context = new BenchmarkDotNetSynchronizationContext(SynchronizationContext.Current); + SynchronizationContext.SetSynchronizationContext(context); + return new(context); + } + + public void Dispose() + => context.Dispose(); + + public void ExecuteUntilComplete(ValueTask valueTask) + => context.ExecuteUntilComplete(valueTask); + + public T ExecuteUntilComplete(ValueTask valueTask) + => context.ExecuteUntilComplete(valueTask); +} + +internal sealed class BenchmarkDotNetSynchronizationContext : SynchronizationContext +{ + private readonly SynchronizationContext? previousContext; private readonly Queue<(SendOrPostCallback d, object? state)> queue = new(); + private bool isDisposed; + volatile private bool isCompleted; - private BenchmarkSynchronizationContext(SynchronizationContext previousContext) + internal BenchmarkDotNetSynchronizationContext(SynchronizationContext? previousContext) { this.previousContext = previousContext; } @@ -24,53 +52,133 @@ public override SynchronizationContext CreateCopy() => this; public override void Post(SendOrPostCallback d, object? state) - => queue.Enqueue((d ?? throw new ArgumentNullException(nameof(d)), state)); + { + if (d is null) throw new ArgumentNullException(nameof(d)); - public static BenchmarkSynchronizationContext CreateAndSetCurrent() + lock (queue) + { + ThrowIfDisposed(); + + queue.Enqueue((d, state)); + Monitor.Pulse(queue); + } + } + + private void ThrowIfDisposed() { - var context = new BenchmarkSynchronizationContext(Current); - SetSynchronizationContext(context); - return context; + if (isDisposed) throw new ObjectDisposedException(nameof(BenchmarkDotNetSynchronizationContext)); } - public void Dispose() - => SetSynchronizationContext(previousContext); + internal void Dispose() + { + lock (queue) + { + ThrowIfDisposed(); + isDisposed = true; - public void ExecuteUntilComplete(ValueTask valueTask) + // Flush any remaining posted callbacks. + while (TryDequeue(out var callbackAndState)) + { + callbackAndState.d(callbackAndState.state); + } + } + SetSynchronizationContext(previousContext); + } + + internal void ExecuteUntilComplete(ValueTask valueTask) { - var spinner = new SpinWait(); - while (!valueTask.IsCompleted) + ThrowIfDisposed(); + + var awaiter = valueTask.GetAwaiter(); + if (valueTask.IsCompleted) { - DoSpin(ref spinner); + awaiter.GetResult(); + return; } - valueTask.GetAwaiter().GetResult(); + + isCompleted = false; + awaiter.UnsafeOnCompleted(OnCompleted); + ExecuteUntilComplete(); + awaiter.GetResult(); } - public T ExecuteUntilComplete(ValueTask valueTask) + internal T ExecuteUntilComplete(ValueTask valueTask) { - var spinner = new SpinWait(); - while (!valueTask.IsCompleted) + ThrowIfDisposed(); + + var awaiter = valueTask.GetAwaiter(); + if (valueTask.IsCompleted) { - DoSpin(ref spinner); + return awaiter.GetResult(); } - return valueTask.GetAwaiter().GetResult(); + + isCompleted = false; + awaiter.UnsafeOnCompleted(OnCompleted); + ExecuteUntilComplete(); + return awaiter.GetResult(); } - private void DoSpin(ref SpinWait spinner) + private void OnCompleted() { - if (queue.Count <= 0) + isCompleted = true; + lock (queue) { + Monitor.Pulse(queue); + } + } + + private void ExecuteUntilComplete() + { + var spinner = new SpinWait(); + while (true) + { + if (TryDequeue(out var callbackAndState)) + { + do + { + callbackAndState.d(callbackAndState.state); + } + while (TryDequeue(out callbackAndState)); + // Reset spinner after any posted callback is executed. + spinner = new(); + } + + if (isCompleted) + { + return; + } + + if (spinner.NextSpinWillYield) + { + // Yield the thread and wait for completion or for a posted callback. + lock (queue) + { + Monitor.Wait(queue); + } + // Reset the spinner. + spinner = new(); + continue; + } + spinner.SpinOnce(); - return; } + } - do + private bool TryDequeue(out (SendOrPostCallback d, object? state) callbackAndState) + { + lock (queue) { - var (d, state) = queue.Dequeue(); - d(state); +#if NETSTANDARD2_0 + if (queue.Count > 0) + { + callbackAndState = queue.Dequeue(); + return true; + } + callbackAndState = default; + return false; +#else + return queue.TryDequeue(out callbackAndState); +#endif } - while (queue.Count > 0); - // Reset spinner after any posted callback is executed. - spinner = new(); } } diff --git a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt index 53e82d78db..34d93c9eba 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt @@ -59,12 +59,17 @@ namespace BenchmarkDotNet.Autogenerated { host.WriteLine("You have not specified benchmark id (an integer) so the first benchmark will be executed."); } - using (global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext benchmarkSynchronizationContext = global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext.CreateAndSetCurrent()) + global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext benchmarkSynchronizationContext = global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext.CreateAndSetCurrent(); + try { global::System.Threading.Tasks.ValueTask runTask; $BenchmarkRunCall$ benchmarkSynchronizationContext.ExecuteUntilComplete(runTask); } + finally + { + benchmarkSynchronizationContext.Dispose(); + } return 0; } catch (global::System.Exception oom) when (oom is global::System.OutOfMemoryException || oom is global::System.Reflection.TargetInvocationException reflection && reflection.InnerException is global::System.OutOfMemoryException) From 3709747b3751722353d3fb5658ad5d0fa6ee5b0c Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 31 Jan 2026 20:54:06 -0500 Subject: [PATCH 05/33] IL emit match behavior of FieldsContainer conditional emit. --- .../Emitters/AsyncCoreEmitter.cs | 2 + .../Emitters/RunnableEmitter.cs | 117 +++++++++--------- 2 files changed, 63 insertions(+), 56 deletions(-) diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncCoreEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncCoreEmitter.cs index d352f9bae1..d13b209e44 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncCoreEmitter.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/AsyncCoreEmitter.cs @@ -24,6 +24,8 @@ private sealed class AsyncCoreEmitter(BuildPartition buildPartition, ModuleBuild private FieldInfo clockField = null!; private FieldInfo invokeCountField = null!; + protected override int GetExtraFieldsCount() => 3; + protected override void EmitExtraFields(TypeBuilder fieldsContainerBuilder) { base.EmitExtraFields(fieldsContainerBuilder); diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs index 0089907b88..c0790193ae 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs @@ -258,72 +258,77 @@ private void EmitRunnableCore() private void EmitFields() { - /* - private unsafe struct FieldsContainer - { - } - */ - var fieldsContainerBuilder = runnableBuilder.DefineNestedType( - "FieldsContainer", - TypeAttributes.NestedPrivate | TypeAttributes.AutoLayout | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, - typeof(ValueType)); - nestedTypeBuilders.Add(fieldsContainerBuilder); - - // Define arg fields - foreach (var parameter in Descriptor.WorkloadMethod.GetParameters()) + var parameters = Descriptor.WorkloadMethod.GetParameters(); + if (parameters.Length + GetExtraFieldsCount() > 0) { - var argValue = benchmark.BenchmarkCase.Parameters.GetArgument(parameter.Name!); - var parameterType = parameter.ParameterType; - - Type argLocalsType; - Type argFieldType; - MethodInfo? opConversion = null; - if (parameterType.IsByRef) - { - argLocalsType = parameterType; - argFieldType = argLocalsType.GetElementType() - ?? throw new InvalidOperationException($"Bug: cannot get field type from {argLocalsType}"); - } - else if (parameterType.IsByRefLike() && argValue.Value != null) - { - argLocalsType = parameterType; - - // Use conversion on load; store passed value - var passedArgType = argValue.Value.GetType(); - opConversion = GetImplicitConversionOpFromTo(passedArgType, argLocalsType) ?? - throw new InvalidOperationException($"Bug: No conversion from {passedArgType} to {argLocalsType}."); - argFieldType = passedArgType; - } - else + /* + private unsafe struct FieldsContainer + { + } + */ + var fieldsContainerBuilder = runnableBuilder.DefineNestedType( + "FieldsContainer", + TypeAttributes.NestedPrivate | TypeAttributes.AutoLayout | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, + typeof(ValueType)); + nestedTypeBuilders.Add(fieldsContainerBuilder); + + // Define arg fields + foreach (var parameter in parameters) { - // No conversion; load ref to arg field; - argLocalsType = parameterType; - argFieldType = parameterType; + var argValue = benchmark.BenchmarkCase.Parameters.GetArgument(parameter.Name!); + var parameterType = parameter.ParameterType; + + Type argLocalsType; + Type argFieldType; + MethodInfo? opConversion = null; + if (parameterType.IsByRef) + { + argLocalsType = parameterType; + argFieldType = argLocalsType.GetElementType() + ?? throw new InvalidOperationException($"Bug: cannot get field type from {argLocalsType}"); + } + else if (parameterType.IsByRefLike() && argValue.Value != null) + { + argLocalsType = parameterType; + + // Use conversion on load; store passed value + var passedArgType = argValue.Value.GetType(); + opConversion = GetImplicitConversionOpFromTo(passedArgType, argLocalsType) + ?? throw new InvalidOperationException($"Bug: No conversion from {passedArgType} to {argLocalsType}."); + argFieldType = passedArgType; + } + else + { + // No conversion; load ref to arg field; + argLocalsType = parameterType; + argFieldType = parameterType; + } + + if (argFieldType.IsByRefLike()) + throw new NotSupportedException($"Passing ref readonly structs by ref is not supported (cannot store {argFieldType} as a class field)."); + + var argField = fieldsContainerBuilder.DefineField( + ArgFieldPrefix + parameter.Position, + argFieldType, + FieldAttributes.Public); + + argFields.Add(new(argField, argLocalsType, opConversion)); } - if (argFieldType.IsByRefLike()) - throw new NotSupportedException( - $"Passing ref readonly structs by ref is not supported (cannot store {argFieldType} as a class field)."); + EmitExtraFields(fieldsContainerBuilder); - var argField = fieldsContainerBuilder.DefineField( - ArgFieldPrefix + parameter.Position, - argFieldType, - FieldAttributes.Public); - - argFields.Add(new(argField, argLocalsType, opConversion)); + // private FieldsContainer __fieldsContainer; + fieldsContainerField = runnableBuilder.DefineField( + FieldsContainerName, + fieldsContainerBuilder, + FieldAttributes.Private); } - EmitExtraFields(fieldsContainerBuilder); - - // private FieldsContainer __fieldsContainer; - fieldsContainerField = runnableBuilder.DefineField( - FieldsContainerName, - fieldsContainerBuilder, - FieldAttributes.Private); - notElevenField = runnableBuilder.DefineField(NotElevenFieldName, typeof(int), FieldAttributes.Public); } + protected virtual int GetExtraFieldsCount() => 0; + protected virtual void EmitExtraFields(TypeBuilder fieldsContainerBuilder) { } private void EmitCtor() From 9242a9df6fdefeac119a1bbd3577d93b46597d40 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 31 Jan 2026 20:54:17 -0500 Subject: [PATCH 06/33] Reduce lock contention. --- .../BenchmarkSynchronizationContext.cs | 92 ++++++++++--------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs index a5e8276c30..a1bdd17a02 100644 --- a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs +++ b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs @@ -1,6 +1,5 @@ using JetBrains.Annotations; using System; -using System.Collections.Generic; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; @@ -39,8 +38,10 @@ public T ExecuteUntilComplete(ValueTask valueTask) internal sealed class BenchmarkDotNetSynchronizationContext : SynchronizationContext { private readonly SynchronizationContext? previousContext; - private readonly Queue<(SendOrPostCallback d, object? state)> queue = new(); - private bool isDisposed; + // Use 2 arrays to reduce lock contention while executing. The common case is only 1 callback will be queued at a time. + private (SendOrPostCallback d, object? state)[] queue = new (SendOrPostCallback d, object? state)[1]; + private (SendOrPostCallback d, object? state)[] executing = new (SendOrPostCallback d, object? state)[1]; + private int queueCount = 0; volatile private bool isCompleted; internal BenchmarkDotNetSynchronizationContext(SynchronizationContext? previousContext) @@ -59,28 +60,38 @@ public override void Post(SendOrPostCallback d, object? state) { ThrowIfDisposed(); - queue.Enqueue((d, state)); + int index = queueCount; + if (++queueCount > queue.Length) + { + Array.Resize(ref queue, queue.Length * 2); + } + queue[index] = (d, state); + Monitor.Pulse(queue); } } - private void ThrowIfDisposed() - { - if (isDisposed) throw new ObjectDisposedException(nameof(BenchmarkDotNetSynchronizationContext)); - } + private void ThrowIfDisposed() => _ = queue ?? throw new ObjectDisposedException(nameof(BenchmarkDotNetSynchronizationContext)); internal void Dispose() { + int count; + (SendOrPostCallback d, object? state)[] executing; lock (queue) { ThrowIfDisposed(); - isDisposed = true; // Flush any remaining posted callbacks. - while (TryDequeue(out var callbackAndState)) - { - callbackAndState.d(callbackAndState.state); - } + count = queueCount; + queueCount = 0; + executing = queue; + queue = null; + } + this.executing = null; + for (int i = 0; i < count; ++i) + { + executing[i].d(executing[i].state); + executing[i] = default; } SetSynchronizationContext(previousContext); } @@ -132,15 +143,26 @@ private void ExecuteUntilComplete() var spinner = new SpinWait(); while (true) { - if (TryDequeue(out var callbackAndState)) + int count; + (SendOrPostCallback d, object? state)[] executing; + lock (queue) + { + count = queueCount; + queueCount = 0; + executing = queue; + queue = this.executing; + } + this.executing = executing; + for (int i = 0; i < count; ++i) + { + executing[i].d(executing[i].state); + executing[i] = default; + } + if (count > 0) { - do - { - callbackAndState.d(callbackAndState.state); - } - while (TryDequeue(out callbackAndState)); // Reset spinner after any posted callback is executed. spinner = new(); + continue; } if (isCompleted) @@ -148,37 +170,19 @@ private void ExecuteUntilComplete() return; } - if (spinner.NextSpinWillYield) + if (!spinner.NextSpinWillYield) { - // Yield the thread and wait for completion or for a posted callback. - lock (queue) - { - Monitor.Wait(queue); - } - // Reset the spinner. - spinner = new(); + spinner.SpinOnce(); continue; } - spinner.SpinOnce(); - } - } - - private bool TryDequeue(out (SendOrPostCallback d, object? state) callbackAndState) - { - lock (queue) - { -#if NETSTANDARD2_0 - if (queue.Count > 0) + // Yield the thread and wait for completion or for a posted callback. + lock (queue) { - callbackAndState = queue.Dequeue(); - return true; + Monitor.Wait(queue); } - callbackAndState = default; - return false; -#else - return queue.TryDequeue(out callbackAndState); -#endif + // Reset the spinner. + spinner = new(); } } } From 7e7e26bc34f651499dd374421e1419d3bee4fe40 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 31 Jan 2026 20:59:39 -0500 Subject: [PATCH 07/33] Simplify argField names and remove unused constants. --- src/BenchmarkDotNet/Code/CodeGenerator.cs | 4 ++-- .../Code/DeclarationsProvider.cs | 4 ++-- .../Emitters/RunnableEmitter.cs | 24 +++++++++---------- .../Emitters/SyncCoreEmitter.cs | 20 ++++++++-------- .../Runnable/RunnableConstants.cs | 7 +----- 5 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/BenchmarkDotNet/Code/CodeGenerator.cs b/src/BenchmarkDotNet/Code/CodeGenerator.cs index 4c72542ba9..a7d586e83a 100644 --- a/src/BenchmarkDotNet/Code/CodeGenerator.cs +++ b/src/BenchmarkDotNet/Code/CodeGenerator.cs @@ -150,7 +150,7 @@ private static string GetArgumentsDefinition(BenchmarkCase benchmarkCase) private static string GetDeclareFieldsContainer(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, string[] extraFields) { var fields = benchmarkCase.Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"public {GetFieldType(parameter.ParameterType, benchmarkCase.Parameters.GetArgument(parameter.Name!)).GetCorrectCSharpTypeName()} __argField{index};") + .Select((parameter, index) => $"public {GetFieldType(parameter.ParameterType, benchmarkCase.Parameters.GetArgument(parameter.Name!)).GetCorrectCSharpTypeName()} argField{index};") .Concat(extraFields) .ToArray(); @@ -194,7 +194,7 @@ private static string GetInitializeArgumentFields(BenchmarkCase benchmarkCase) => string.Join( Environment.NewLine, benchmarkCase.Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"this.__fieldsContainer.__argField{index} = {benchmarkCase.Parameters.GetArgument(parameter.Name!).ToSourceCode()};")); // we init the fields in ctor to provoke all possible allocations and overhead of other type + .Select((parameter, index) => $"this.__fieldsContainer.argField{index} = {benchmarkCase.Parameters.GetArgument(parameter.Name!).ToSourceCode()};")); // we init the fields in ctor to provoke all possible allocations and overhead of other type private static string GetExtraAttributes(Descriptor descriptor) => descriptor.WorkloadMethod.GetCustomAttributes(false).OfType().Any() ? "[System.STAThreadAttribute]" : string.Empty; diff --git a/src/BenchmarkDotNet/Code/DeclarationsProvider.cs b/src/BenchmarkDotNet/Code/DeclarationsProvider.cs index 2a37d9b943..655313a43f 100644 --- a/src/BenchmarkDotNet/Code/DeclarationsProvider.cs +++ b/src/BenchmarkDotNet/Code/DeclarationsProvider.cs @@ -94,7 +94,7 @@ protected string GetPassArgumentsDirect() => string.Join( ", ", Descriptor.WorkloadMethod.GetParameters() - .Select((parameter, index) => $"{CodeGenerator.GetParameterModifier(parameter)} this.__fieldsContainer.__argField{index}") + .Select((parameter, index) => $"{CodeGenerator.GetParameterModifier(parameter)} this.__fieldsContainer.argField{index}") ); } @@ -166,7 +166,7 @@ private string GetLoadArguments() .Select((parameter, index) => { var refModifier = parameter.ParameterType.IsByRef ? "ref" : string.Empty; - return $"{refModifier} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index} = {refModifier} this.__fieldsContainer.__argField{index};"; + return $"{refModifier} {parameter.ParameterType.GetCorrectCSharpTypeName()} arg{index} = {refModifier} this.__fieldsContainer.argField{index};"; }) ); diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs index c0790193ae..5865f0b1f4 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs @@ -344,34 +344,34 @@ private void EmitCtor() protected void EmitLoadArgFieldsForCall(ILGenerator ilBuilder, LocalBuilder? runnableLocal) { /* - // base.InvokeOnceVoid(__fieldsContainer.__argField0, __fieldsContainer.__argField1); + // base.InvokeOnceVoid(__fieldsContainer.argField0, __fieldsContainer.argField1); IL_000b: ldarg.0 IL_000c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer - IL_0011: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::__argField0 + IL_0011: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::argField0 IL_0016: ldarg.0 IL_0017: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer - IL_001c: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::__argField1 + IL_001c: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::argField1 // -or- - // base.InvokeOnceVoid(ref __fieldsContainer.__argField0, ref __fieldsContainer.__argField1); + // base.InvokeOnceVoid(ref __fieldsContainer.argField0, ref __fieldsContainer.argField1); IL_000b: ldarg.0 IL_000c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_2::__fieldsContainer - IL_0011: ldflda bool BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer::__argField0 + IL_0011: ldflda bool BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer::argField0 IL_0016: ldarg.0 IL_0017: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_2::__fieldsContainer - IL_001c: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer::__argField1 + IL_001c: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_2/FieldsContainer::argField1 // -or- (ref struct arg call) - // base.InvokeOnceVoid((Span)__fieldsContainer.__argField0, __fieldsContainer.__argField1); + // base.InvokeOnceVoid((Span)__fieldsContainer.argField0, __fieldsContainer.argField1); IL_000b: ldarg.0 IL_000c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer - IL_0011: ldfld bool[] BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField0 + IL_0011: ldfld bool[] BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::argField0 IL_0016: call valuetype [System.Runtime]System.Span`1 valuetype [System.Runtime]System.Span`1::op_Implicit(!0[]) IL_001b: ldarg.0 IL_001c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer - IL_0021: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField1 + IL_0021: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::argField1 */ foreach (var argFieldInfo in argFields) { @@ -480,14 +480,14 @@ private MethodBuilder EmitForDisassemblyDiagnoserMethod() ilBuilder.Emit(OpCodes.Bne_Un, notElevenLabel); { /* - // base.Simple(__fieldsContainer.__argField0, __fieldsContainer.__argField1); + // base.Simple(__fieldsContainer.argField0, __fieldsContainer.argField1); IL_000a: ldarg.0 IL_000b: ldarg.0 IL_000c: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer - IL_0011: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::__argField0 + IL_0011: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::argField0 IL_0016: ldarg.0 IL_0017: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_1::__fieldsContainer - IL_001c: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::__argField1 + IL_001c: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_1/FieldsContainer::argField1 IL_0021: call instance void [BenchmarkDotNet.IntegrationTests]BenchmarkDotNet.IntegrationTests.ArgumentsTests/WithArguments::Simple(bool, int32) */ if (!workloadMethod.IsStatic) diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SyncCoreEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SyncCoreEmitter.cs index 2fedfcbb2d..91ccdfb4b2 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SyncCoreEmitter.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/SyncCoreEmitter.cs @@ -112,36 +112,36 @@ class [Perfolizer]Perfolizer.Horology.IClock clock private void EmitLoadArgFieldsToLocals(ILGenerator ilBuilder, List argLocals) { /* - // bool _argField = __fieldsContainer.__argField0; + // bool _argField = __fieldsContainer.argField0; IL_0000: ldarg.0 IL_0001: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer - IL_0006: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField0 + IL_0006: ldfld bool BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::argField0 IL_000b: stloc.0 - // int _argField2 = __fieldsContainer.__argField1; + // int _argField2 = __fieldsContainer.argField1; IL_000c: ldarg.0 IL_000d: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer - IL_0012: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField1 + IL_0012: ldfld int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::argField1 IL_0017: stloc.1 // -or- - // ref bool _argField = ref __fieldsContainer.__argField0; + // ref bool _argField = ref __fieldsContainer.argField0; IL_0000: ldarg.0 IL_0001: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer - IL_0006: ldflda bool BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField0 + IL_0006: ldflda bool BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::argField0 IL_000b: stloc.0 - // ref int _argField2 = ref __fieldsContainer.__argField1; + // ref int _argField2 = ref __fieldsContainer.argField1; IL_000c: ldarg.0 IL_000d: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer - IL_0012: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField1 + IL_0012: ldflda int32 BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::argField1 IL_0017: stloc.1 // -or- (ref struct arg call) - // Span arg = __fieldsContainer.__argField0; + // Span arg = __fieldsContainer.argField0; IL_0000: ldarg.0 IL_0001: ldflda valuetype BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer BenchmarkDotNet.Autogenerated.Runnable_0::__fieldsContainer - IL_0006: ldfld int32[] BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::__argField0 + IL_0006: ldfld int32[] BenchmarkDotNet.Autogenerated.Runnable_0/FieldsContainer::argField0 IL_000b: call valuetype [System.Runtime]System.Span`1 valuetype [System.Runtime]System.Span`1::op_Implicit(!0[]) IL_0010: stloc.0 */ diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableConstants.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableConstants.cs index a22e7262da..6d4be8161a 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableConstants.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Runnable/RunnableConstants.cs @@ -6,14 +6,13 @@ internal class RunnableConstants public const string DynamicAssemblySuffix = "Emitted"; public const string EmittedTypePrefix = "BenchmarkDotNet.Autogenerated.Runnable_"; - public const string ArgFieldPrefix = "__argField"; + public const string ArgFieldPrefix = "argField"; public const string ArgParamPrefix = "arg"; public const string FieldsContainerName = "__fieldsContainer"; public const string NotElevenFieldName = "NotEleven"; public const string TrickTheJitCoreMethodName = "__TrickTheJIT__"; - public const string WorkloadImplementationMethodName = "__Workload"; public const string OverheadImplementationMethodName = "__Overhead"; public const string OverheadActionUnrollMethodName = "OverheadActionUnroll"; public const string OverheadActionNoUnrollMethodName = "OverheadActionNoUnroll"; @@ -23,15 +22,11 @@ internal class RunnableConstants public const string InvokeCountParamName = "invokeCount"; public const string ClockParamName = "clock"; - public const string DummyParamName = "_"; - public const string GlobalSetupMethodName = "__GlobalSetup"; public const string GlobalCleanupMethodName = "__GlobalCleanup"; public const string IterationSetupMethodName = "__IterationSetup"; public const string IterationCleanupMethodName = "__IterationCleanup"; - public const string RunMethodName = "Run"; - public const string WorkloadContinuerAndValueTaskSourceFieldName = "workloadContinuerAndValueTaskSource"; public const string ClockFieldName = "clock"; public const string InvokeCountFieldName = "invokeCount"; From 62d575f20d183d0b6e94238d603c6060b15fee09 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 31 Jan 2026 23:53:29 -0500 Subject: [PATCH 08/33] Fix race condition. --- .../BenchmarkSynchronizationContext.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs index a1bdd17a02..e3636b8173 100644 --- a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs +++ b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs @@ -38,6 +38,7 @@ public T ExecuteUntilComplete(ValueTask valueTask) internal sealed class BenchmarkDotNetSynchronizationContext : SynchronizationContext { private readonly SynchronizationContext? previousContext; + private readonly object syncRoot = new(); // Use 2 arrays to reduce lock contention while executing. The common case is only 1 callback will be queued at a time. private (SendOrPostCallback d, object? state)[] queue = new (SendOrPostCallback d, object? state)[1]; private (SendOrPostCallback d, object? state)[] executing = new (SendOrPostCallback d, object? state)[1]; @@ -56,7 +57,7 @@ public override void Post(SendOrPostCallback d, object? state) { if (d is null) throw new ArgumentNullException(nameof(d)); - lock (queue) + lock (syncRoot) { ThrowIfDisposed(); @@ -67,7 +68,7 @@ public override void Post(SendOrPostCallback d, object? state) } queue[index] = (d, state); - Monitor.Pulse(queue); + Monitor.Pulse(syncRoot); } } @@ -77,7 +78,7 @@ internal void Dispose() { int count; (SendOrPostCallback d, object? state)[] executing; - lock (queue) + lock (syncRoot) { ThrowIfDisposed(); @@ -132,9 +133,9 @@ internal T ExecuteUntilComplete(ValueTask valueTask) private void OnCompleted() { isCompleted = true; - lock (queue) + lock (syncRoot) { - Monitor.Pulse(queue); + Monitor.Pulse(syncRoot); } } @@ -145,7 +146,7 @@ private void ExecuteUntilComplete() { int count; (SendOrPostCallback d, object? state)[] executing; - lock (queue) + lock (syncRoot) { count = queueCount; queueCount = 0; @@ -155,8 +156,9 @@ private void ExecuteUntilComplete() this.executing = executing; for (int i = 0; i < count; ++i) { - executing[i].d(executing[i].state); + var (d, state) = executing[i]; executing[i] = default; + d(state); } if (count > 0) { @@ -177,9 +179,9 @@ private void ExecuteUntilComplete() } // Yield the thread and wait for completion or for a posted callback. - lock (queue) + lock (syncRoot) { - Monitor.Wait(queue); + Monitor.Wait(syncRoot); } // Reset the spinner. spinner = new(); From 1e7b41c6d38b5d7e1d241649e466616a6797b37a Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 7 Feb 2026 22:56:09 -0500 Subject: [PATCH 09/33] Use async entry-point in wasm. --- .../Code/CodeGenEntryPointType.cs | 10 ++ src/BenchmarkDotNet/Code/CodeGenerator.cs | 134 ++++++++++++++++-- .../BenchmarkSynchronizationContext.cs | 45 ++---- .../Templates/BenchmarkProgram.txt | 72 +--------- .../Toolchains/GeneratorBase.cs | 5 +- .../Toolchains/MonoWasm/WasmGenerator.cs | 1 + .../CodeGeneratorTests.cs | 2 + 7 files changed, 151 insertions(+), 118 deletions(-) create mode 100644 src/BenchmarkDotNet/Code/CodeGenEntryPointType.cs diff --git a/src/BenchmarkDotNet/Code/CodeGenEntryPointType.cs b/src/BenchmarkDotNet/Code/CodeGenEntryPointType.cs new file mode 100644 index 0000000000..a049cc008f --- /dev/null +++ b/src/BenchmarkDotNet/Code/CodeGenEntryPointType.cs @@ -0,0 +1,10 @@ +namespace BenchmarkDotNet.Code; + +/// +/// Specifies how to generate the entry-point for the benchmark process. +/// +public enum CodeGenEntryPointType +{ + Synchronous, + Asynchronous +} diff --git a/src/BenchmarkDotNet/Code/CodeGenerator.cs b/src/BenchmarkDotNet/Code/CodeGenerator.cs index a7d586e83a..07e34c6c25 100644 --- a/src/BenchmarkDotNet/Code/CodeGenerator.cs +++ b/src/BenchmarkDotNet/Code/CodeGenerator.cs @@ -22,7 +22,7 @@ namespace BenchmarkDotNet.Code { internal static class CodeGenerator { - internal static string Generate(BuildPartition buildPartition, CodeGenBenchmarkRunCallType benchmarkRunCallType) + internal static string Generate(BuildPartition buildPartition, CodeGenEntryPointType entryPointType, CodeGenBenchmarkRunCallType benchmarkRunCallType) { (bool useShadowCopy, string shadowCopyFolderPath) = GetShadowCopySettings(); @@ -55,11 +55,9 @@ internal static string Generate(BuildPartition buildPartition, CodeGenBenchmarkR } string benchmarkProgramContent = new SmartStringBuilder(ResourceHelper.LoadTemplate("BenchmarkProgram.txt")) - .Replace("$ShadowCopyDefines$", useShadowCopy ? "#define SHADOWCOPY" : null).Replace("$ShadowCopyFolderPath$", shadowCopyFolderPath) - .Replace("$ExtraDefines$", buildPartition.IsNetFramework ? "#define NETFRAMEWORK" : string.Empty) - .Replace("$DerivedTypes$", string.Join(Environment.NewLine, benchmarksCode)) - .Replace("$ExtraAttribute$", GetExtraAttributes(buildPartition.RepresentativeBenchmarkCase.Descriptor)) + .Replace("$EntryPoint$", GetEntryPoint(buildPartition, entryPointType, useShadowCopy, shadowCopyFolderPath)) .Replace("$BenchmarkRunCall$", GetBenchmarkRunCall(buildPartition, benchmarkRunCallType)) + .Replace("$DerivedTypes$", string.Join(Environment.NewLine, benchmarksCode)) .ToString(); return benchmarkProgramContent; @@ -196,9 +194,6 @@ private static string GetInitializeArgumentFields(BenchmarkCase benchmarkCase) benchmarkCase.Descriptor.WorkloadMethod.GetParameters() .Select((parameter, index) => $"this.__fieldsContainer.argField{index} = {benchmarkCase.Parameters.GetArgument(parameter.Name!).ToSourceCode()};")); // we init the fields in ctor to provoke all possible allocations and overhead of other type - private static string GetExtraAttributes(Descriptor descriptor) - => descriptor.WorkloadMethod.GetCustomAttributes(false).OfType().Any() ? "[System.STAThreadAttribute]" : string.Empty; - private static string GetEngineFactoryTypeName(BenchmarkCase benchmarkCase) { var factory = benchmarkCase.Job.ResolveValue(InfrastructureMode.EngineFactoryCharacteristic, InfrastructureResolver.Instance)!; @@ -253,16 +248,129 @@ internal static string GetParameterModifier(ParameterInfo parameterInfo) return "ref"; } + private static string GetEntryPoint(BuildPartition buildPartition, CodeGenEntryPointType entryPointType, bool useShadowCopy, string shadowCopyFolderPath) + { + if (entryPointType == CodeGenEntryPointType.Asynchronous) + { + // Only wasm uses async entry-point, we don't need to worry about .Net Framework assembly resolve helper. + // Async entry-points also cannot participate in STAThread, so we ignore that as well. + return """ + public static async global::System.Threading.Tasks.Task Main(global::System.String[] args) + { + return await MainCore(args); + } + """; + } + + string mainImpl = """ + global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext benchmarkSynchronizationContext = global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext.CreateAndSetCurrent(); + try + { + global::System.Threading.Tasks.ValueTask task = MainCore(args); + return benchmarkSynchronizationContext.ExecuteUntilComplete(task); + } + finally + { + benchmarkSynchronizationContext.Dispose(); + } + """; + + if (!buildPartition.IsNetFramework) + { + return $$""" + {{GetSTAThreadAttribute()}} + public static global::System.Int32 Main(global::System.String[] args) + { + {{mainImpl}} + } + """; + } + + return $$""" + {{GetAssemblyResolveHelperClass()}} + + {{GetSTAThreadAttribute()}} + public static global::System.Int32 Main(global::System.String[] args) + { + // this method MUST NOT have any dependencies to BenchmarkDotNet and any other external dlls! + // otherwise if LINQPad's shadow copy is enabled, we will not register for AssemblyLoading event + // before .NET Framework tries to load it for this method + using(new BenchmarkDotNet.Autogenerated.UniqueProgramName.DirtyAssemblyResolveHelper()) + return AfterAssemblyLoadingAttached(args); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + private static global::System.Int32 AfterAssemblyLoadingAttached(global::System.String[] args) + { + {{mainImpl}} + } + """; + + string GetSTAThreadAttribute() + => buildPartition.RepresentativeBenchmarkCase.Descriptor.WorkloadMethod.GetCustomAttributes(false).OfType().Any() + ? "[global::System.STAThread]" + : string.Empty; + + string GetAssemblyResolveHelperClass() + { + string impl = useShadowCopy + // used for LINQPad + ? $$""" + global::System.String guessedPath = global::System.IO.Path.Combine(@"{{shadowCopyFolderPath}}", $"{new global::System.Reflection.AssemblyName(args.Name).Name}.dll"); + return global::System.IO.File.Exists(guessedPath) ? global::System.Reflection.Assembly.LoadFrom(guessedPath) : null; + """ + : """ + global::System.Reflection.AssemblyName fullName = new global::System.Reflection.AssemblyName(args.Name); + global::System.String simpleName = fullName.Name; + + global::System.String guessedPath = global::System.IO.Path.Combine(global::System.AppDomain.CurrentDomain.BaseDirectory, $"{simpleName}.dll"); + + if (!global::System.IO.File.Exists(guessedPath)) + { + global::System.Console.WriteLine($"// Wrong assembly binding redirects for {args.Name}."); + return null; // we can't help, and we also don't call Assembly.Load which if fails comes back here, creates endless loop and causes StackOverflow + } + + // the file is right there, but has most probably different version and there is no assembly binding redirect or there is a wrong one... + // so we just load it and ignore the version mismatch + + // we warn the user about that, in case some Super User want to be aware of that + global::System.Console.WriteLine($"// Wrong assembly binding redirects for {simpleName}, loading it from disk anyway."); + + return global::System.Reflection.Assembly.LoadFrom(guessedPath); + """; + + return $$""" + private sealed class DirtyAssemblyResolveHelper : global::System.IDisposable + { + internal DirtyAssemblyResolveHelper() => global::System.AppDomain.CurrentDomain.AssemblyResolve += HelpTheFrameworkToResolveTheAssembly; + + public void Dispose() => global::System.AppDomain.CurrentDomain.AssemblyResolve -= HelpTheFrameworkToResolveTheAssembly; + + /// + /// according to https://msdn.microsoft.com/en-us/library/ff527268(v=vs.110).aspx + /// "the handler is invoked whenever the runtime fails to bind to an assembly by name." + /// + /// not null when we find it manually, null when we can't help + private global::System.Reflection.Assembly HelpTheFrameworkToResolveTheAssembly(global::System.Object sender, global::System.ResolveEventArgs args) + { + {{impl}} + } + } + """; + } + } + private static string GetBenchmarkRunCall(BuildPartition buildPartition, CodeGenBenchmarkRunCallType runCallType) { if (runCallType == CodeGenBenchmarkRunCallType.Reflection) { // Use reflection to call benchmark's Run method indirectly. return """ - runTask = (global::System.Threading.Tasks.ValueTask) typeof(global::BenchmarkDotNet.Autogenerated.UniqueProgramName).Assembly - .GetType($"BenchmarkDotNet.Autogenerated.Runnable_{id}") - .GetMethod("Run", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.Static) - .Invoke(null, new global::System.Object[] { host, benchmarkName, diagnoserRunMode }); + await (global::System.Threading.Tasks.ValueTask) typeof(global::BenchmarkDotNet.Autogenerated.UniqueProgramName).Assembly + .GetType($"BenchmarkDotNet.Autogenerated.Runnable_{id}") + .GetMethod("Run", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.Static) + .Invoke(null, new global::System.Object[] { host, benchmarkName, diagnoserRunMode }); """; } @@ -272,7 +380,7 @@ private static string GetBenchmarkRunCall(BuildPartition buildPartition, CodeGen foreach (var buildInfo in buildPartition.Benchmarks) { - @switch.AppendLine($"case {buildInfo.Id.Value}: runTask = BenchmarkDotNet.Autogenerated.Runnable_{buildInfo.Id.Value}.Run(host, benchmarkName, diagnoserRunMode); break;"); + @switch.AppendLine($"case {buildInfo.Id.Value}: await BenchmarkDotNet.Autogenerated.Runnable_{buildInfo.Id.Value}.Run(host, benchmarkName, diagnoserRunMode); break;"); } @switch.AppendLine("default: throw new System.NotSupportedException(\"invalid benchmark id\");"); diff --git a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs index e3636b8173..f68ffd4426 100644 --- a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs +++ b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs @@ -28,9 +28,6 @@ public static BenchmarkSynchronizationContext CreateAndSetCurrent() public void Dispose() => context.Dispose(); - public void ExecuteUntilComplete(ValueTask valueTask) - => context.ExecuteUntilComplete(valueTask); - public T ExecuteUntilComplete(ValueTask valueTask) => context.ExecuteUntilComplete(valueTask); } @@ -97,23 +94,6 @@ internal void Dispose() SetSynchronizationContext(previousContext); } - internal void ExecuteUntilComplete(ValueTask valueTask) - { - ThrowIfDisposed(); - - var awaiter = valueTask.GetAwaiter(); - if (valueTask.IsCompleted) - { - awaiter.GetResult(); - return; - } - - isCompleted = false; - awaiter.UnsafeOnCompleted(OnCompleted); - ExecuteUntilComplete(); - awaiter.GetResult(); - } - internal T ExecuteUntilComplete(ValueTask valueTask) { ThrowIfDisposed(); @@ -126,21 +106,7 @@ internal T ExecuteUntilComplete(ValueTask valueTask) isCompleted = false; awaiter.UnsafeOnCompleted(OnCompleted); - ExecuteUntilComplete(); - return awaiter.GetResult(); - } - - private void OnCompleted() - { - isCompleted = true; - lock (syncRoot) - { - Monitor.Pulse(syncRoot); - } - } - private void ExecuteUntilComplete() - { var spinner = new SpinWait(); while (true) { @@ -169,7 +135,7 @@ private void ExecuteUntilComplete() if (isCompleted) { - return; + return awaiter.GetResult(); } if (!spinner.NextSpinWillYield) @@ -187,4 +153,13 @@ private void ExecuteUntilComplete() spinner = new(); } } + + private void OnCompleted() + { + isCompleted = true; + lock (syncRoot) + { + Monitor.Pulse(syncRoot); + } + } } diff --git a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt index 34d93c9eba..a3674d15ee 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt @@ -1,5 +1,3 @@ -$ShadowCopyDefines$ -$ExtraDefines$ // // this file must not be importing any namespaces // we should use full names everywhere to avoid any potential naming conflicts, example: #1007, #778 @@ -22,19 +20,9 @@ namespace BenchmarkDotNet.Autogenerated { public class UniqueProgramName // we need different name than typical "Program" to avoid problems with referencing "Program" types from benchmarked code, #691 { - $ExtraAttribute$ - public static global::System.Int32 Main(global::System.String[] args) - { - // this method MUST NOT have any dependencies to BenchmarkDotNet and any other external dlls! (CoreRT is exception from this rule) - // otherwise if LINQPad's shadow copy is enabled, we will not register for AssemblyLoading event - // before .NET Framework tries to load it for this method -#if NETFRAMEWORK - using(new DirtyAssemblyResolveHelper()) -#endif - return AfterAssemblyLoadingAttached(args); - } + $EntryPoint$ - private static global::System.Int32 AfterAssemblyLoadingAttached(global::System.String[] args) + private static async global::System.Threading.Tasks.ValueTask MainCore(global::System.String[] args) { global::BenchmarkDotNet.Engines.IHost host; // this variable name is used by CodeGenerator.GetBenchmarkRunCall, do NOT change it if (global::BenchmarkDotNet.Engines.AnonymousPipesHost.TryGetFileHandles(args, out global::System.String writeHandle, out global::System.String readHandle)) @@ -59,17 +47,7 @@ namespace BenchmarkDotNet.Autogenerated { host.WriteLine("You have not specified benchmark id (an integer) so the first benchmark will be executed."); } - global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext benchmarkSynchronizationContext = global::BenchmarkDotNet.Engines.BenchmarkSynchronizationContext.CreateAndSetCurrent(); - try - { - global::System.Threading.Tasks.ValueTask runTask; - $BenchmarkRunCall$ - benchmarkSynchronizationContext.ExecuteUntilComplete(runTask); - } - finally - { - benchmarkSynchronizationContext.Dispose(); - } + $BenchmarkRunCall$ return 0; } catch (global::System.Exception oom) when (oom is global::System.OutOfMemoryException || oom is global::System.Reflection.TargetInvocationException reflection && reflection.InnerException is global::System.OutOfMemoryException) @@ -99,49 +77,5 @@ namespace BenchmarkDotNet.Autogenerated } } -#if NETFRAMEWORK - internal class DirtyAssemblyResolveHelper : global::System.IDisposable - { - internal DirtyAssemblyResolveHelper() => global::System.AppDomain.CurrentDomain.AssemblyResolve += HelpTheFrameworkToResolveTheAssembly; - - public void Dispose() => global::System.AppDomain.CurrentDomain.AssemblyResolve -= HelpTheFrameworkToResolveTheAssembly; - - /// - /// according to https://msdn.microsoft.com/en-us/library/ff527268(v=vs.110).aspx - /// "the handler is invoked whenever the runtime fails to bind to an assembly by name." - /// - /// not null when we find it manually, null when can't help - private global::System.Reflection.Assembly HelpTheFrameworkToResolveTheAssembly(global::System.Object sender, global::System.ResolveEventArgs args) - { -#if SHADOWCOPY // used for LINQPad - const global::System.String shadowCopyFolderPath = @"$ShadowCopyFolderPath$"; - - global::System.String guessedPath = global::System.IO.Path.Combine(shadowCopyFolderPath, $"{new global::System.Reflection.AssemblyName(args.Name).Name}.dll"); - - return global::System.IO.File.Exists(guessedPath) ? global::System.Reflection.Assembly.LoadFrom(guessedPath) : null; -#else - global::System.Reflection.AssemblyName fullName = new global::System.Reflection.AssemblyName(args.Name); - global::System.String simpleName = fullName.Name; - - global::System.String guessedPath = global::System.IO.Path.Combine(global::System.AppDomain.CurrentDomain.BaseDirectory, $"{simpleName}.dll"); - - if (!global::System.IO.File.Exists(guessedPath)) - { - global::System.Console.WriteLine($"// Wrong assembly binding redirects for {args.Name}."); - return null; // we can't help, and we also don't call Assembly.Load which if fails comes back here, creates endless loop and causes StackOverflow - } - - // the file is right there, but has most probably different version and there is no assembly binding redirect or there is a wrong one... - // so we just load it and ignore the version mismatch - - // we warn the user about that, in case some Super User want to be aware of that - global::System.Console.WriteLine($"// Wrong assembly binding redirects for {simpleName}, loading it from disk anyway."); - - return global::System.Reflection.Assembly.LoadFrom(guessedPath); -#endif // SHADOWCOPY - } - } -#endif // NETFRAMEWORK - $DerivedTypes$ } diff --git a/src/BenchmarkDotNet/Toolchains/GeneratorBase.cs b/src/BenchmarkDotNet/Toolchains/GeneratorBase.cs index b846af7037..993ff42705 100644 --- a/src/BenchmarkDotNet/Toolchains/GeneratorBase.cs +++ b/src/BenchmarkDotNet/Toolchains/GeneratorBase.cs @@ -15,6 +15,9 @@ namespace BenchmarkDotNet.Toolchains [PublicAPI] public abstract class GeneratorBase : IGenerator { + /// + public CodeGenEntryPointType EntryPointType { get; init; } + /// public CodeGenBenchmarkRunCallType BenchmarkRunCallType { get; init; } @@ -121,7 +124,7 @@ [PublicAPI] protected virtual void GenerateAppConfig(BuildPartition buildPartiti /// You most probably do NOT need to override this method!! /// [PublicAPI] protected virtual void GenerateCode(BuildPartition buildPartition, ArtifactsPaths artifactsPaths) - => File.WriteAllText(artifactsPaths.ProgramCodePath, CodeGenerator.Generate(buildPartition, BenchmarkRunCallType)); + => File.WriteAllText(artifactsPaths.ProgramCodePath, CodeGenerator.Generate(buildPartition, EntryPointType, BenchmarkRunCallType)); protected virtual string GetExecutablePath(string binariesDirectoryPath, string programName) => Path.Combine(binariesDirectoryPath, $"{programName}{GetExecutableExtension()}"); diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index d17ad8c304..b4a3d5ea3b 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -20,6 +20,7 @@ public WasmGenerator(string targetFrameworkMoniker, string cliPath, string packa { CustomRuntimePack = customRuntimePack; MainJS = (targetFrameworkMoniker == "net5.0" || targetFrameworkMoniker == "net6.0") ? "main.js" : "test-main.js"; + EntryPointType = Code.CodeGenEntryPointType.Asynchronous; BenchmarkRunCallType = aot ? Code.CodeGenBenchmarkRunCallType.Direct : Code.CodeGenBenchmarkRunCallType.Reflection; } diff --git a/tests/BenchmarkDotNet.Tests/CodeGeneratorTests.cs b/tests/BenchmarkDotNet.Tests/CodeGeneratorTests.cs index e5f9cbd0d5..bfa00edd24 100644 --- a/tests/BenchmarkDotNet.Tests/CodeGeneratorTests.cs +++ b/tests/BenchmarkDotNet.Tests/CodeGeneratorTests.cs @@ -32,6 +32,7 @@ public void AsyncVoidIsNotSupported(CodeGenBenchmarkRunCallType benchmarkRunCall Assert.Throws(() => CodeGenerator.Generate(new BuildPartition( [new BenchmarkBuildInfo(benchmark, ManualConfig.CreateEmpty().CreateImmutableConfig(), 0, new([]))], BenchmarkRunnerClean.DefaultResolver), + CodeGenEntryPointType.Synchronous, benchmarkRunCallType )); } @@ -52,6 +53,7 @@ public void UsingStatementsInTheAutoGeneratedCodeAreProhibited(CodeGenBenchmarkR var generatedSourceFile = CodeGenerator.Generate(new BuildPartition( [new BenchmarkBuildInfo(benchmark, ManualConfig.CreateEmpty().CreateImmutableConfig(), 0, new([]))], BenchmarkRunnerClean.DefaultResolver), + CodeGenEntryPointType.Synchronous, benchmarkRunCallType ); From db34ae285b137f0c0cf40b537c1038a6d5dbeaf5 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 9 Feb 2026 01:31:03 -0500 Subject: [PATCH 10/33] Made IExecutor.ExecuteAsync implementations truly async. Replaced anonymous pipes with named pipe, and made IHost methods async for true async I/O. --- .../Diagnosers/CompositeDiagnoser.cs | 10 +- .../Engines/AnonymousPipesHost.cs | 88 -------------- src/BenchmarkDotNet/Engines/Engine.cs | 24 ++-- src/BenchmarkDotNet/Engines/HostExtensions.cs | 13 +- src/BenchmarkDotNet/Engines/IHost.cs | 27 +++-- src/BenchmarkDotNet/Engines/NamedPipeHost.cs | 113 ++++++++++++++++++ .../Engines/NoAcknowledgementConsoleHost.cs | 44 +++---- src/BenchmarkDotNet/Engines/RunResults.cs | 14 ++- src/BenchmarkDotNet/Loggers/Broker.cs | 49 +++----- src/BenchmarkDotNet/Running/BenchmarkId.cs | 4 +- .../Running/BenchmarkRunnerDirty.cs | 21 ---- .../Running/BenchmarkSwitcher.cs | 9 +- .../Templates/BenchmarkProgram.txt | 31 +++-- .../Templates/BenchmarkType.txt | 18 +-- .../Toolchains/DotNetCli/DotNetCliExecutor.cs | 19 ++- src/BenchmarkDotNet/Toolchains/Executor.cs | 22 ++-- .../InProcess/Emit/InProcessEmitExecutor.cs | 5 +- .../InProcess/Emit/InProcessEmitRunner.cs | 40 +++---- .../Toolchains/InProcess/InProcessHost.cs | 26 ++-- .../NoEmit/InProcessNoEmitExecutor.cs | 2 - .../InProcess/NoEmit/InProcessNoEmitRunner.cs | 40 +++---- .../Validators/ValidationErrorReporter.cs | 26 ++-- 22 files changed, 310 insertions(+), 335 deletions(-) delete mode 100644 src/BenchmarkDotNet/Engines/AnonymousPipesHost.cs create mode 100644 src/BenchmarkDotNet/Engines/NamedPipeHost.cs diff --git a/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs index bed433788d..50df371aa2 100644 --- a/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs @@ -4,7 +4,9 @@ using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using BenchmarkDotNet.Analysers; +using BenchmarkDotNet.Attributes.CompilerServices; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Loggers; @@ -70,12 +72,12 @@ public void DeserializeResults(int index, BenchmarkCase benchmarkCase, string re => InProcessDiagnosers[index].DeserializeResults(benchmarkCase, results); } + [AggressivelyOptimizeMethods] [UsedImplicitly] [EditorBrowsable(EditorBrowsableState.Never)] public sealed class CompositeInProcessDiagnoserHandler(IReadOnlyList routers, IHost host, RunMode runMode, InProcessDiagnoserActionArgs parameters) { - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - public void Handle(BenchmarkSignal signal) + public async ValueTask HandleAsync(BenchmarkSignal signal) { if (runMode == RunMode.None) { @@ -107,10 +109,10 @@ public void Handle(BenchmarkSignal signal) // Ideally we would simply use results.Length, write it directly to host, then the host reads the exact count of chars. // But WasmExecutor does not use Broker, and reads all output, so we need to instead use line count and prepend every line with CompositeInProcessDiagnoser.ResultsKey. var resultsLines = results.Split(["\r\n", "\r", "\n"], StringSplitOptions.None); - host.WriteLine($"{CompositeInProcessDiagnoser.HeaderKey} {router.index} {resultsLines.Length}"); + await host.WriteLineAsync($"{CompositeInProcessDiagnoser.HeaderKey} {router.index} {resultsLines.Length}"); foreach (var line in resultsLines) { - host.WriteLine($"{CompositeInProcessDiagnoser.ResultsKey} {line}"); + await host.WriteLineAsync($"{CompositeInProcessDiagnoser.ResultsKey} {line}"); } } } diff --git a/src/BenchmarkDotNet/Engines/AnonymousPipesHost.cs b/src/BenchmarkDotNet/Engines/AnonymousPipesHost.cs deleted file mode 100644 index d7503707c4..0000000000 --- a/src/BenchmarkDotNet/Engines/AnonymousPipesHost.cs +++ /dev/null @@ -1,88 +0,0 @@ -using BenchmarkDotNet.Validators; -using System; -using System.IO; -using System.Text; -using Microsoft.Win32.SafeHandles; -using JetBrains.Annotations; -using BenchmarkDotNet.Portability; -using System.Runtime.CompilerServices; - -#nullable enable - -namespace BenchmarkDotNet.Engines -{ - public class AnonymousPipesHost : IHost - { - internal const string AnonymousPipesDescriptors = "--anonymousPipes"; - internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - - private readonly StreamWriter outWriter; - private readonly StreamReader inReader; - - public AnonymousPipesHost(string writHandle, string readHandle) - { - outWriter = new StreamWriter(OpenAnonymousPipe(writHandle, FileAccess.Write), UTF8NoBOM); - // Flush the data to the Stream after each write, otherwise the host process will wait for input endlessly! - outWriter.AutoFlush = true; - inReader = new StreamReader(OpenAnonymousPipe(readHandle, FileAccess.Read), UTF8NoBOM, detectEncodingFromByteOrderMarks: false); - } - - private Stream OpenAnonymousPipe(string fileHandle, FileAccess access) - => new FileStream(new SafeFileHandle(new IntPtr(int.Parse(fileHandle)), ownsHandle: true), access, bufferSize: 1); - - public void Dispose() - { - outWriter.Dispose(); - inReader.Dispose(); - } - - public void Write(string message) => outWriter.Write(message); - - public void WriteLine() => outWriter.WriteLine(); - - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - public void WriteLine(string message) => outWriter.WriteLine(message); - - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - public void SendSignal(HostSignal hostSignal) - { - if (hostSignal == HostSignal.AfterAll) - { - // Before the last signal is reported and the benchmark process exits, - // add an artificial sleep to increase the chance of host process reading all std output. - System.Threading.Thread.Sleep(1); - } - - WriteLine(Engine.Signals.ToMessage(hostSignal)); - - // read the response from Parent process, make the communication blocking - string? acknowledgment = inReader.ReadLine(); - if (acknowledgment != Engine.Signals.Acknowledgment - && !(acknowledgment is null && hostSignal == HostSignal.AfterAll)) // an early EOF, but still valid - { - throw new NotSupportedException($"Unknown Acknowledgment: {acknowledgment}"); - } - } - - public void SendError(string message) => outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); - - public void ReportResults(RunResults runResults) => runResults.Print(outWriter); - - [PublicAPI] // called from generated code - public static bool TryGetFileHandles(string[] args, out string? writeHandle, out string? readHandle) - { - for (int i = 0; i < args.Length; i++) - { - if (args[i] == AnonymousPipesDescriptors) - { - writeHandle = args[i + 1]; // IndexOutOfRangeException means a bug (incomplete data) - readHandle = args[i + 2]; - return true; - } - } - - writeHandle = readHandle = null; - return false; - } - } -} diff --git a/src/BenchmarkDotNet/Engines/Engine.cs b/src/BenchmarkDotNet/Engines/Engine.cs index 83914db117..6184ab0756 100644 --- a/src/BenchmarkDotNet/Engines/Engine.cs +++ b/src/BenchmarkDotNet/Engines/Engine.cs @@ -88,7 +88,7 @@ public async ValueTask RunAsync() // We only catch if the benchmark threw to not overwrite the exception. #1045 catch (Exception e) when (didThrow) { - Host.SendError($"Exception during GlobalCleanup!{Environment.NewLine}{e}"); + await Host.SendErrorAsync($"Exception during GlobalCleanup!{Environment.NewLine}{e}"); } } } @@ -108,8 +108,8 @@ private async ValueTask RunCore() { if (stage.Stage == IterationStage.Actual && stage.Mode == IterationMode.Workload) { - Host.BeforeMainRun(); - Parameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.BeforeActualRun); + await Host.BeforeMainRunAsync(); + await Parameters.InProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.BeforeActualRun); } var stageMeasurements = stage.GetMeasurementList(); @@ -147,19 +147,19 @@ private async ValueTask RunCore() // Results var measurement = new Measurement(0, iterationData.mode, iterationData.stage, iterationData.index, totalOperations, clockSpan.GetNanoseconds()); - Host.WriteLine(measurement.ToString()); + await Host.WriteLineAsync(measurement.ToString()); stageMeasurements.Add(measurement); // Actual Workload is always the last stage, so we use the same data to run extra stats. extraIterationData = iterationData; } measurements.AddRange(stageMeasurements); - Host.WriteLine(); + await Host.WriteLineAsync(); if (stage.Stage == IterationStage.Actual && stage.Mode == IterationMode.Workload) { - Host.AfterMainRun(); - Parameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterActualRun); + await Host.AfterMainRunAsync(); + await Parameters.InProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.AfterActualRun); } } @@ -172,8 +172,8 @@ private async ValueTask RunCore() await extraIterationData.setupAction!(); // we run iteration setup first, so even if it allocates, it is not included in the results - Host.SendSignal(HostSignal.BeforeExtraIteration); - Parameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.BeforeExtraIteration); + await Host.SendSignalAsync(HostSignal.BeforeExtraIteration); + await Parameters.InProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.BeforeExtraIteration); // GC collect before measuring allocations. ForceGcCollect(); @@ -190,14 +190,14 @@ private async ValueTask RunCore() (gcStats, clockSpan) = await MeasureWithGc(extraIterationData.workloadAction!, extraIterationData.invokeCount / extraIterationData.unrollFactor); } - Parameters.InProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterExtraIteration); - Host.SendSignal(HostSignal.AfterExtraIteration); + await Parameters.InProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.AfterExtraIteration); + await Host.SendSignalAsync(HostSignal.AfterExtraIteration); await extraIterationData.cleanupAction!(); // we run iteration cleanup after diagnosers are complete. var totalOperations = extraIterationData.invokeCount * Parameters.OperationsPerInvoke; var measurement = new Measurement(0, IterationMode.Workload, IterationStage.Extra, 1, totalOperations, clockSpan.GetNanoseconds()); - Host.WriteLine(measurement.ToString()); + await Host.WriteLineAsync(measurement.ToString()); workGcHasDone = gcStats.WithTotalOperations(totalOperations); measurements.Add(measurement); } diff --git a/src/BenchmarkDotNet/Engines/HostExtensions.cs b/src/BenchmarkDotNet/Engines/HostExtensions.cs index 41272610a4..5e610705c9 100644 --- a/src/BenchmarkDotNet/Engines/HostExtensions.cs +++ b/src/BenchmarkDotNet/Engines/HostExtensions.cs @@ -1,19 +1,20 @@ using JetBrains.Annotations; +using System.Threading.Tasks; namespace BenchmarkDotNet.Engines { public static class HostExtensions { [StringFormatMethod("messageFormat")] - public static void WriteLine(this IHost host, string messageFormat, params object[] args) - => host.WriteLine(string.Format(messageFormat, args)); + public static ValueTask WriteLineAsync(this IHost host, string messageFormat, params object[] args) + => host.WriteLineAsync(string.Format(messageFormat, args)); - public static void BeforeAnythingElse(this IHost host) => host.SendSignal(HostSignal.BeforeAnythingElse); + public static ValueTask BeforeAnythingElseAsync(this IHost host) => host.SendSignalAsync(HostSignal.BeforeAnythingElse); - public static void BeforeMainRun(this IHost host) => host.SendSignal(HostSignal.BeforeActualRun); + public static ValueTask BeforeMainRunAsync(this IHost host) => host.SendSignalAsync(HostSignal.BeforeActualRun); - public static void AfterMainRun(this IHost host) => host.SendSignal(HostSignal.AfterActualRun); + public static ValueTask AfterMainRunAsync(this IHost host) => host.SendSignalAsync(HostSignal.AfterActualRun); - public static void AfterAll(this IHost host) => host.SendSignal(HostSignal.AfterAll); + public static ValueTask AfterAllAsync(this IHost host) => host.SendSignalAsync(HostSignal.AfterAll); } } diff --git a/src/BenchmarkDotNet/Engines/IHost.cs b/src/BenchmarkDotNet/Engines/IHost.cs index e57ee2172b..5cad8437a9 100644 --- a/src/BenchmarkDotNet/Engines/IHost.cs +++ b/src/BenchmarkDotNet/Engines/IHost.cs @@ -1,18 +1,19 @@ -using System; -using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using System; +using System.ComponentModel; +using System.Threading.Tasks; -namespace BenchmarkDotNet.Engines +namespace BenchmarkDotNet.Engines; + +[UsedImplicitly] +[EditorBrowsable(EditorBrowsableState.Never)] +public interface IHost : IDisposable { - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public interface IHost : IDisposable - { - void Write(string message); - void WriteLine(); - void WriteLine(string message); + ValueTask WriteLineAsync(); + ValueTask WriteLineAsync(string message); - void SendSignal(HostSignal hostSignal); - void SendError(string message); + ValueTask SendSignalAsync(HostSignal hostSignal); + ValueTask SendErrorAsync(string message); - void ReportResults(RunResults runResults); - } + ValueTask ReportResultsAsync(RunResults runResults); } diff --git a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs b/src/BenchmarkDotNet/Engines/NamedPipeHost.cs new file mode 100644 index 0000000000..5848f706a2 --- /dev/null +++ b/src/BenchmarkDotNet/Engines/NamedPipeHost.cs @@ -0,0 +1,113 @@ +using BenchmarkDotNet.Attributes.CompilerServices; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Validators; +using JetBrains.Annotations; +using System; +using System.ComponentModel; +using System.IO; +using System.IO.Pipes; +using System.Text; +using System.Threading.Tasks; + +#nullable enable + +namespace BenchmarkDotNet.Engines; + +[AggressivelyOptimizeMethods] +[UsedImplicitly] +[EditorBrowsable(EditorBrowsableState.Never)] +public class NamedPipeHost : IHost +{ + internal const string PipeNameDescriptor = "--pipeName"; + internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + + private readonly NamedPipeClientStream pipe; + private readonly StreamWriter outWriter; + private readonly StreamReader inReader; + + public NamedPipeHost(NamedPipeClientStream pipe) + { + this.pipe = pipe; + // Flush the data to the Stream after each write, otherwise the host process will wait for input endlessly! + outWriter = new(pipe, UTF8NoBOM, bufferSize: 1024, leaveOpen: true) { AutoFlush = true }; + inReader = new(pipe, UTF8NoBOM, detectEncodingFromByteOrderMarks: false, bufferSize: 1024, leaveOpen: true); + } + + public void Dispose() + { + outWriter.Dispose(); + inReader.Dispose(); + pipe.Dispose(); + } + + public async ValueTask WriteAsync(string message) + => await outWriter.WriteAsync(message); + + public async ValueTask WriteLineAsync() + => await outWriter.WriteLineAsync(); + + public async ValueTask WriteLineAsync(string message) + => await outWriter.WriteLineAsync(message); + + public async ValueTask SendSignalAsync(HostSignal hostSignal) + { + if (hostSignal == HostSignal.AfterAll) + { + // Before the last signal is reported and the benchmark process exits, + // add an artificial sleep to increase the chance of host process reading all std output. + System.Threading.Thread.Sleep(1); + } + + await WriteLineAsync(Engine.Signals.ToMessage(hostSignal)); + + // Read the response from Parent process. + string? acknowledgment = await inReader.ReadLineAsync(); + if (acknowledgment != Engine.Signals.Acknowledgment + && !(acknowledgment is null && hostSignal == HostSignal.AfterAll)) // an early EOF, but still valid + { + throw new NotSupportedException($"Unknown Acknowledgment: {acknowledgment}"); + } + } + + public async ValueTask SendErrorAsync(string message) + => await outWriter.WriteLineAsync($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + + public ValueTask ReportResultsAsync(RunResults runResults) + => runResults.WriteAsync(this); + + public static async ValueTask GetHostAsync(string[] args) + { + for (int i = 0; i < args.Length; i++) + { + if (args[i] == PipeNameDescriptor) + { + var pipeName = args[i + 1]; // IndexOutOfRangeException means a bug (incomplete data) + var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); + await pipe.ConnectAsync(); + return new NamedPipeHost(pipe); + } + } + return new NoAcknowledgementConsoleHost(); + } + + public static NamedPipeServerStream GetPipeServerStream(BenchmarkId benchmarkId, out string pipeName) + { + int retryCounter = 0; + while (true) + { + try + { + pipeName = $"BenchmarkDotNet-{benchmarkId.Value}-{Guid.NewGuid()}"; + return new(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + } + catch (IOException) + { + // Rare case where the pipe name is already in use, retry up to 5 times. + if (++retryCounter >= 5) + { + throw; + } + } + } + } +} diff --git a/src/BenchmarkDotNet/Engines/NoAcknowledgementConsoleHost.cs b/src/BenchmarkDotNet/Engines/NoAcknowledgementConsoleHost.cs index bcd0281e0d..d4ad568bdc 100644 --- a/src/BenchmarkDotNet/Engines/NoAcknowledgementConsoleHost.cs +++ b/src/BenchmarkDotNet/Engines/NoAcknowledgementConsoleHost.cs @@ -1,35 +1,39 @@ using System; using System.IO; -using System.Runtime.CompilerServices; -using BenchmarkDotNet.Portability; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes.CompilerServices; using BenchmarkDotNet.Validators; -namespace BenchmarkDotNet.Engines +namespace BenchmarkDotNet.Engines; + +// This class is used when somebody manually launches benchmarking .exe without providing pipe name, or for wasm that doesn't support pipes. +[AggressivelyOptimizeMethods] +internal sealed class NoAcknowledgementConsoleHost : IHost { - // this class is used only when somebody manually launches benchmarking .exe without providing anonymous pipes file descriptors - public sealed class NoAcknowledgementConsoleHost : IHost - { - private readonly TextWriter outWriter; + private readonly TextWriter outWriter; - public NoAcknowledgementConsoleHost() => outWriter = Console.Out; + public NoAcknowledgementConsoleHost() => outWriter = Console.Out; - public void Write(string message) => outWriter.Write(message); + public async ValueTask WriteAsync(string message) + => await outWriter.WriteAsync(message); - public void WriteLine() => outWriter.WriteLine(); + public async ValueTask WriteLineAsync() + => await outWriter.WriteLineAsync(); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - public void WriteLine(string message) => outWriter.WriteLine(message); + public async ValueTask WriteLineAsync(string message) + => await outWriter.WriteLineAsync(message); - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - public void SendSignal(HostSignal hostSignal) => WriteLine(Engine.Signals.ToMessage(hostSignal)); + public ValueTask SendSignalAsync(HostSignal hostSignal) + => WriteLineAsync(Engine.Signals.ToMessage(hostSignal)); - public void SendError(string message) => outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + public ValueTask SendErrorAsync(string message) + => WriteLineAsync($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); - public void ReportResults(RunResults runResults) => runResults.Print(outWriter); + public ValueTask ReportResultsAsync(RunResults runResults) + => runResults.WriteAsync(this); - public void Dispose() - { - // do nothing on purpose - there is no point in closing STD OUT - } + public void Dispose() + { + // do nothing on purpose - there is no point in closing STD OUT } } diff --git a/src/BenchmarkDotNet/Engines/RunResults.cs b/src/BenchmarkDotNet/Engines/RunResults.cs index d6e08fa25e..9d4dbc4ae1 100644 --- a/src/BenchmarkDotNet/Engines/RunResults.cs +++ b/src/BenchmarkDotNet/Engines/RunResults.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; +using System.Threading.Tasks; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Mathematics; using BenchmarkDotNet.Reports; @@ -67,15 +67,19 @@ public IEnumerable GetAllMeasurements() yield return measurement; } - public void Print(TextWriter outWriter) + internal async ValueTask WriteAsync(IHost host) { foreach (var measurement in GetWorkloadResultMeasurements()) - outWriter.WriteLine(measurement.ToString()); + { + await host.WriteLineAsync(measurement.ToString()); + } if (!GCStats.Equals(GcStats.Empty)) - outWriter.WriteLine(GCStats.ToOutputLine()); + { + await host.WriteLineAsync(GCStats.ToOutputLine()); + } - outWriter.WriteLine(); + await host.WriteLineAsync(); } // TODO: improve diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index a6cfe27721..84bbdf63df 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -4,7 +4,6 @@ using System.IO; using System.IO.Pipes; using System.Text; -using System.Threading; using System.Threading.Tasks; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; @@ -19,20 +18,17 @@ internal class Broker private readonly ILogger logger; private readonly Process process; private readonly CompositeInProcessDiagnoser compositeInProcessDiagnoser; - private readonly AnonymousPipeServerStream inputFromBenchmark, acknowledgments; - private readonly ManualResetEvent finished; + private readonly NamedPipeServerStream pipe; public Broker(ILogger logger, Process process, IDiagnoser? diagnoser, CompositeInProcessDiagnoser compositeInProcessDiagnoser, - BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, AnonymousPipeServerStream inputFromBenchmark, AnonymousPipeServerStream acknowledgments) + BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, NamedPipeServerStream pipe) { this.logger = logger; this.process = process; this.Diagnoser = diagnoser; this.compositeInProcessDiagnoser = compositeInProcessDiagnoser; - this.inputFromBenchmark = inputFromBenchmark; - this.acknowledgments = acknowledgments; + this.pipe = pipe; DiagnoserActionParameters = new DiagnoserActionParameters(process, benchmarkCase, benchmarkId); - finished = new ManualResetEvent(false); process.EnableRaisingEvents = true; process.Exited += OnProcessExited; @@ -46,7 +42,7 @@ public Broker(ILogger logger, Process process, IDiagnoser? diagnoser, CompositeI internal List PrefixedOutput { get; } = []; - internal void ProcessData() + internal async ValueTask ProcessData() { // When the process fails to start, there is no pipe to read from. // If we try to read from such pipe, the read blocks and BDN hangs. @@ -58,32 +54,26 @@ internal void ProcessData() return; } - Task.Run(ProcessDataBlocking); - - finished.WaitOne(); + await ProcessDataCore(); } private void OnProcessExited(object? sender, EventArgs e) { process.Exited -= OnProcessExited; - // Dispose all the pipes to let reading from pipe finish with EOF and avoid a reasource leak. - inputFromBenchmark.DisposeLocalCopyOfClientHandle(); - inputFromBenchmark.Dispose(); - acknowledgments.DisposeLocalCopyOfClientHandle(); - acknowledgments.Dispose(); - - finished.Set(); + // Dispose the pipe to let reading from pipe finish with EOF and avoid a resource leak. + pipe.Dispose(); } - private void ProcessDataBlocking() + private async ValueTask ProcessDataCore() { - using StreamReader reader = new(inputFromBenchmark, AnonymousPipesHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); - using StreamWriter writer = new(acknowledgments, AnonymousPipesHost.UTF8NoBOM, bufferSize: 1); + await pipe.WaitForConnectionAsync(); + + using StreamReader reader = new(pipe, NamedPipeHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); // Flush the data to the Stream after each write, otherwise the client will wait for input endlessly! - writer.AutoFlush = true; + using StreamWriter writer = new(pipe, NamedPipeHost.UTF8NoBOM, bufferSize: 1) { AutoFlush = true }; - while (reader.ReadLine() is { } line) + while (await reader.ReadLineAsync() is { } line) { // TODO: implement Silent mode here logger.WriteLine(LogKind.Default, line); @@ -116,22 +106,13 @@ private void ProcessDataBlocking() { Diagnoser?.Handle(signal, DiagnoserActionParameters); - writer.WriteLine(Engine.Signals.Acknowledgment); + await writer.WriteLineAsync(Engine.Signals.Acknowledgment); - if (signal == HostSignal.BeforeAnythingElse) - { - // The client has connected, we no longer need to keep the local copy of client handle alive. - // This allows server to detect that child process is done and hence avoid resource leak. - // Full explanation: https://stackoverflow.com/a/39700027 - inputFromBenchmark.DisposeLocalCopyOfClientHandle(); - acknowledgments.DisposeLocalCopyOfClientHandle(); - } - else if (signal == HostSignal.AfterAll) + if (signal == HostSignal.AfterAll) { // we have received the last signal so we can stop reading from the pipe // if the process won't exit after this, its hung and needs to be killed process.Exited -= OnProcessExited; - finished.Set(); return; } } diff --git a/src/BenchmarkDotNet/Running/BenchmarkId.cs b/src/BenchmarkDotNet/Running/BenchmarkId.cs index c5c867bf6d..7a35f8254a 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkId.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkId.cs @@ -35,8 +35,8 @@ public BenchmarkId(int value, BenchmarkCase benchmarkCase) public string ToArguments(Diagnosers.RunMode diagnoserRunMode) => $"--benchmarkName {FullBenchmarkName.EscapeCommandLine()} --job {JobId.EscapeCommandLine()} --diagnoserRunMode {(int) diagnoserRunMode} --benchmarkId {Value}"; - public string ToArguments(string fromBenchmark, string toBenchmark, Diagnosers.RunMode diagnoserRunMode) - => $"{AnonymousPipesHost.AnonymousPipesDescriptors} {fromBenchmark} {toBenchmark} {ToArguments(diagnoserRunMode)}"; + public string ToArguments(string pipeName, Diagnosers.RunMode diagnoserRunMode) + => $"{NamedPipeHost.PipeNameDescriptor} {pipeName} {ToArguments(diagnoserRunMode)}"; public override string ToString() => Value.ToString(); diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerDirty.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerDirty.cs index 67ac056276..7bff744578 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerDirty.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerDirty.cs @@ -66,16 +66,10 @@ public static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos) return context.ExecuteUntilComplete(RunAsync(benchmarkRunInfos)); } - /// - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. - /// [PublicAPI] public static ValueTask RunAsync(IConfig? config = null, string[]? args = null) => RunAsync(typeof(T), config, args); - /// - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. - /// [PublicAPI] public static async ValueTask RunAsync(Type type, IConfig? config = null, string[]? args = null) { @@ -85,9 +79,6 @@ public static async ValueTask RunAsync(Type type, IConfig? config = nul } } - /// - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. - /// [PublicAPI] public static async ValueTask RunAsync(Type[] types, IConfig? config = null, string[]? args = null) { @@ -97,9 +88,6 @@ public static async ValueTask RunAsync(Type[] types, IConfig? config } } - /// - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. - /// [PublicAPI] public static async ValueTask RunAsync(Type type, MethodInfo[] methods, IConfig? config = null) { @@ -109,9 +97,6 @@ public static async ValueTask RunAsync(Type type, MethodInfo[] methods, } } - /// - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. - /// [PublicAPI] public static async ValueTask RunAsync(Assembly assembly, IConfig? config = null, string[]? args = null) { @@ -121,16 +106,10 @@ public static async ValueTask RunAsync(Assembly assembly, IConfig? co } } - /// - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. - /// [PublicAPI] public static async ValueTask RunAsync(BenchmarkRunInfo benchmarkRunInfo) => (await RunAsync([benchmarkRunInfo])).Single(); - /// - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. - /// [PublicAPI] public static async ValueTask RunAsync(BenchmarkRunInfo[] benchmarkRunInfos) { diff --git a/src/BenchmarkDotNet/Running/BenchmarkSwitcher.cs b/src/BenchmarkDotNet/Running/BenchmarkSwitcher.cs index 9a2e3b7b07..411e409a89 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkSwitcher.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkSwitcher.cs @@ -80,8 +80,7 @@ public IEnumerable Run(string[]? args = null, IConfig? config = null) } /// - /// Run all available benchmarks. - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// Run all available benchmarks asynchronously. /// [PublicAPI] public ValueTask> RunAllAsync(IConfig? config = null, string[]? args = null) @@ -94,8 +93,7 @@ public ValueTask> RunAllAsync(IConfig? config = null, strin } /// - /// Run all available benchmarks and join them to a single summary. - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. + /// Run all available benchmarks and join them to a single summary asynchronously. /// [PublicAPI] public async ValueTask RunAllJoinedAsync(IConfig? config = null, string[]? args = null) @@ -108,9 +106,6 @@ public async ValueTask RunAllJoinedAsync(IConfig? config = null, string return results.Single(); } - /// - /// Runs async if any benchmark is async and ran in-process; otherwise runs sync. - /// [PublicAPI] public async ValueTask> RunAsync(string[]? args = null, IConfig? config = null) { diff --git a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt index a3674d15ee..bebce6403a 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt @@ -24,15 +24,12 @@ namespace BenchmarkDotNet.Autogenerated private static async global::System.Threading.Tasks.ValueTask MainCore(global::System.String[] args) { - global::BenchmarkDotNet.Engines.IHost host; // this variable name is used by CodeGenerator.GetBenchmarkRunCall, do NOT change it - if (global::BenchmarkDotNet.Engines.AnonymousPipesHost.TryGetFileHandles(args, out global::System.String writeHandle, out global::System.String readHandle)) - host = new global::BenchmarkDotNet.Engines.AnonymousPipesHost(writeHandle, readHandle); - else - host = new global::BenchmarkDotNet.Engines.NoAcknowledgementConsoleHost(); + // This variable name is used by CodeGenerator.GetBenchmarkRunCall, do NOT change it! + global::BenchmarkDotNet.Engines.IHost host = await global::BenchmarkDotNet.Engines.NamedPipeHost.GetHostAsync(args); // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! - global::BenchmarkDotNet.Engines.HostExtensions.BeforeAnythingElse(host); + await global::BenchmarkDotNet.Engines.HostExtensions.BeforeAnythingElseAsync(host); try { @@ -45,32 +42,32 @@ namespace BenchmarkDotNet.Autogenerated if (args.Length == 0) { - host.WriteLine("You have not specified benchmark id (an integer) so the first benchmark will be executed."); + await host.WriteLineAsync("You have not specified benchmark id (an integer) so the first benchmark will be executed."); } $BenchmarkRunCall$ return 0; } catch (global::System.Exception oom) when (oom is global::System.OutOfMemoryException || oom is global::System.Reflection.TargetInvocationException reflection && reflection.InnerException is global::System.OutOfMemoryException) { - host.WriteLine(); - host.WriteLine("OutOfMemoryException!"); - host.WriteLine("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); - host.WriteLine("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); - host.WriteLine("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); - host.WriteLine(); - host.WriteLine(oom.ToString()); + await host.WriteLineAsync(); + await host.WriteLineAsync("OutOfMemoryException!"); + await host.WriteLineAsync("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); + await host.WriteLineAsync("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); + await host.WriteLineAsync("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); + await host.WriteLineAsync(); + await host.WriteLineAsync(oom.ToString()); return -1; } catch(global::System.Exception ex) { - host.WriteLine(); - host.WriteLine(ex.ToString()); + await host.WriteLineAsync(); + await host.WriteLineAsync(ex.ToString()); return -1; } finally { - global::BenchmarkDotNet.Engines.HostExtensions.AfterAll(host); + await global::BenchmarkDotNet.Engines.HostExtensions.AfterAllAsync(host); host.Dispose(); } diff --git a/src/BenchmarkDotNet/Templates/BenchmarkType.txt b/src/BenchmarkDotNet/Templates/BenchmarkType.txt index 46a0c4e307..5bd846834c 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkType.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkType.txt @@ -6,19 +6,19 @@ { global::BenchmarkDotNet.Autogenerated.Runnable_$ID$ instance = new global::BenchmarkDotNet.Autogenerated.Runnable_$ID$(); - host.WriteLine(); + await host.WriteLineAsync(); foreach (global::System.String infoLine in global::BenchmarkDotNet.Environments.BenchmarkEnvironmentInfo.GetCurrent().ToFormattedString()) { - host.WriteLine($"// {infoLine}"); + await host.WriteLineAsync($"// {infoLine}"); } global::BenchmarkDotNet.Jobs.Job job = new global::BenchmarkDotNet.Jobs.Job(); // use full name to avoid naming conflicts, #778 $JobSetDefinition$; job.Freeze(); - host.WriteLine($"// Job: {job.DisplayInfo}"); - host.WriteLine(); + await host.WriteLineAsync($"// Job: {job.DisplayInfo}"); + await host.WriteLineAsync(); global::System.Collections.Generic.IEnumerable errors = global::BenchmarkDotNet.Validators.BenchmarkProcessValidator.Validate(job, instance); - if (global::BenchmarkDotNet.Validators.ValidationErrorReporter.ReportIfAny(errors, host)) + if (await global::BenchmarkDotNet.Validators.ValidationErrorReporter.ReportIfAnyAsync(errors, host)) return; global::BenchmarkDotNet.Diagnosers.CompositeInProcessDiagnoserHandler compositeInProcessDiagnoserHandler = new global::BenchmarkDotNet.Diagnosers.CompositeInProcessDiagnoserHandler( @@ -31,10 +31,10 @@ ); if (diagnoserRunMode == global::BenchmarkDotNet.Diagnosers.RunMode.SeparateLogic) { - compositeInProcessDiagnoserHandler.Handle(global::BenchmarkDotNet.Engines.BenchmarkSignal.SeparateLogic); + await compositeInProcessDiagnoserHandler.HandleAsync(global::BenchmarkDotNet.Engines.BenchmarkSignal.SeparateLogic); return; } - compositeInProcessDiagnoserHandler.Handle(global::BenchmarkDotNet.Engines.BenchmarkSignal.BeforeEngine); + await compositeInProcessDiagnoserHandler.HandleAsync(global::BenchmarkDotNet.Engines.BenchmarkSignal.BeforeEngine); global::BenchmarkDotNet.Engines.EngineParameters engineParameters = new global::BenchmarkDotNet.Engines.EngineParameters() { @@ -55,10 +55,10 @@ }; global::BenchmarkDotNet.Engines.RunResults results = await new $EngineFactoryType$().Create(engineParameters).RunAsync(); - host.ReportResults(results); // printing costs memory, do this after runs + await host.ReportResultsAsync(results); // printing costs memory, do this after runs instance.__TrickTheJIT__(); // compile the method for disassembler, but without actual run of the benchmark ;) - compositeInProcessDiagnoserHandler.Handle(global::BenchmarkDotNet.Engines.BenchmarkSignal.AfterEngine); + await compositeInProcessDiagnoserHandler.HandleAsync(global::BenchmarkDotNet.Engines.BenchmarkSignal.AfterEngine); } [global::System.Diagnostics.CodeAnalysis.SetsRequiredMembers] diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs index add86fd580..92b92e6f80 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using System.IO; -using System.IO.Pipes; using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Diagnosers; @@ -19,7 +18,7 @@ namespace BenchmarkDotNet.Toolchains.DotNetCli [PublicAPI] public class DotNetCliExecutor(string customDotNetCliPath) : IExecutor { - public ValueTask ExecuteAsync(ExecuteParameters executeParameters) + public async ValueTask ExecuteAsync(ExecuteParameters executeParameters) { if (!File.Exists(executeParameters.BuildResult.ArtifactsPaths.ExecutablePath)) { @@ -29,12 +28,12 @@ public ValueTask ExecuteAsync(ExecuteParameters executeParameters executeParameters.Logger.WriteLineError(file.Name); } - return new ValueTask(ExecuteResult.CreateFailed()); + return ExecuteResult.CreateFailed(); } try { - var executeResult = Execute( + return await Execute( executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, @@ -45,7 +44,6 @@ public ValueTask ExecuteAsync(ExecuteParameters executeParameters executeParameters.Resolver, executeParameters.LaunchIndex, executeParameters.DiagnoserRunMode); - return new ValueTask(executeResult); } finally { @@ -55,7 +53,7 @@ public ValueTask ExecuteAsync(ExecuteParameters executeParameters } } - private ExecuteResult Execute(BenchmarkCase benchmarkCase, + private async ValueTask Execute(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, ArtifactsPaths artifactsPaths, @@ -66,13 +64,12 @@ private ExecuteResult Execute(BenchmarkCase benchmarkCase, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { - using AnonymousPipeServerStream inputFromBenchmark = new(PipeDirection.In, HandleInheritability.Inheritable); - using AnonymousPipeServerStream acknowledgments = new(PipeDirection.Out, HandleInheritability.Inheritable); + using var pipe = NamedPipeHost.GetPipeServerStream(benchmarkId, out string pipeName); var startInfo = DotNetCliCommandExecutor.BuildStartInfo( customDotNetCliPath, artifactsPaths.BinariesDirectoryPath, - $"{executableName.EscapeCommandLine()} {benchmarkId.ToArguments(inputFromBenchmark.GetClientHandleAsString(), acknowledgments.GetClientHandleAsString(), diagnoserRunMode)}", + $"{executableName.EscapeCommandLine()} {benchmarkId.ToArguments(pipeName, diagnoserRunMode)}", redirectStandardOutput: true, redirectStandardInput: false, redirectStandardError: false); // #1629 @@ -83,7 +80,7 @@ private ExecuteResult Execute(BenchmarkCase benchmarkCase, using ConsoleExitHandler consoleExitHandler = new(process, logger); using AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false); - Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, inputFromBenchmark, acknowledgments); + Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, pipe); logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); @@ -101,7 +98,7 @@ private ExecuteResult Execute(BenchmarkCase benchmarkCase, process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); } - broker.ProcessData(); + await broker.ProcessData(); if (!process.WaitForExit(milliseconds: (int) ExecuteParameters.ProcessExitTimeout.TotalMilliseconds)) { diff --git a/src/BenchmarkDotNet/Toolchains/Executor.cs b/src/BenchmarkDotNet/Toolchains/Executor.cs index 3bbe2ba7f7..21f0ecbde6 100644 --- a/src/BenchmarkDotNet/Toolchains/Executor.cs +++ b/src/BenchmarkDotNet/Toolchains/Executor.cs @@ -29,38 +29,36 @@ public class Executor : IExecutor /// /// Out-of-process executor executes synchronously. /// - public ValueTask ExecuteAsync(ExecuteParameters executeParameters) + public async ValueTask ExecuteAsync(ExecuteParameters executeParameters) { string exePath = executeParameters.BuildResult.ArtifactsPaths.ExecutablePath; - var executeResult = !File.Exists(exePath) + return !File.Exists(exePath) ? ExecuteResult.CreateFailed() - : Execute(executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, executeParameters.BuildResult.ArtifactsPaths, + : await Execute(executeParameters.BenchmarkCase, executeParameters.BenchmarkId, executeParameters.Logger, executeParameters.BuildResult.ArtifactsPaths, executeParameters.Diagnoser, executeParameters.CompositeInProcessDiagnoser, executeParameters.Resolver, executeParameters.LaunchIndex, executeParameters.DiagnoserRunMode); - return new ValueTask(executeResult); } - private static ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, ArtifactsPaths artifactsPaths, + private static async ValueTask Execute(BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, ArtifactsPaths artifactsPaths, IDiagnoser? diagnoser, CompositeInProcessDiagnoser compositeInProcessDiagnoser, IResolver resolver, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { try { - using AnonymousPipeServerStream inputFromBenchmark = new(PipeDirection.In, HandleInheritability.Inheritable); - using AnonymousPipeServerStream acknowledgments = new(PipeDirection.Out, HandleInheritability.Inheritable); + using var pipe = NamedPipeHost.GetPipeServerStream(benchmarkId, out string pipeName); - string args = benchmarkId.ToArguments(inputFromBenchmark.GetClientHandleAsString(), acknowledgments.GetClientHandleAsString(), diagnoserRunMode); + string args = benchmarkId.ToArguments(pipeName, diagnoserRunMode); using Process process = new() { StartInfo = CreateStartInfo(benchmarkCase, artifactsPaths, args, resolver) }; using ConsoleExitHandler consoleExitHandler = new(process, logger); using AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false); - Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, inputFromBenchmark, acknowledgments); + Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, pipe); diagnoser?.Handle(HostSignal.BeforeProcessStart, new DiagnoserActionParameters(process, benchmarkCase, benchmarkId)); - return Execute(process, benchmarkCase, broker, logger, consoleExitHandler, launchIndex, processOutputReader); + return await Execute(process, benchmarkCase, broker, logger, consoleExitHandler, launchIndex, processOutputReader); } finally { @@ -68,7 +66,7 @@ private static ExecuteResult Execute(BenchmarkCase benchmarkCase, BenchmarkId be } } - private static ExecuteResult Execute(Process process, BenchmarkCase benchmarkCase, Broker broker, + private static async ValueTask Execute(Process process, BenchmarkCase benchmarkCase, Broker broker, ILogger logger, ConsoleExitHandler consoleExitHandler, int launchIndex, AsyncProcessOutputReader processOutputReader) { logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); @@ -94,7 +92,7 @@ private static ExecuteResult Execute(Process process, BenchmarkCase benchmarkCas process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); } - broker.ProcessData(); + await broker.ProcessData(); if (!process.WaitForExit(milliseconds: (int)ExecuteParameters.ProcessExitTimeout.TotalMilliseconds)) { diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs index 98babe8a8f..f332ca9375 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs @@ -9,10 +9,11 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Toolchains.InProcess.Emit.Implementation; using BenchmarkDotNet.Toolchains.Parameters; using BenchmarkDotNet.Toolchains.Results; +#nullable enable + namespace BenchmarkDotNet.Toolchains.InProcess.Emit { internal class InProcessEmitExecutor(bool executeOnSeparateThread) : IExecutor @@ -36,9 +37,7 @@ public async ValueTask ExecuteAsync(ExecuteParameters executePara runThread.IsBackground = true; runThread.Start(); - runThread.Join(); - // We must await the task after joining the thread or else it can deadlock. exitCode = await taskCompletionSource.Task; } else diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs index 728c8d62a3..dab329691a 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs @@ -20,7 +20,7 @@ public static async ValueTask Run(IHost host, ExecuteParameters parameters) { // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! - host.BeforeAnythingElse(); + await host.BeforeAnythingElseAsync(); try { @@ -34,25 +34,25 @@ public static async ValueTask Run(IHost host, ExecuteParameters parameters) } catch (Exception oom) when (oom is OutOfMemoryException || oom is TargetInvocationException reflection && reflection.InnerException is OutOfMemoryException) { - host.WriteLine(); - host.WriteLine("OutOfMemoryException!"); - host.WriteLine("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); - host.WriteLine("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); - host.WriteLine("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); - host.WriteLine(); - host.WriteLine(oom.ToString()); + await host.WriteLineAsync(); + await host.WriteLineAsync("OutOfMemoryException!"); + await host.WriteLineAsync("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); + await host.WriteLineAsync("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); + await host.WriteLineAsync("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); + await host.WriteLineAsync(); + await host.WriteLineAsync(oom.ToString()); return -1; } catch (Exception ex) { - host.WriteLine(); - host.WriteLine(ex.ToString()); + await host.WriteLineAsync(); + await host.WriteLineAsync(ex.ToString()); return -1; } finally { - host.AfterAll(); + await host.AfterAllAsync(); } } @@ -63,17 +63,17 @@ private static async ValueTask RunCore(Type runnableType, IHost host, ExecutePar var instance = Activator.CreateInstance(runnableType); FillMembers(instance, benchmarkCase); - host.WriteLine(); + await host.WriteLineAsync(); foreach (string infoLine in BenchmarkEnvironmentInfo.GetCurrent().ToFormattedString()) { - host.WriteLine($"// {infoLine}"); + await host.WriteLineAsync($"// {infoLine}"); } var job = new Job().Apply(benchmarkCase.Job).Freeze(); - host.WriteLine($"// Job: {job.DisplayInfo}"); - host.WriteLine(); + await host.WriteLineAsync($"// Job: {job.DisplayInfo}"); + await host.WriteLineAsync(); var errors = BenchmarkProcessValidator.Validate(job, instance); - if (ValidationErrorReporter.ReportIfAny(errors, host)) + if (await ValidationErrorReporter.ReportIfAnyAsync(errors, host)) return; var compositeInProcessDiagnoserHandler = new Diagnosers.CompositeInProcessDiagnoserHandler( @@ -87,10 +87,10 @@ private static async ValueTask RunCore(Type runnableType, IHost host, ExecutePar ); if (parameters.DiagnoserRunMode == Diagnosers.RunMode.SeparateLogic) { - compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.SeparateLogic); + await compositeInProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.SeparateLogic); return; } - compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.BeforeEngine); + await compositeInProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.BeforeEngine); var engineParameters = new EngineParameters() { @@ -114,11 +114,11 @@ private static async ValueTask RunCore(Type runnableType, IHost host, ExecutePar .ResolveValue(InfrastructureMode.EngineFactoryCharacteristic, InfrastructureResolver.Instance) .Create(engineParameters) .RunAsync(); - host.ReportResults(results); + await host.ReportResultsAsync(results); runnableType.GetMethod(TrickTheJitCoreMethodName).Invoke(instance, []); - compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterEngine); + await compositeInProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.AfterEngine); } private static void FillMembers(object instance, BenchmarkCase benchmarkCase) diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/InProcessHost.cs b/src/BenchmarkDotNet/Toolchains/InProcess/InProcessHost.cs index bf093121b1..934fb0c8a1 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/InProcessHost.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/InProcessHost.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; using System.Text; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes.CompilerServices; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; using BenchmarkDotNet.Loggers; -using BenchmarkDotNet.Portability; using BenchmarkDotNet.Running; using BenchmarkDotNet.Validators; @@ -18,6 +17,7 @@ namespace BenchmarkDotNet.Toolchains.InProcess { /// Host API for in-process benchmarks. /// + [AggressivelyOptimizeMethods] internal sealed class InProcessHost : IHost { private readonly ILogger logger; @@ -54,15 +54,14 @@ public InProcessHost(BenchmarkCase benchmarkCase, ILogger logger, IDiagnoser? di /// Passes text to the host. /// Text to write. - public void Write(string message) => logger.Write(message); + public async ValueTask WriteAsync(string message) => logger.Write(message); /// Passes new line to the host. - public void WriteLine() => logger.WriteLine(); + public async ValueTask WriteLineAsync() => logger.WriteLine(); /// Passes text (new line appended) to the host. /// Text to write. - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - public void WriteLine(string message) + public async ValueTask WriteLineAsync(string message) { logger.WriteLine(message); if (message.StartsWith(CompositeInProcessDiagnoser.HeaderKey)) // Captures both header and results @@ -73,22 +72,17 @@ public void WriteLine(string message) /// Sends notification signal to the host. /// The signal to send. - [MethodImpl(CodeGenHelper.AggressiveOptimizationOption)] - public void SendSignal(HostSignal hostSignal) => diagnoser?.Handle(hostSignal, diagnoserActionParameters!); + public async ValueTask SendSignalAsync(HostSignal hostSignal) => diagnoser?.Handle(hostSignal, diagnoserActionParameters!); - public void SendError(string message) => logger.WriteLine(LogKind.Error, $"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + public async ValueTask SendErrorAsync(string message) => logger.WriteLine(LogKind.Error, $"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); /// Submits run results to the host. /// The run results. - public void ReportResults(RunResults runResults) + public async ValueTask ReportResultsAsync(RunResults runResults) { RunResults = runResults; - using (var w = new StringWriter()) - { - runResults.Print(w); - logger.Write(w.GetStringBuilder().ToString()); - } + await runResults.WriteAsync(this); } // Keep in sync with Broker and WasmExecutor. diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs index 6e11dff54f..12739aa35f 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitExecutor.cs @@ -37,9 +37,7 @@ public async ValueTask ExecuteAsync(ExecuteParameters executePara runThread.IsBackground = true; runThread.Start(); - runThread.Join(); - // We must await the task after joining the thread or else it can deadlock. exitCode = await taskCompletionSource.Task; } else diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs index 8010d6a729..f7d195a95c 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs @@ -26,7 +26,7 @@ public static async ValueTask Run(IHost host, ExecuteParameters parameters) { // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! - host.BeforeAnythingElse(); + await host.BeforeAnythingElseAsync(); try { @@ -46,25 +46,25 @@ public static async ValueTask Run(IHost host, ExecuteParameters parameters) } catch (Exception oom) when (oom is OutOfMemoryException || oom is TargetInvocationException reflection && reflection.InnerException is OutOfMemoryException) { - host.WriteLine(); - host.WriteLine("OutOfMemoryException!"); - host.WriteLine("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); - host.WriteLine("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); - host.WriteLine("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); - host.WriteLine(); - host.WriteLine(oom.ToString()); + await host.WriteLineAsync(); + await host.WriteLineAsync("OutOfMemoryException!"); + await host.WriteLineAsync("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); + await host.WriteLineAsync("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); + await host.WriteLineAsync("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); + await host.WriteLineAsync(); + await host.WriteLineAsync(oom.ToString()); return -1; } catch (Exception ex) { - host.WriteLine(); - host.WriteLine(ex.ToString()); + await host.WriteLineAsync(); + await host.WriteLineAsync(ex.ToString()); return -1; } finally { - host.AfterAll(); + await host.AfterAllAsync(); } } @@ -125,14 +125,14 @@ public static async ValueTask RunCore(IHost host, ExecuteParameters parameters) FillMembers(instance, benchmarkCase); - host.WriteLine(); + await host.WriteLineAsync(); foreach (string infoLine in BenchmarkEnvironmentInfo.GetCurrent().ToFormattedString()) - host.WriteLine("// {0}", infoLine); - host.WriteLine("// Job: {0}", job.DisplayInfo); - host.WriteLine(); + await host.WriteLineAsync("// {0}", infoLine); + await host.WriteLineAsync("// Job: {0}", job.DisplayInfo); + await host.WriteLineAsync(); var errors = BenchmarkProcessValidator.Validate(job, instance); - if (ValidationErrorReporter.ReportIfAny(errors, host)) + if (await ValidationErrorReporter.ReportIfAnyAsync(errors, host)) return; var compositeInProcessDiagnoserHandler = new Diagnosers.CompositeInProcessDiagnoserHandler( @@ -146,10 +146,10 @@ public static async ValueTask RunCore(IHost host, ExecuteParameters parameters) ); if (parameters.DiagnoserRunMode == Diagnosers.RunMode.SeparateLogic) { - compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.SeparateLogic); + await compositeInProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.SeparateLogic); return; } - compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.BeforeEngine); + await compositeInProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.BeforeEngine); var engineParameters = new EngineParameters { @@ -178,9 +178,9 @@ public static async ValueTask RunCore(IHost host, ExecuteParameters parameters) .ResolveValue(InfrastructureMode.EngineFactoryCharacteristic, InfrastructureResolver.Instance)! .Create(engineParameters) .RunAsync(); - host.ReportResults(results); // printing costs memory, do this after runs + await host.ReportResultsAsync(results); // printing costs memory, do this after runs - compositeInProcessDiagnoserHandler.Handle(BenchmarkSignal.AfterEngine); + await compositeInProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.AfterEngine); } } } diff --git a/src/BenchmarkDotNet/Validators/ValidationErrorReporter.cs b/src/BenchmarkDotNet/Validators/ValidationErrorReporter.cs index 7afc00765c..c5c96f87ab 100644 --- a/src/BenchmarkDotNet/Validators/ValidationErrorReporter.cs +++ b/src/BenchmarkDotNet/Validators/ValidationErrorReporter.cs @@ -1,23 +1,23 @@ using System.Collections.Generic; +using System.Threading.Tasks; using BenchmarkDotNet.Engines; using JetBrains.Annotations; -namespace BenchmarkDotNet.Validators +namespace BenchmarkDotNet.Validators; + +[UsedImplicitly] +public static class ValidationErrorReporter { - public static class ValidationErrorReporter - { - public const string ConsoleErrorPrefix = "// ERROR: "; + public const string ConsoleErrorPrefix = "// ERROR: "; - [UsedImplicitly] // Generated benchmarks - public static bool ReportIfAny(IEnumerable validationErrors, IHost host) + public static async ValueTask ReportIfAnyAsync(IEnumerable validationErrors, IHost host) + { + bool hasErrors = false; + foreach (var validationError in validationErrors) { - bool hasErrors = false; - foreach (var validationError in validationErrors) - { - host.SendError(validationError.Message); - hasErrors = true; - } - return hasErrors; + await host.SendErrorAsync(validationError.Message); + hasErrors = true; } + return hasErrors; } } \ No newline at end of file From d87b53069dc70671a9408529723a383c86520d22 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 9 Feb 2026 01:47:57 -0500 Subject: [PATCH 11/33] Allow RoslynToolchain in old Mono. --- src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs b/src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs index 381298d91a..18c24ffc82 100644 --- a/src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs +++ b/src/BenchmarkDotNet/Toolchains/Roslyn/RoslynToolchain.cs @@ -29,10 +29,10 @@ public override async IAsyncEnumerable ValidateAsync(BenchmarkC yield return validationError; } - if (!RuntimeInformation.IsFullFramework) + if (!(RuntimeInformation.IsFullFramework || RuntimeInformation.IsOldMono)) { yield return new ValidationError(true, - "The Roslyn toolchain is only supported on .NET Framework", + "The Roslyn toolchain is only supported on .NET Framework and legacy Mono", benchmarkCase); } From 6db0cdea7b57c8c0b5775cf5d9915d68ddd50b6e Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 9 Feb 2026 02:48:43 -0500 Subject: [PATCH 12/33] Fix tests. --- src/BenchmarkDotNet/Loggers/Broker.cs | 10 +++++++++- .../BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 84bbdf63df..4d65f1ad2e 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -67,7 +67,15 @@ private void OnProcessExited(object? sender, EventArgs e) private async ValueTask ProcessDataCore() { - await pipe.WaitForConnectionAsync(); + try + { + await pipe.WaitForConnectionAsync(); + } + catch (IOException) + { + // If the process exited before the connection was established, it throws IOException. + return; + } using StreamReader reader = new(pipe, NamedPipeHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); // Flush the data to the Stream after each write, otherwise the client will wait for input endlessly! diff --git a/tests/BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs b/tests/BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs index 8bb7972fd4..2d7729d998 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/RunAsyncTests.cs @@ -35,8 +35,8 @@ private void ExecuteAndAssert(IToolchain toolchain, bool expectsAsyn } [Fact] - public void OutOfProcessAsyncBenchmarksRunSync() - => ExecuteAndAssert(Job.Default.GetToolchain(), false); + public void OutOfProcessBenchmarks() + => ExecuteAndAssert(Job.Default.GetToolchain(), true); [Theory] [MemberData(nameof(GetInProcessToolchains), DisableDiscoveryEnumeration = true)] From 5423ea1aa2952b2020d2996d161f3a21452ef34f Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 9 Feb 2026 03:46:33 -0500 Subject: [PATCH 13/33] Catch proper exception on Unix. --- src/BenchmarkDotNet/Loggers/Broker.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 4d65f1ad2e..2524c7cd34 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.IO.Pipes; +using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using BenchmarkDotNet.Diagnosers; @@ -71,9 +72,9 @@ private async ValueTask ProcessDataCore() { await pipe.WaitForConnectionAsync(); } - catch (IOException) + // If the process exited before the connection was established, it throws IOException on Windows or SocketException on Unix. + catch (Exception e) when (e is IOException || e is SocketException) { - // If the process exited before the connection was established, it throws IOException. return; } From c3d4529b43bdbc1d0b8f2b0edc78290fc8106ccd Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 9 Feb 2026 13:44:04 -0500 Subject: [PATCH 14/33] Shorten pipe name. --- src/BenchmarkDotNet/Engines/NamedPipeHost.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs b/src/BenchmarkDotNet/Engines/NamedPipeHost.cs index 5848f706a2..e4e3c72a8b 100644 --- a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs +++ b/src/BenchmarkDotNet/Engines/NamedPipeHost.cs @@ -97,7 +97,8 @@ public static NamedPipeServerStream GetPipeServerStream(BenchmarkId benchmarkId, { try { - pipeName = $"BenchmarkDotNet-{benchmarkId.Value}-{Guid.NewGuid()}"; + // MacOS has a small character limit, so we use random file name instead of guid. + pipeName = $"BDN-{benchmarkId.Value}-{Path.GetRandomFileName().Replace(".", "")}"; return new(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); } catch (IOException) From 0edf78a0ec4b181473a5125019c56c0d72d1b873 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 9 Feb 2026 17:53:23 -0500 Subject: [PATCH 15/33] Attempt to fix Windows hangs. Added pipe connection timeout. --- src/BenchmarkDotNet/Engines/NamedPipeHost.cs | 18 ++++++++++++++++-- src/BenchmarkDotNet/Loggers/Broker.cs | 8 +++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs b/src/BenchmarkDotNet/Engines/NamedPipeHost.cs index e4e3c72a8b..a1d18721b8 100644 --- a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs +++ b/src/BenchmarkDotNet/Engines/NamedPipeHost.cs @@ -1,4 +1,5 @@ using BenchmarkDotNet.Attributes.CompilerServices; +using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Running; using BenchmarkDotNet.Validators; using JetBrains.Annotations; @@ -6,6 +7,7 @@ using System.ComponentModel; using System.IO; using System.IO.Pipes; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -20,6 +22,7 @@ public class NamedPipeHost : IHost { internal const string PipeNameDescriptor = "--pipeName"; internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + public static readonly TimeSpan PipeConnectionTimeout = TimeSpan.FromMinutes(1); private readonly NamedPipeClientStream pipe; private readonly StreamWriter outWriter; @@ -83,7 +86,7 @@ public static async ValueTask GetHostAsync(string[] args) { var pipeName = args[i + 1]; // IndexOutOfRangeException means a bug (incomplete data) var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); - await pipe.ConnectAsync(); + await pipe.ConnectAsync((int) PipeConnectionTimeout.TotalMilliseconds); return new NamedPipeHost(pipe); } } @@ -99,7 +102,13 @@ public static NamedPipeServerStream GetPipeServerStream(BenchmarkId benchmarkId, { // MacOS has a small character limit, so we use random file name instead of guid. pipeName = $"BDN-{benchmarkId.Value}-{Path.GetRandomFileName().Replace(".", "")}"; - return new(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + var pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + // Ensure the pipe handle is not inherited to prevent hangs on Windows. + if (OsDetector.IsWindows()) + { + SetHandleInformation(pipe.SafePipeHandle.DangerousGetHandle(), HANDLE_FLAG_INHERIT, 0); + } + return pipe; } catch (IOException) { @@ -111,4 +120,9 @@ public static NamedPipeServerStream GetPipeServerStream(BenchmarkId benchmarkId, } } } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetHandleInformation(IntPtr hObject, int dwMask, int dwFlags); + + const int HANDLE_FLAG_INHERIT = 1; } diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 2524c7cd34..1afeed2bb8 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -5,6 +5,7 @@ using System.IO.Pipes; using System.Net.Sockets; using System.Text; +using System.Threading; using System.Threading.Tasks; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; @@ -70,7 +71,12 @@ private async ValueTask ProcessDataCore() { try { - await pipe.WaitForConnectionAsync(); + using var cts = new CancellationTokenSource(NamedPipeHost.PipeConnectionTimeout); + await pipe.WaitForConnectionAsync(cts.Token); + } + catch (OperationCanceledException) + { + throw new TimeoutException($"The connection to the benchmark process timed out after {NamedPipeHost.PipeConnectionTimeout}."); } // If the process exited before the connection was established, it throws IOException on Windows or SocketException on Unix. catch (Exception e) when (e is IOException || e is SocketException) From 5a465a818e3ccb2731952f877239715a3954d5d4 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 9 Feb 2026 21:11:56 -0500 Subject: [PATCH 16/33] Remove redundant check. --- src/BenchmarkDotNet/Loggers/Broker.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 8da0a0e758..55c221693c 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -74,9 +74,6 @@ internal async ValueTask ProcessData() private async ValueTask ProcessDataCore() { - if (process.HasExited) - return Result.EarlyProcessExit; - if (process.HasExited || this.pipe is not { } pipe) return Result.EarlyProcessExit; From 70a6b14630eeaa80fbc4287803f634e9c6c7013c Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 9 Feb 2026 22:32:25 -0500 Subject: [PATCH 17/33] ReadLineAsync --- src/BenchmarkDotNet/Loggers/Broker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 55c221693c..f87f531fee 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -99,7 +99,7 @@ private async ValueTask ProcessDataCore() while (true) { - var line = reader.ReadLine(); + var line = await reader.ReadLineAsync(); if (line == null) return Result.EndOfStream; @@ -125,7 +125,7 @@ private async ValueTask ProcessDataCore() var resultsStringBuilder = new StringBuilder(); for (int i = 0; i < resultsLinesCount;) { - line = reader.ReadLine(); + line = await reader.ReadLineAsync(); if (line == null) return Result.EndOfStream; From 48a307d90ec5d722515754926bc4d6ac5ef00159 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Tue, 10 Feb 2026 02:22:23 -0500 Subject: [PATCH 18/33] Use 2 pipes to fix deadlock. --- src/BenchmarkDotNet/Engines/NamedPipeHost.cs | 33 ++++++++++--------- src/BenchmarkDotNet/Loggers/Broker.cs | 28 +++++++++------- src/BenchmarkDotNet/Running/BenchmarkId.cs | 4 +-- .../Toolchains/DotNetCli/DotNetCliExecutor.cs | 8 +++-- src/BenchmarkDotNet/Toolchains/Executor.cs | 7 ++-- 5 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs b/src/BenchmarkDotNet/Engines/NamedPipeHost.cs index a1d18721b8..cb57e922e1 100644 --- a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs +++ b/src/BenchmarkDotNet/Engines/NamedPipeHost.cs @@ -20,27 +20,24 @@ namespace BenchmarkDotNet.Engines; [EditorBrowsable(EditorBrowsableState.Never)] public class NamedPipeHost : IHost { - internal const string PipeNameDescriptor = "--pipeName"; + internal const string PipeNamesDescriptor = "--pipeNames"; internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); public static readonly TimeSpan PipeConnectionTimeout = TimeSpan.FromMinutes(1); - private readonly NamedPipeClientStream pipe; private readonly StreamWriter outWriter; private readonly StreamReader inReader; - public NamedPipeHost(NamedPipeClientStream pipe) + public NamedPipeHost(NamedPipeClientStream fromBenchmarkPipe, NamedPipeClientStream toBenchmarkPipe) { - this.pipe = pipe; // Flush the data to the Stream after each write, otherwise the host process will wait for input endlessly! - outWriter = new(pipe, UTF8NoBOM, bufferSize: 1024, leaveOpen: true) { AutoFlush = true }; - inReader = new(pipe, UTF8NoBOM, detectEncodingFromByteOrderMarks: false, bufferSize: 1024, leaveOpen: true); + outWriter = new(fromBenchmarkPipe, UTF8NoBOM) { AutoFlush = true }; + inReader = new(toBenchmarkPipe, UTF8NoBOM, detectEncodingFromByteOrderMarks: false); } public void Dispose() { outWriter.Dispose(); inReader.Dispose(); - pipe.Dispose(); } public async ValueTask WriteAsync(string message) @@ -82,18 +79,24 @@ public static async ValueTask GetHostAsync(string[] args) { for (int i = 0; i < args.Length; i++) { - if (args[i] == PipeNameDescriptor) + if (args[i] == PipeNamesDescriptor) { - var pipeName = args[i + 1]; // IndexOutOfRangeException means a bug (incomplete data) - var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); - await pipe.ConnectAsync((int) PipeConnectionTimeout.TotalMilliseconds); - return new NamedPipeHost(pipe); + // IndexOutOfRangeException means a bug (incomplete data) + var fromBenchmarkPipeName = args[i + 1]; + var toBenchmarkPipeName = args[i + 2]; + var fromBenchmarkPipe = new NamedPipeClientStream(".", fromBenchmarkPipeName, PipeDirection.Out, PipeOptions.Asynchronous); + var toBenchmarkPipe = new NamedPipeClientStream(".", toBenchmarkPipeName, PipeDirection.In, PipeOptions.Asynchronous); + await Task.WhenAll([ + fromBenchmarkPipe.ConnectAsync((int) PipeConnectionTimeout.TotalMilliseconds), + toBenchmarkPipe.ConnectAsync((int) PipeConnectionTimeout.TotalMilliseconds) + ]); + return new NamedPipeHost(fromBenchmarkPipe, toBenchmarkPipe); } } return new NoAcknowledgementConsoleHost(); } - public static NamedPipeServerStream GetPipeServerStream(BenchmarkId benchmarkId, out string pipeName) + public static NamedPipeServerStream GetPipeServerStream(BenchmarkId benchmarkId, PipeDirection pipeDirection, out string pipeName) { int retryCounter = 0; while (true) @@ -101,8 +104,8 @@ public static NamedPipeServerStream GetPipeServerStream(BenchmarkId benchmarkId, try { // MacOS has a small character limit, so we use random file name instead of guid. - pipeName = $"BDN-{benchmarkId.Value}-{Path.GetRandomFileName().Replace(".", "")}"; - var pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + pipeName = $"BDN-{benchmarkId.Value}-{pipeDirection}-{Path.GetRandomFileName().Replace(".", "")}"; + var pipe = new NamedPipeServerStream(pipeName, pipeDirection, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); // Ensure the pipe handle is not inherited to prevent hangs on Windows. if (OsDetector.IsWindows()) { diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index f87f531fee..48bfebe73a 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -20,7 +20,8 @@ internal class Broker : IDisposable private readonly ILogger logger; private readonly Process process; private readonly CompositeInProcessDiagnoser compositeInProcessDiagnoser; - private NamedPipeServerStream? pipe; + private NamedPipeServerStream? inPipe; + private NamedPipeServerStream? outPipe; private enum Result { @@ -31,13 +32,14 @@ private enum Result } public Broker(ILogger logger, Process process, IDiagnoser? diagnoser, CompositeInProcessDiagnoser compositeInProcessDiagnoser, - BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, NamedPipeServerStream pipe) + BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, NamedPipeServerStream inPipe, NamedPipeServerStream outPipe) { this.logger = logger; this.process = process; this.Diagnoser = diagnoser; this.compositeInProcessDiagnoser = compositeInProcessDiagnoser; - this.pipe = pipe; + this.inPipe = inPipe; + this.outPipe = outPipe; DiagnoserActionParameters = new DiagnoserActionParameters(process, benchmarkCase, benchmarkId); process.EnableRaisingEvents = true; @@ -56,8 +58,9 @@ public void Dispose() { process.Exited -= OnProcessExited; - // Dispose the pipe to let reading from pipe finish with EOF and avoid a resource leak. - Interlocked.Exchange(ref pipe, null)?.Dispose(); + // Dispose the pipes to let reading from pipe finish with EOF and avoid a resource leak. + Interlocked.Exchange(ref inPipe, null)?.Dispose(); + Interlocked.Exchange(ref outPipe, null)?.Dispose(); } private void OnProcessExited(object? sender, EventArgs e) @@ -74,13 +77,16 @@ internal async ValueTask ProcessData() private async ValueTask ProcessDataCore() { - if (process.HasExited || this.pipe is not { } pipe) + if (process.HasExited || this.inPipe is not { } inPipe || this.outPipe is not { } outPipe) return Result.EarlyProcessExit; try { using var cts = new CancellationTokenSource(NamedPipeHost.PipeConnectionTimeout); - await pipe.WaitForConnectionAsync(cts.Token); + await Task.WhenAll([ + inPipe.WaitForConnectionAsync(cts.Token), + outPipe.WaitForConnectionAsync(cts.Token)] + ); } catch (OperationCanceledException) { @@ -93,9 +99,9 @@ private async ValueTask ProcessDataCore() return Result.EarlyProcessExit; } - using StreamReader reader = new(pipe, NamedPipeHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); + using StreamReader reader = new(inPipe, NamedPipeHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); // Flush the data to the Stream after each write, otherwise the client will wait for input endlessly! - using StreamWriter writer = new(pipe, NamedPipeHost.UTF8NoBOM, bufferSize: 1) { AutoFlush = true }; + using StreamWriter writer = new(outPipe, NamedPipeHost.UTF8NoBOM, bufferSize: 1) { AutoFlush = true }; while (true) { @@ -149,8 +155,6 @@ private async ValueTask ProcessDataCore() { Diagnoser?.Handle(signal, DiagnoserActionParameters); - await writer.WriteLineAsync(Engine.Signals.Acknowledgment); - if (signal == HostSignal.AfterAll) { // we have received the last signal so we can stop reading from the pipe @@ -158,6 +162,8 @@ private async ValueTask ProcessDataCore() return Result.Success; } + await writer.WriteLineAsync(Engine.Signals.Acknowledgment); + continue; } diff --git a/src/BenchmarkDotNet/Running/BenchmarkId.cs b/src/BenchmarkDotNet/Running/BenchmarkId.cs index 7a35f8254a..615dcdedd2 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkId.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkId.cs @@ -35,8 +35,8 @@ public BenchmarkId(int value, BenchmarkCase benchmarkCase) public string ToArguments(Diagnosers.RunMode diagnoserRunMode) => $"--benchmarkName {FullBenchmarkName.EscapeCommandLine()} --job {JobId.EscapeCommandLine()} --diagnoserRunMode {(int) diagnoserRunMode} --benchmarkId {Value}"; - public string ToArguments(string pipeName, Diagnosers.RunMode diagnoserRunMode) - => $"{NamedPipeHost.PipeNameDescriptor} {pipeName} {ToArguments(diagnoserRunMode)}"; + public string ToArguments(string fromBenchmark, string toBenchmark, Diagnosers.RunMode diagnoserRunMode) + => $"{NamedPipeHost.PipeNamesDescriptor} {fromBenchmark} {toBenchmark} {ToArguments(diagnoserRunMode)}"; public override string ToString() => Value.ToString(); diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs index bb8ee5a9d8..bc3256aa78 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.IO.Pipes; using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Diagnosers; @@ -65,12 +66,13 @@ private async ValueTask Execute(BenchmarkCase benchmarkCase, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { - using var pipe = NamedPipeHost.GetPipeServerStream(benchmarkId, out string pipeName); + using var fromBenchmarkPipe = NamedPipeHost.GetPipeServerStream(benchmarkId, PipeDirection.In, out string fromBenchmarkPipeName); + using var toBenchmarkPipe = NamedPipeHost.GetPipeServerStream(benchmarkId, PipeDirection.Out, out string toBenchmarkPipeName); var startInfo = DotNetCliCommandExecutor.BuildStartInfo( customDotNetCliPath, artifactsPaths.BinariesDirectoryPath, - $"{executableName.EscapeCommandLine()} {benchmarkId.ToArguments(pipeName, diagnoserRunMode)}", + $"{executableName.EscapeCommandLine()} {benchmarkId.ToArguments(fromBenchmarkPipeName, toBenchmarkPipeName, diagnoserRunMode)}", redirectStandardOutput: true, redirectStandardInput: false, redirectStandardError: false); // #1629 @@ -83,7 +85,7 @@ private async ValueTask Execute(BenchmarkCase benchmarkCase, List results; List prefixedOutput; - using (Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, pipe)) + using (Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, fromBenchmarkPipe, toBenchmarkPipe)) { logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); diff --git a/src/BenchmarkDotNet/Toolchains/Executor.cs b/src/BenchmarkDotNet/Toolchains/Executor.cs index 85ae471f5e..a05ae60bf5 100644 --- a/src/BenchmarkDotNet/Toolchains/Executor.cs +++ b/src/BenchmarkDotNet/Toolchains/Executor.cs @@ -59,9 +59,10 @@ private static async ValueTask ExecuteCore(BenchmarkCase benchmar IDiagnoser? diagnoser, CompositeInProcessDiagnoser compositeInProcessDiagnoser, IResolver resolver, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { - using var pipe = NamedPipeHost.GetPipeServerStream(benchmarkId, out string pipeName); + using var fromBenchmarkPipe = NamedPipeHost.GetPipeServerStream(benchmarkId, PipeDirection.In, out string fromBenchmarkPipeName); + using var toBenchmarkPipe = NamedPipeHost.GetPipeServerStream(benchmarkId, PipeDirection.Out, out string toBenchmarkPipeName); - string args = benchmarkId.ToArguments(pipeName, diagnoserRunMode); + string args = benchmarkId.ToArguments(fromBenchmarkPipeName, toBenchmarkPipeName, diagnoserRunMode); using Process process = new() { StartInfo = CreateStartInfo(benchmarkCase, artifactsPaths, args, resolver) }; using ConsoleExitHandler consoleExitHandler = new(process, logger); @@ -69,7 +70,7 @@ private static async ValueTask ExecuteCore(BenchmarkCase benchmar List results; List prefixedOutput; - using (Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, pipe)) + using (Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, fromBenchmarkPipe, toBenchmarkPipe)) { diagnoser?.Handle(HostSignal.BeforeProcessStart, new DiagnoserActionParameters(process, benchmarkCase, benchmarkId)); From 3c082cc319e6c0e2952afa5e8e6ff52360a741a8 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Tue, 10 Feb 2026 02:24:19 -0500 Subject: [PATCH 19/33] Pluralize NamedPipesHost name. --- .../Engines/{NamedPipeHost.cs => NamedPipesHost.cs} | 6 +++--- src/BenchmarkDotNet/Loggers/Broker.cs | 8 ++++---- src/BenchmarkDotNet/Running/BenchmarkId.cs | 2 +- src/BenchmarkDotNet/Templates/BenchmarkProgram.txt | 2 +- .../Toolchains/DotNetCli/DotNetCliExecutor.cs | 4 ++-- src/BenchmarkDotNet/Toolchains/Executor.cs | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) rename src/BenchmarkDotNet/Engines/{NamedPipeHost.cs => NamedPipesHost.cs} (95%) diff --git a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs b/src/BenchmarkDotNet/Engines/NamedPipesHost.cs similarity index 95% rename from src/BenchmarkDotNet/Engines/NamedPipeHost.cs rename to src/BenchmarkDotNet/Engines/NamedPipesHost.cs index cb57e922e1..16924a3c7c 100644 --- a/src/BenchmarkDotNet/Engines/NamedPipeHost.cs +++ b/src/BenchmarkDotNet/Engines/NamedPipesHost.cs @@ -18,7 +18,7 @@ namespace BenchmarkDotNet.Engines; [AggressivelyOptimizeMethods] [UsedImplicitly] [EditorBrowsable(EditorBrowsableState.Never)] -public class NamedPipeHost : IHost +public class NamedPipesHost : IHost { internal const string PipeNamesDescriptor = "--pipeNames"; internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); @@ -27,7 +27,7 @@ public class NamedPipeHost : IHost private readonly StreamWriter outWriter; private readonly StreamReader inReader; - public NamedPipeHost(NamedPipeClientStream fromBenchmarkPipe, NamedPipeClientStream toBenchmarkPipe) + public NamedPipesHost(NamedPipeClientStream fromBenchmarkPipe, NamedPipeClientStream toBenchmarkPipe) { // Flush the data to the Stream after each write, otherwise the host process will wait for input endlessly! outWriter = new(fromBenchmarkPipe, UTF8NoBOM) { AutoFlush = true }; @@ -90,7 +90,7 @@ await Task.WhenAll([ fromBenchmarkPipe.ConnectAsync((int) PipeConnectionTimeout.TotalMilliseconds), toBenchmarkPipe.ConnectAsync((int) PipeConnectionTimeout.TotalMilliseconds) ]); - return new NamedPipeHost(fromBenchmarkPipe, toBenchmarkPipe); + return new NamedPipesHost(fromBenchmarkPipe, toBenchmarkPipe); } } return new NoAcknowledgementConsoleHost(); diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 48bfebe73a..4c57720fd7 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -82,7 +82,7 @@ private async ValueTask ProcessDataCore() try { - using var cts = new CancellationTokenSource(NamedPipeHost.PipeConnectionTimeout); + using var cts = new CancellationTokenSource(NamedPipesHost.PipeConnectionTimeout); await Task.WhenAll([ inPipe.WaitForConnectionAsync(cts.Token), outPipe.WaitForConnectionAsync(cts.Token)] @@ -90,7 +90,7 @@ await Task.WhenAll([ } catch (OperationCanceledException) { - throw new TimeoutException($"The connection to the benchmark process timed out after {NamedPipeHost.PipeConnectionTimeout}."); + throw new TimeoutException($"The connection to the benchmark process timed out after {NamedPipesHost.PipeConnectionTimeout}."); } // If the process exited before the connection was established such that the pipe is disposed from the exited handler, // it throws IOException on Windows or SocketException on Unix. @@ -99,9 +99,9 @@ await Task.WhenAll([ return Result.EarlyProcessExit; } - using StreamReader reader = new(inPipe, NamedPipeHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); + using StreamReader reader = new(inPipe, NamedPipesHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); // Flush the data to the Stream after each write, otherwise the client will wait for input endlessly! - using StreamWriter writer = new(outPipe, NamedPipeHost.UTF8NoBOM, bufferSize: 1) { AutoFlush = true }; + using StreamWriter writer = new(outPipe, NamedPipesHost.UTF8NoBOM, bufferSize: 1) { AutoFlush = true }; while (true) { diff --git a/src/BenchmarkDotNet/Running/BenchmarkId.cs b/src/BenchmarkDotNet/Running/BenchmarkId.cs index 615dcdedd2..fc35f89d87 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkId.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkId.cs @@ -36,7 +36,7 @@ public string ToArguments(Diagnosers.RunMode diagnoserRunMode) => $"--benchmarkName {FullBenchmarkName.EscapeCommandLine()} --job {JobId.EscapeCommandLine()} --diagnoserRunMode {(int) diagnoserRunMode} --benchmarkId {Value}"; public string ToArguments(string fromBenchmark, string toBenchmark, Diagnosers.RunMode diagnoserRunMode) - => $"{NamedPipeHost.PipeNamesDescriptor} {fromBenchmark} {toBenchmark} {ToArguments(diagnoserRunMode)}"; + => $"{NamedPipesHost.PipeNamesDescriptor} {fromBenchmark} {toBenchmark} {ToArguments(diagnoserRunMode)}"; public override string ToString() => Value.ToString(); diff --git a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt index bebce6403a..736073f670 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt @@ -25,7 +25,7 @@ namespace BenchmarkDotNet.Autogenerated private static async global::System.Threading.Tasks.ValueTask MainCore(global::System.String[] args) { // This variable name is used by CodeGenerator.GetBenchmarkRunCall, do NOT change it! - global::BenchmarkDotNet.Engines.IHost host = await global::BenchmarkDotNet.Engines.NamedPipeHost.GetHostAsync(args); + global::BenchmarkDotNet.Engines.IHost host = await global::BenchmarkDotNet.Engines.NamedPipesHost.GetHostAsync(args); // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs index bc3256aa78..57b4fad190 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs @@ -66,8 +66,8 @@ private async ValueTask Execute(BenchmarkCase benchmarkCase, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { - using var fromBenchmarkPipe = NamedPipeHost.GetPipeServerStream(benchmarkId, PipeDirection.In, out string fromBenchmarkPipeName); - using var toBenchmarkPipe = NamedPipeHost.GetPipeServerStream(benchmarkId, PipeDirection.Out, out string toBenchmarkPipeName); + using var fromBenchmarkPipe = NamedPipesHost.GetPipeServerStream(benchmarkId, PipeDirection.In, out string fromBenchmarkPipeName); + using var toBenchmarkPipe = NamedPipesHost.GetPipeServerStream(benchmarkId, PipeDirection.Out, out string toBenchmarkPipeName); var startInfo = DotNetCliCommandExecutor.BuildStartInfo( customDotNetCliPath, diff --git a/src/BenchmarkDotNet/Toolchains/Executor.cs b/src/BenchmarkDotNet/Toolchains/Executor.cs index a05ae60bf5..2a61937bf2 100644 --- a/src/BenchmarkDotNet/Toolchains/Executor.cs +++ b/src/BenchmarkDotNet/Toolchains/Executor.cs @@ -59,8 +59,8 @@ private static async ValueTask ExecuteCore(BenchmarkCase benchmar IDiagnoser? diagnoser, CompositeInProcessDiagnoser compositeInProcessDiagnoser, IResolver resolver, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { - using var fromBenchmarkPipe = NamedPipeHost.GetPipeServerStream(benchmarkId, PipeDirection.In, out string fromBenchmarkPipeName); - using var toBenchmarkPipe = NamedPipeHost.GetPipeServerStream(benchmarkId, PipeDirection.Out, out string toBenchmarkPipeName); + using var fromBenchmarkPipe = NamedPipesHost.GetPipeServerStream(benchmarkId, PipeDirection.In, out string fromBenchmarkPipeName); + using var toBenchmarkPipe = NamedPipesHost.GetPipeServerStream(benchmarkId, PipeDirection.Out, out string toBenchmarkPipeName); string args = benchmarkId.ToArguments(fromBenchmarkPipeName, toBenchmarkPipeName, diagnoserRunMode); From d3fdd8ecea5c7c933eda7768cce6d41b7571f9e0 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Wed, 11 Feb 2026 19:12:05 -0500 Subject: [PATCH 20/33] Try read/write synchronously in the host. --- src/BenchmarkDotNet/Engines/NamedPipesHost.cs | 18 ++++++++---------- src/BenchmarkDotNet/Loggers/Broker.cs | 4 ++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/NamedPipesHost.cs b/src/BenchmarkDotNet/Engines/NamedPipesHost.cs index 16924a3c7c..2fd71ae2cb 100644 --- a/src/BenchmarkDotNet/Engines/NamedPipesHost.cs +++ b/src/BenchmarkDotNet/Engines/NamedPipesHost.cs @@ -41,13 +41,13 @@ public void Dispose() } public async ValueTask WriteAsync(string message) - => await outWriter.WriteAsync(message); + => outWriter.Write(message); public async ValueTask WriteLineAsync() - => await outWriter.WriteLineAsync(); + => outWriter.WriteLine(); public async ValueTask WriteLineAsync(string message) - => await outWriter.WriteLineAsync(message); + => outWriter.WriteLine(message); public async ValueTask SendSignalAsync(HostSignal hostSignal) { @@ -58,10 +58,10 @@ public async ValueTask SendSignalAsync(HostSignal hostSignal) System.Threading.Thread.Sleep(1); } - await WriteLineAsync(Engine.Signals.ToMessage(hostSignal)); + outWriter.WriteLine(Engine.Signals.ToMessage(hostSignal)); // Read the response from Parent process. - string? acknowledgment = await inReader.ReadLineAsync(); + string? acknowledgment = inReader.ReadLine(); if (acknowledgment != Engine.Signals.Acknowledgment && !(acknowledgment is null && hostSignal == HostSignal.AfterAll)) // an early EOF, but still valid { @@ -70,7 +70,7 @@ public async ValueTask SendSignalAsync(HostSignal hostSignal) } public async ValueTask SendErrorAsync(string message) - => await outWriter.WriteLineAsync($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + => outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); public ValueTask ReportResultsAsync(RunResults runResults) => runResults.WriteAsync(this); @@ -86,10 +86,8 @@ public static async ValueTask GetHostAsync(string[] args) var toBenchmarkPipeName = args[i + 2]; var fromBenchmarkPipe = new NamedPipeClientStream(".", fromBenchmarkPipeName, PipeDirection.Out, PipeOptions.Asynchronous); var toBenchmarkPipe = new NamedPipeClientStream(".", toBenchmarkPipeName, PipeDirection.In, PipeOptions.Asynchronous); - await Task.WhenAll([ - fromBenchmarkPipe.ConnectAsync((int) PipeConnectionTimeout.TotalMilliseconds), - toBenchmarkPipe.ConnectAsync((int) PipeConnectionTimeout.TotalMilliseconds) - ]); + fromBenchmarkPipe.Connect((int) PipeConnectionTimeout.TotalMilliseconds); + toBenchmarkPipe.Connect((int) PipeConnectionTimeout.TotalMilliseconds); return new NamedPipesHost(fromBenchmarkPipe, toBenchmarkPipe); } } diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 4c57720fd7..3155a00d7c 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -155,6 +155,8 @@ await Task.WhenAll([ { Diagnoser?.Handle(signal, DiagnoserActionParameters); + await writer.WriteLineAsync(Engine.Signals.Acknowledgment); + if (signal == HostSignal.AfterAll) { // we have received the last signal so we can stop reading from the pipe @@ -162,8 +164,6 @@ await Task.WhenAll([ return Result.Success; } - await writer.WriteLineAsync(Engine.Signals.Acknowledgment); - continue; } From fa7ecbb942403f21cdd78628ead6abebae8d619f Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Wed, 11 Feb 2026 21:34:22 -0500 Subject: [PATCH 21/33] Read/write synchronously on both ends. --- src/BenchmarkDotNet/Loggers/Broker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 3155a00d7c..2386c65f61 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -105,7 +105,7 @@ await Task.WhenAll([ while (true) { - var line = await reader.ReadLineAsync(); + var line = reader.ReadLine(); if (line == null) return Result.EndOfStream; @@ -131,7 +131,7 @@ await Task.WhenAll([ var resultsStringBuilder = new StringBuilder(); for (int i = 0; i < resultsLinesCount;) { - line = await reader.ReadLineAsync(); + line = reader.ReadLine(); if (line == null) return Result.EndOfStream; @@ -155,7 +155,7 @@ await Task.WhenAll([ { Diagnoser?.Handle(signal, DiagnoserActionParameters); - await writer.WriteLineAsync(Engine.Signals.Acknowledgment); + writer.WriteLine(Engine.Signals.Acknowledgment); if (signal == HostSignal.AfterAll) { From 00dc6d226514524aa017d7f2a1885e0298ca3238 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Fri, 13 Feb 2026 08:44:45 -0500 Subject: [PATCH 22/33] Change named pipes to TCP loopback. --- src/BenchmarkDotNet/Engines/NamedPipesHost.cs | 129 -------------- src/BenchmarkDotNet/Engines/TcpHost.cs | 105 ++++++++++++ src/BenchmarkDotNet/Loggers/Broker.cs | 159 +++++++++--------- src/BenchmarkDotNet/Running/BenchmarkId.cs | 4 +- .../Templates/BenchmarkProgram.txt | 2 +- .../Toolchains/DotNetCli/DotNetCliExecutor.cs | 107 ++++++------ src/BenchmarkDotNet/Toolchains/Executor.cs | 107 ++++++------ 7 files changed, 304 insertions(+), 309 deletions(-) delete mode 100644 src/BenchmarkDotNet/Engines/NamedPipesHost.cs create mode 100644 src/BenchmarkDotNet/Engines/TcpHost.cs diff --git a/src/BenchmarkDotNet/Engines/NamedPipesHost.cs b/src/BenchmarkDotNet/Engines/NamedPipesHost.cs deleted file mode 100644 index 2fd71ae2cb..0000000000 --- a/src/BenchmarkDotNet/Engines/NamedPipesHost.cs +++ /dev/null @@ -1,129 +0,0 @@ -using BenchmarkDotNet.Attributes.CompilerServices; -using BenchmarkDotNet.Detectors; -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Validators; -using JetBrains.Annotations; -using System; -using System.ComponentModel; -using System.IO; -using System.IO.Pipes; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -#nullable enable - -namespace BenchmarkDotNet.Engines; - -[AggressivelyOptimizeMethods] -[UsedImplicitly] -[EditorBrowsable(EditorBrowsableState.Never)] -public class NamedPipesHost : IHost -{ - internal const string PipeNamesDescriptor = "--pipeNames"; - internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - public static readonly TimeSpan PipeConnectionTimeout = TimeSpan.FromMinutes(1); - - private readonly StreamWriter outWriter; - private readonly StreamReader inReader; - - public NamedPipesHost(NamedPipeClientStream fromBenchmarkPipe, NamedPipeClientStream toBenchmarkPipe) - { - // Flush the data to the Stream after each write, otherwise the host process will wait for input endlessly! - outWriter = new(fromBenchmarkPipe, UTF8NoBOM) { AutoFlush = true }; - inReader = new(toBenchmarkPipe, UTF8NoBOM, detectEncodingFromByteOrderMarks: false); - } - - public void Dispose() - { - outWriter.Dispose(); - inReader.Dispose(); - } - - public async ValueTask WriteAsync(string message) - => outWriter.Write(message); - - public async ValueTask WriteLineAsync() - => outWriter.WriteLine(); - - public async ValueTask WriteLineAsync(string message) - => outWriter.WriteLine(message); - - public async ValueTask SendSignalAsync(HostSignal hostSignal) - { - if (hostSignal == HostSignal.AfterAll) - { - // Before the last signal is reported and the benchmark process exits, - // add an artificial sleep to increase the chance of host process reading all std output. - System.Threading.Thread.Sleep(1); - } - - outWriter.WriteLine(Engine.Signals.ToMessage(hostSignal)); - - // Read the response from Parent process. - string? acknowledgment = inReader.ReadLine(); - if (acknowledgment != Engine.Signals.Acknowledgment - && !(acknowledgment is null && hostSignal == HostSignal.AfterAll)) // an early EOF, but still valid - { - throw new NotSupportedException($"Unknown Acknowledgment: {acknowledgment}"); - } - } - - public async ValueTask SendErrorAsync(string message) - => outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); - - public ValueTask ReportResultsAsync(RunResults runResults) - => runResults.WriteAsync(this); - - public static async ValueTask GetHostAsync(string[] args) - { - for (int i = 0; i < args.Length; i++) - { - if (args[i] == PipeNamesDescriptor) - { - // IndexOutOfRangeException means a bug (incomplete data) - var fromBenchmarkPipeName = args[i + 1]; - var toBenchmarkPipeName = args[i + 2]; - var fromBenchmarkPipe = new NamedPipeClientStream(".", fromBenchmarkPipeName, PipeDirection.Out, PipeOptions.Asynchronous); - var toBenchmarkPipe = new NamedPipeClientStream(".", toBenchmarkPipeName, PipeDirection.In, PipeOptions.Asynchronous); - fromBenchmarkPipe.Connect((int) PipeConnectionTimeout.TotalMilliseconds); - toBenchmarkPipe.Connect((int) PipeConnectionTimeout.TotalMilliseconds); - return new NamedPipesHost(fromBenchmarkPipe, toBenchmarkPipe); - } - } - return new NoAcknowledgementConsoleHost(); - } - - public static NamedPipeServerStream GetPipeServerStream(BenchmarkId benchmarkId, PipeDirection pipeDirection, out string pipeName) - { - int retryCounter = 0; - while (true) - { - try - { - // MacOS has a small character limit, so we use random file name instead of guid. - pipeName = $"BDN-{benchmarkId.Value}-{pipeDirection}-{Path.GetRandomFileName().Replace(".", "")}"; - var pipe = new NamedPipeServerStream(pipeName, pipeDirection, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); - // Ensure the pipe handle is not inherited to prevent hangs on Windows. - if (OsDetector.IsWindows()) - { - SetHandleInformation(pipe.SafePipeHandle.DangerousGetHandle(), HANDLE_FLAG_INHERIT, 0); - } - return pipe; - } - catch (IOException) - { - // Rare case where the pipe name is already in use, retry up to 5 times. - if (++retryCounter >= 5) - { - throw; - } - } - } - } - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool SetHandleInformation(IntPtr hObject, int dwMask, int dwFlags); - - const int HANDLE_FLAG_INHERIT = 1; -} diff --git a/src/BenchmarkDotNet/Engines/TcpHost.cs b/src/BenchmarkDotNet/Engines/TcpHost.cs new file mode 100644 index 0000000000..87dc889eee --- /dev/null +++ b/src/BenchmarkDotNet/Engines/TcpHost.cs @@ -0,0 +1,105 @@ +using BenchmarkDotNet.Attributes.CompilerServices; +using BenchmarkDotNet.Validators; +using JetBrains.Annotations; +using System; +using System.ComponentModel; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable + +namespace BenchmarkDotNet.Engines; + +[AggressivelyOptimizeMethods] +[UsedImplicitly] +[EditorBrowsable(EditorBrowsableState.Never)] +public class TcpHost : IHost +{ + internal const string TcpPortDescriptor = "--tcpPort"; + internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + public static readonly TimeSpan ConnectionTimeout = TimeSpan.FromMinutes(1); + + private readonly StreamWriter outWriter; + private readonly StreamReader inReader; + + public TcpHost(TcpClient client) + { + var stream = client.GetStream(); + // Flush the data to the Stream after each write, otherwise the host process will wait for input endlessly! + outWriter = new(stream, UTF8NoBOM) { AutoFlush = true }; + inReader = new(stream, UTF8NoBOM, detectEncodingFromByteOrderMarks: false); + } + + public void Dispose() + { + outWriter.Dispose(); + inReader.Dispose(); + } + + public async ValueTask WriteAsync(string message) + => await outWriter.WriteAsync(message); + + public async ValueTask WriteLineAsync() + => await outWriter.WriteLineAsync(); + + public async ValueTask WriteLineAsync(string message) + => await outWriter.WriteLineAsync(message); + + public async ValueTask SendSignalAsync(HostSignal hostSignal) + { + if (hostSignal == HostSignal.AfterAll) + { + // Before the last signal is reported and the benchmark process exits, + // add an artificial sleep to increase the chance of host process reading all std output. + Thread.Sleep(1); + } + + await outWriter.WriteLineAsync(Engine.Signals.ToMessage(hostSignal)); + + // Read the response from Parent process. + string? acknowledgment = await inReader.ReadLineAsync(); + if (acknowledgment != Engine.Signals.Acknowledgment + && !(acknowledgment is null && hostSignal == HostSignal.AfterAll)) // an early EOF, but still valid + { + throw new NotSupportedException($"Unknown Acknowledgment: {acknowledgment}"); + } + } + + public async ValueTask SendErrorAsync(string message) + => await outWriter.WriteLineAsync($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + + public ValueTask ReportResultsAsync(RunResults runResults) + => runResults.WriteAsync(this); + + public static async ValueTask GetHostAsync(string[] args) + { + for (int i = 0; i < args.Length; i++) + { + if (args[i] == TcpPortDescriptor) + { + int port = int.Parse(args[i + 1]); + var client = new TcpClient(); +#if NETSTANDARD2_0 + client.Connect(IPAddress.Loopback, port); + await client.ConnectAsync(IPAddress.Loopback, port); +#else + try + { + using var cts = new CancellationTokenSource(ConnectionTimeout); + await client.ConnectAsync(IPAddress.Loopback, port, cts.Token); + } + catch (OperationCanceledException) + { + throw new TimeoutException($"The connection to the host process timed out after {ConnectionTimeout}."); + } +#endif + return new TcpHost(client); + } + } + return new NoAcknowledgementConsoleHost(); + } +} diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 2386c65f61..f6fc9b4b7d 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -20,8 +20,7 @@ internal class Broker : IDisposable private readonly ILogger logger; private readonly Process process; private readonly CompositeInProcessDiagnoser compositeInProcessDiagnoser; - private NamedPipeServerStream? inPipe; - private NamedPipeServerStream? outPipe; + private TcpListener? tcpListener; private enum Result { @@ -32,14 +31,13 @@ private enum Result } public Broker(ILogger logger, Process process, IDiagnoser? diagnoser, CompositeInProcessDiagnoser compositeInProcessDiagnoser, - BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, NamedPipeServerStream inPipe, NamedPipeServerStream outPipe) + BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, TcpListener tcpListener) { this.logger = logger; this.process = process; this.Diagnoser = diagnoser; this.compositeInProcessDiagnoser = compositeInProcessDiagnoser; - this.inPipe = inPipe; - this.outPipe = outPipe; + this.tcpListener = tcpListener; DiagnoserActionParameters = new DiagnoserActionParameters(process, benchmarkCase, benchmarkId); process.EnableRaisingEvents = true; @@ -58,9 +56,7 @@ public void Dispose() { process.Exited -= OnProcessExited; - // Dispose the pipes to let reading from pipe finish with EOF and avoid a resource leak. - Interlocked.Exchange(ref inPipe, null)?.Dispose(); - Interlocked.Exchange(ref outPipe, null)?.Dispose(); + Interlocked.Exchange(ref tcpListener, null)?.Stop(); } private void OnProcessExited(object? sender, EventArgs e) @@ -77,98 +73,103 @@ internal async ValueTask ProcessData() private async ValueTask ProcessDataCore() { - if (process.HasExited || this.inPipe is not { } inPipe || this.outPipe is not { } outPipe) + if (process.HasExited || this.tcpListener is not { } tcpListener) return Result.EarlyProcessExit; try { - using var cts = new CancellationTokenSource(NamedPipesHost.PipeConnectionTimeout); - await Task.WhenAll([ - inPipe.WaitForConnectionAsync(cts.Token), - outPipe.WaitForConnectionAsync(cts.Token)] - ); - } - catch (OperationCanceledException) - { - throw new TimeoutException($"The connection to the benchmark process timed out after {NamedPipesHost.PipeConnectionTimeout}."); - } - // If the process exited before the connection was established such that the pipe is disposed from the exited handler, - // it throws IOException on Windows or SocketException on Unix. - catch (Exception e) when (e is IOException or SocketException) - { - return Result.EarlyProcessExit; - } - - using StreamReader reader = new(inPipe, NamedPipesHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); - // Flush the data to the Stream after each write, otherwise the client will wait for input endlessly! - using StreamWriter writer = new(outPipe, NamedPipesHost.UTF8NoBOM, bufferSize: 1) { AutoFlush = true }; - - while (true) - { - var line = reader.ReadLine(); - if (line == null) - return Result.EndOfStream; - - // TODO: implement Silent mode here - logger.WriteLine(LogKind.Default, line); - - // Handle normal log. - if (!line.StartsWith("//")) +#if NETSTANDARD2_0 + using var client = await tcpListener.AcceptTcpClientAsync(); +#else + TcpClient client; + try { - Results.Add(line); - continue; + using var cts = new CancellationTokenSource(TcpHost.ConnectionTimeout); + client = await tcpListener.AcceptTcpClientAsync(cts.Token); } + catch (OperationCanceledException) + { + throw new TimeoutException($"The connection to the benchmark process timed out after {TcpHost.ConnectionTimeout}."); + } + using var _ = client; +#endif + using var stream = client.GetStream(); - // Keep in sync with WasmExecutor and InProcessHost. + using StreamReader reader = new(stream, TcpHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); + // Flush the data to the Stream after each write, otherwise the client will wait for input endlessly! + using StreamWriter writer = new(stream, TcpHost.UTF8NoBOM, bufferSize: 1) { AutoFlush = true }; - // Handle line prefixed with "// InProcessDiagnoser " - if (line.StartsWith(CompositeInProcessDiagnoser.HeaderKey)) + while (true) { - // Something like "// InProcessDiagnoser 0 1" - string[] lineItems = line.Split(' '); - int diagnoserIndex = int.Parse(lineItems[2]); - int resultsLinesCount = int.Parse(lineItems[3]); - var resultsStringBuilder = new StringBuilder(); - for (int i = 0; i < resultsLinesCount;) + var line = await reader.ReadLineAsync(); + if (line == null) + return Result.EndOfStream; + + // TODO: implement Silent mode here + logger.WriteLine(LogKind.Default, line); + + // Handle normal log. + if (!line.StartsWith("//")) { - line = reader.ReadLine(); - if (line == null) - return Result.EndOfStream; + Results.Add(line); + continue; + } - if (!line.StartsWith($"{CompositeInProcessDiagnoser.ResultsKey} ")) - return Result.InvalidData; + // Keep in sync with WasmExecutor and InProcessHost. - // Strip the prepended "// InProcessDiagnoserResults ". - line = line.Substring(CompositeInProcessDiagnoser.ResultsKey.Length + 1); - resultsStringBuilder.Append(line); - if (++i < resultsLinesCount) + // Handle line prefixed with "// InProcessDiagnoser " + if (line.StartsWith(CompositeInProcessDiagnoser.HeaderKey)) + { + // Something like "// InProcessDiagnoser 0 1" + string[] lineItems = line.Split(' '); + int diagnoserIndex = int.Parse(lineItems[2]); + int resultsLinesCount = int.Parse(lineItems[3]); + var resultsStringBuilder = new StringBuilder(); + for (int i = 0; i < resultsLinesCount;) { - resultsStringBuilder.AppendLine(); + line = await reader.ReadLineAsync(); + if (line == null) + return Result.EndOfStream; + + if (!line.StartsWith($"{CompositeInProcessDiagnoser.ResultsKey} ")) + return Result.InvalidData; + + // Strip the prepended "// InProcessDiagnoserResults ". + line = line.Substring(CompositeInProcessDiagnoser.ResultsKey.Length + 1); + resultsStringBuilder.Append(line); + if (++i < resultsLinesCount) + { + resultsStringBuilder.AppendLine(); + } } + compositeInProcessDiagnoser.DeserializeResults(diagnoserIndex, DiagnoserActionParameters.BenchmarkCase, resultsStringBuilder.ToString()); + continue; } - compositeInProcessDiagnoser.DeserializeResults(diagnoserIndex, DiagnoserActionParameters.BenchmarkCase, resultsStringBuilder.ToString()); - continue; - } - // Handle HostSignal data - if (Engine.Signals.TryGetSignal(line, out var signal)) - { - Diagnoser?.Handle(signal, DiagnoserActionParameters); + // Handle HostSignal data + if (Engine.Signals.TryGetSignal(line, out var signal)) + { + Diagnoser?.Handle(signal, DiagnoserActionParameters); - writer.WriteLine(Engine.Signals.Acknowledgment); + await writer.WriteLineAsync(Engine.Signals.Acknowledgment); - if (signal == HostSignal.AfterAll) - { - // we have received the last signal so we can stop reading from the pipe - // if the process won't exit after this, its hung and needs to be killed - return Result.Success; + if (signal == HostSignal.AfterAll) + { + // we have received the last signal so we can stop reading from the pipe + // if the process won't exit after this, its hung and needs to be killed + return Result.Success; + } + + continue; } - continue; + // Other line that have "//" prefix. + PrefixedOutput.Add(line); } - - // Other line that have "//" prefix. - PrefixedOutput.Add(line); + } + catch (SocketException e) when (e.ErrorCode == 10004) // The socket was closed. + { + return Result.EarlyProcessExit; } } } diff --git a/src/BenchmarkDotNet/Running/BenchmarkId.cs b/src/BenchmarkDotNet/Running/BenchmarkId.cs index fc35f89d87..fb349e417b 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkId.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkId.cs @@ -35,8 +35,8 @@ public BenchmarkId(int value, BenchmarkCase benchmarkCase) public string ToArguments(Diagnosers.RunMode diagnoserRunMode) => $"--benchmarkName {FullBenchmarkName.EscapeCommandLine()} --job {JobId.EscapeCommandLine()} --diagnoserRunMode {(int) diagnoserRunMode} --benchmarkId {Value}"; - public string ToArguments(string fromBenchmark, string toBenchmark, Diagnosers.RunMode diagnoserRunMode) - => $"{NamedPipesHost.PipeNamesDescriptor} {fromBenchmark} {toBenchmark} {ToArguments(diagnoserRunMode)}"; + public string ToArguments(int port, Diagnosers.RunMode diagnoserRunMode) + => $"{TcpHost.TcpPortDescriptor} {port} {ToArguments(diagnoserRunMode)}"; public override string ToString() => Value.ToString(); diff --git a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt index 736073f670..22ab9b1bb5 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt @@ -25,7 +25,7 @@ namespace BenchmarkDotNet.Autogenerated private static async global::System.Threading.Tasks.ValueTask MainCore(global::System.String[] args) { // This variable name is used by CodeGenerator.GetBenchmarkRunCall, do NOT change it! - global::BenchmarkDotNet.Engines.IHost host = await global::BenchmarkDotNet.Engines.NamedPipesHost.GetHostAsync(args); + global::BenchmarkDotNet.Engines.IHost host = await global::BenchmarkDotNet.Engines.TcpHost.GetHostAsync(args); // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs index 57b4fad190..f0b347e2ff 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs @@ -2,6 +2,8 @@ using System.Diagnostics; using System.IO; using System.IO.Pipes; +using System.Net; +using System.Net.Sockets; using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Diagnosers; @@ -66,68 +68,75 @@ private async ValueTask Execute(BenchmarkCase benchmarkCase, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { - using var fromBenchmarkPipe = NamedPipesHost.GetPipeServerStream(benchmarkId, PipeDirection.In, out string fromBenchmarkPipeName); - using var toBenchmarkPipe = NamedPipesHost.GetPipeServerStream(benchmarkId, PipeDirection.Out, out string toBenchmarkPipeName); - - var startInfo = DotNetCliCommandExecutor.BuildStartInfo( - customDotNetCliPath, - artifactsPaths.BinariesDirectoryPath, - $"{executableName.EscapeCommandLine()} {benchmarkId.ToArguments(fromBenchmarkPipeName, toBenchmarkPipeName, diagnoserRunMode)}", - redirectStandardOutput: true, - redirectStandardInput: false, - redirectStandardError: false); // #1629 - - startInfo.SetEnvironmentVariables(benchmarkCase, resolver); - - using Process process = new() { StartInfo = startInfo }; - using ConsoleExitHandler consoleExitHandler = new(process, logger); - using AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false); - - List results; - List prefixedOutput; - using (Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, fromBenchmarkPipe, toBenchmarkPipe)) + var tcplistener = new TcpListener(IPAddress.Loopback, port: 0); + try { - logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); - - diagnoser?.Handle(HostSignal.BeforeProcessStart, broker.DiagnoserActionParameters); + tcplistener.Start(1); - process.Start(); + var startInfo = DotNetCliCommandExecutor.BuildStartInfo( + customDotNetCliPath, + artifactsPaths.BinariesDirectoryPath, + $"{executableName.EscapeCommandLine()} {benchmarkId.ToArguments(((IPEndPoint) tcplistener.LocalEndpoint).Port, diagnoserRunMode)}", + redirectStandardOutput: true, + redirectStandardInput: false, + redirectStandardError: false); // #1629 - diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); + startInfo.SetEnvironmentVariables(benchmarkCase, resolver); - processOutputReader.BeginRead(); + using Process process = new() { StartInfo = startInfo }; + using ConsoleExitHandler consoleExitHandler = new(process, logger); + using AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false); - process.EnsureHighPriority(logger); - if (benchmarkCase.Job.Environment.HasValue(EnvironmentMode.AffinityCharacteristic)) + List results; + List prefixedOutput; + using (Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, tcplistener)) { - process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); - } + logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); - await broker.ProcessData(); + diagnoser?.Handle(HostSignal.BeforeProcessStart, broker.DiagnoserActionParameters); - results = broker.Results; - prefixedOutput = broker.PrefixedOutput; - } + process.Start(); - if (!process.WaitForExit(milliseconds: (int) ExecuteParameters.ProcessExitTimeout.TotalMilliseconds)) - { - logger.WriteLineInfo($"// The benchmarking process did not quit within {ExecuteParameters.ProcessExitTimeout.TotalSeconds} seconds, it's going to get force killed now."); + diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); + + processOutputReader.BeginRead(); + + process.EnsureHighPriority(logger); + if (benchmarkCase.Job.Environment.HasValue(EnvironmentMode.AffinityCharacteristic)) + { + process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); + } + + await broker.ProcessData(); + + results = broker.Results; + prefixedOutput = broker.PrefixedOutput; + } + + if (!process.WaitForExit(milliseconds: (int) ExecuteParameters.ProcessExitTimeout.TotalMilliseconds)) + { + logger.WriteLineInfo($"// The benchmarking process did not quit within {ExecuteParameters.ProcessExitTimeout.TotalSeconds} seconds, it's going to get force killed now."); - processOutputReader.CancelRead(); - consoleExitHandler.KillProcessTree(); + processOutputReader.CancelRead(); + consoleExitHandler.KillProcessTree(); + } + else + { + processOutputReader.StopRead(); + } + + return new ExecuteResult(true, + process.HasExited ? process.ExitCode : null, + process.Id, + results, + prefixedOutput, + processOutputReader.GetOutputLines(), + launchIndex); } - else + finally { - processOutputReader.StopRead(); + tcplistener.Stop(); } - - return new ExecuteResult(true, - process.HasExited ? process.ExitCode : null, - process.Id, - results, - prefixedOutput, - processOutputReader.GetOutputLines(), - launchIndex); } } } diff --git a/src/BenchmarkDotNet/Toolchains/Executor.cs b/src/BenchmarkDotNet/Toolchains/Executor.cs index 2a61937bf2..355af945cd 100644 --- a/src/BenchmarkDotNet/Toolchains/Executor.cs +++ b/src/BenchmarkDotNet/Toolchains/Executor.cs @@ -5,6 +5,8 @@ using System.IO; using System.IO.Pipes; using System.Linq; +using System.Net; +using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using BenchmarkDotNet.Characteristics; @@ -59,72 +61,79 @@ private static async ValueTask ExecuteCore(BenchmarkCase benchmar IDiagnoser? diagnoser, CompositeInProcessDiagnoser compositeInProcessDiagnoser, IResolver resolver, int launchIndex, Diagnosers.RunMode diagnoserRunMode) { - using var fromBenchmarkPipe = NamedPipesHost.GetPipeServerStream(benchmarkId, PipeDirection.In, out string fromBenchmarkPipeName); - using var toBenchmarkPipe = NamedPipesHost.GetPipeServerStream(benchmarkId, PipeDirection.Out, out string toBenchmarkPipeName); + var tcplistener = new TcpListener(IPAddress.Loopback, port: 0); + try + { + tcplistener.Start(1); - string args = benchmarkId.ToArguments(fromBenchmarkPipeName, toBenchmarkPipeName, diagnoserRunMode); + string args = benchmarkId.ToArguments(((IPEndPoint) tcplistener.LocalEndpoint).Port, diagnoserRunMode); - using Process process = new() { StartInfo = CreateStartInfo(benchmarkCase, artifactsPaths, args, resolver) }; - using ConsoleExitHandler consoleExitHandler = new(process, logger); - using AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false); + using Process process = new() { StartInfo = CreateStartInfo(benchmarkCase, artifactsPaths, args, resolver) }; + using ConsoleExitHandler consoleExitHandler = new(process, logger); + using AsyncProcessOutputReader processOutputReader = new(process, logOutput: true, logger, readStandardError: false); - List results; - List prefixedOutput; - using (Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, fromBenchmarkPipe, toBenchmarkPipe)) - { - diagnoser?.Handle(HostSignal.BeforeProcessStart, new DiagnoserActionParameters(process, benchmarkCase, benchmarkId)); + List results; + List prefixedOutput; + using (Broker broker = new(logger, process, diagnoser, compositeInProcessDiagnoser, benchmarkCase, benchmarkId, tcplistener)) + { + diagnoser?.Handle(HostSignal.BeforeProcessStart, new DiagnoserActionParameters(process, benchmarkCase, benchmarkId)); - logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); + logger.WriteLineInfo($"// Execute: {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}"); - try - { - process.Start(); - } - catch (Win32Exception ex) - { - logger.WriteLineError($"// Failed to start the benchmark process: {ex}"); + try + { + process.Start(); + } + catch (Win32Exception ex) + { + logger.WriteLineError($"// Failed to start the benchmark process: {ex}"); - return new ExecuteResult(true, null, null, [], [], [], launchIndex); - } + return new ExecuteResult(true, null, null, [], [], [], launchIndex); + } - broker.Diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); + broker.Diagnoser?.Handle(HostSignal.AfterProcessStart, broker.DiagnoserActionParameters); - processOutputReader.BeginRead(); + processOutputReader.BeginRead(); - process.EnsureHighPriority(logger); - if (benchmarkCase.Job.Environment.HasValue(EnvironmentMode.AffinityCharacteristic)) - { - process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); + process.EnsureHighPriority(logger); + if (benchmarkCase.Job.Environment.HasValue(EnvironmentMode.AffinityCharacteristic)) + { + process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); + } + + await broker.ProcessData(); + + results = broker.Results; + prefixedOutput = broker.PrefixedOutput; } - await broker.ProcessData(); + if (!process.WaitForExit(milliseconds: (int) ExecuteParameters.ProcessExitTimeout.TotalMilliseconds)) + { + logger.WriteLineInfo("// The benchmarking process did not quit on time, it's going to get force killed now."); - results = broker.Results; - prefixedOutput = broker.PrefixedOutput; - } + processOutputReader.CancelRead(); + consoleExitHandler.KillProcessTree(); + } + else + { + processOutputReader.StopRead(); + } - if (!process.WaitForExit(milliseconds: (int)ExecuteParameters.ProcessExitTimeout.TotalMilliseconds)) - { - logger.WriteLineInfo("// The benchmarking process did not quit on time, it's going to get force killed now."); + if (results.Any(line => line.Contains("BadImageFormatException"))) + logger.WriteLineError("You are probably missing AnyCPU in your .csproj file."); - processOutputReader.CancelRead(); - consoleExitHandler.KillProcessTree(); + return new ExecuteResult(true, + process.HasExited ? process.ExitCode : null, + process.Id, + results, + prefixedOutput, + processOutputReader.GetOutputLines(), + launchIndex); } - else + finally { - processOutputReader.StopRead(); + tcplistener.Stop(); } - - if (results.Any(line => line.Contains("BadImageFormatException"))) - logger.WriteLineError("You are probably missing AnyCPU in your .csproj file."); - - return new ExecuteResult(true, - process.HasExited ? process.ExitCode : null, - process.Id, - results, - prefixedOutput, - processOutputReader.GetOutputLines(), - launchIndex); } private static ProcessStartInfo CreateStartInfo(BenchmarkCase benchmarkCase, ArtifactsPaths artifactsPaths, string args, IResolver resolver) From 4358883bb0573a4e7e5e70217d323b213dc2410d Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Fri, 13 Feb 2026 09:24:54 -0500 Subject: [PATCH 23/33] Remove extra connect call. Catch known socket errors for early child process exit. --- src/BenchmarkDotNet/Engines/TcpHost.cs | 1 - src/BenchmarkDotNet/Loggers/Broker.cs | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/TcpHost.cs b/src/BenchmarkDotNet/Engines/TcpHost.cs index 87dc889eee..5a8af48381 100644 --- a/src/BenchmarkDotNet/Engines/TcpHost.cs +++ b/src/BenchmarkDotNet/Engines/TcpHost.cs @@ -84,7 +84,6 @@ public static async ValueTask GetHostAsync(string[] args) int port = int.Parse(args[i + 1]); var client = new TcpClient(); #if NETSTANDARD2_0 - client.Connect(IPAddress.Loopback, port); await client.ConnectAsync(IPAddress.Loopback, port); #else try diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index f6fc9b4b7d..091de255d8 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.IO.Pipes; using System.Net.Sockets; using System.Text; using System.Threading; @@ -167,10 +166,19 @@ private async ValueTask ProcessDataCore() PrefixedOutput.Add(line); } } - catch (SocketException e) when (e.ErrorCode == 10004) // The socket was closed. + catch (IOException e) when (e.InnerException is SocketException se && IsEarlyExitCode(se.SocketErrorCode)) { return Result.EarlyProcessExit; } + catch (SocketException e) when (IsEarlyExitCode(e.SocketErrorCode)) + { + return Result.EarlyProcessExit; + } + + static bool IsEarlyExitCode(SocketError error) + => error is SocketError.ConnectionReset + or SocketError.Shutdown + or SocketError.OperationAborted; } } } From 4dfa2ce88cb914c17c414398d5b3b678986a8c08 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 14 Feb 2026 02:36:25 -0500 Subject: [PATCH 24/33] Use synchronous read/write in child, synchronous write in parent. --- src/BenchmarkDotNet/Engines/TcpHost.cs | 12 ++++++------ src/BenchmarkDotNet/Loggers/Broker.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/TcpHost.cs b/src/BenchmarkDotNet/Engines/TcpHost.cs index 5a8af48381..205008371d 100644 --- a/src/BenchmarkDotNet/Engines/TcpHost.cs +++ b/src/BenchmarkDotNet/Engines/TcpHost.cs @@ -41,13 +41,13 @@ public void Dispose() } public async ValueTask WriteAsync(string message) - => await outWriter.WriteAsync(message); + => outWriter.Write(message); public async ValueTask WriteLineAsync() - => await outWriter.WriteLineAsync(); + => outWriter.WriteLine(); public async ValueTask WriteLineAsync(string message) - => await outWriter.WriteLineAsync(message); + => outWriter.WriteLine(message); public async ValueTask SendSignalAsync(HostSignal hostSignal) { @@ -58,10 +58,10 @@ public async ValueTask SendSignalAsync(HostSignal hostSignal) Thread.Sleep(1); } - await outWriter.WriteLineAsync(Engine.Signals.ToMessage(hostSignal)); + outWriter.WriteLine(Engine.Signals.ToMessage(hostSignal)); // Read the response from Parent process. - string? acknowledgment = await inReader.ReadLineAsync(); + string? acknowledgment = inReader.ReadLine(); if (acknowledgment != Engine.Signals.Acknowledgment && !(acknowledgment is null && hostSignal == HostSignal.AfterAll)) // an early EOF, but still valid { @@ -70,7 +70,7 @@ public async ValueTask SendSignalAsync(HostSignal hostSignal) } public async ValueTask SendErrorAsync(string message) - => await outWriter.WriteLineAsync($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + => outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); public ValueTask ReportResultsAsync(RunResults runResults) => runResults.WriteAsync(this); diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 091de255d8..46cc729d7c 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -150,7 +150,7 @@ private async ValueTask ProcessDataCore() { Diagnoser?.Handle(signal, DiagnoserActionParameters); - await writer.WriteLineAsync(Engine.Signals.Acknowledgment); + writer.WriteLine(Engine.Signals.Acknowledgment); if (signal == HostSignal.AfterAll) { From 3de0c6ffc0f443a0c50b74c76f1be66b0acd727f Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 14 Feb 2026 03:49:57 -0500 Subject: [PATCH 25/33] Make tcp host connect synchronously and change IHost methods back to synchronous. --- .../Diagnosers/CompositeDiagnoser.cs | 4 +-- src/BenchmarkDotNet/Engines/Engine.cs | 16 +++++----- src/BenchmarkDotNet/Engines/HostExtensions.cs | 13 ++++---- src/BenchmarkDotNet/Engines/IHost.cs | 11 +++---- .../Engines/NoAcknowledgementConsoleHost.cs | 25 +++++++-------- src/BenchmarkDotNet/Engines/RunResults.cs | 14 +++----- src/BenchmarkDotNet/Engines/TcpHost.cs | 31 ++++++------------ .../Templates/BenchmarkProgram.txt | 26 +++++++-------- .../Templates/BenchmarkType.txt | 10 +++--- .../InProcess/Emit/InProcessEmitRunner.cs | 32 +++++++++---------- .../Toolchains/InProcess/InProcessHost.cs | 20 +++++++----- .../InProcess/NoEmit/InProcessNoEmitRunner.cs | 32 +++++++++---------- .../Validators/ValidationErrorReporter.cs | 2 +- 13 files changed, 110 insertions(+), 126 deletions(-) diff --git a/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs b/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs index 50df371aa2..68b9b693d3 100644 --- a/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs +++ b/src/BenchmarkDotNet/Diagnosers/CompositeDiagnoser.cs @@ -109,10 +109,10 @@ public async ValueTask HandleAsync(BenchmarkSignal signal) // Ideally we would simply use results.Length, write it directly to host, then the host reads the exact count of chars. // But WasmExecutor does not use Broker, and reads all output, so we need to instead use line count and prepend every line with CompositeInProcessDiagnoser.ResultsKey. var resultsLines = results.Split(["\r\n", "\r", "\n"], StringSplitOptions.None); - await host.WriteLineAsync($"{CompositeInProcessDiagnoser.HeaderKey} {router.index} {resultsLines.Length}"); + host.WriteLine($"{CompositeInProcessDiagnoser.HeaderKey} {router.index} {resultsLines.Length}"); foreach (var line in resultsLines) { - await host.WriteLineAsync($"{CompositeInProcessDiagnoser.ResultsKey} {line}"); + host.WriteLine($"{CompositeInProcessDiagnoser.ResultsKey} {line}"); } } } diff --git a/src/BenchmarkDotNet/Engines/Engine.cs b/src/BenchmarkDotNet/Engines/Engine.cs index 6184ab0756..9334844371 100644 --- a/src/BenchmarkDotNet/Engines/Engine.cs +++ b/src/BenchmarkDotNet/Engines/Engine.cs @@ -88,7 +88,7 @@ public async ValueTask RunAsync() // We only catch if the benchmark threw to not overwrite the exception. #1045 catch (Exception e) when (didThrow) { - await Host.SendErrorAsync($"Exception during GlobalCleanup!{Environment.NewLine}{e}"); + Host.SendError($"Exception during GlobalCleanup!{Environment.NewLine}{e}"); } } } @@ -108,7 +108,7 @@ private async ValueTask RunCore() { if (stage.Stage == IterationStage.Actual && stage.Mode == IterationMode.Workload) { - await Host.BeforeMainRunAsync(); + Host.BeforeMainRun(); await Parameters.InProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.BeforeActualRun); } @@ -147,18 +147,18 @@ private async ValueTask RunCore() // Results var measurement = new Measurement(0, iterationData.mode, iterationData.stage, iterationData.index, totalOperations, clockSpan.GetNanoseconds()); - await Host.WriteLineAsync(measurement.ToString()); + Host.WriteLine(measurement.ToString()); stageMeasurements.Add(measurement); // Actual Workload is always the last stage, so we use the same data to run extra stats. extraIterationData = iterationData; } measurements.AddRange(stageMeasurements); - await Host.WriteLineAsync(); + Host.WriteLine(); if (stage.Stage == IterationStage.Actual && stage.Mode == IterationMode.Workload) { - await Host.AfterMainRunAsync(); + Host.AfterMainRun(); await Parameters.InProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.AfterActualRun); } } @@ -172,7 +172,7 @@ private async ValueTask RunCore() await extraIterationData.setupAction!(); // we run iteration setup first, so even if it allocates, it is not included in the results - await Host.SendSignalAsync(HostSignal.BeforeExtraIteration); + Host.SendSignal(HostSignal.BeforeExtraIteration); await Parameters.InProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.BeforeExtraIteration); // GC collect before measuring allocations. @@ -191,13 +191,13 @@ private async ValueTask RunCore() } await Parameters.InProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.AfterExtraIteration); - await Host.SendSignalAsync(HostSignal.AfterExtraIteration); + Host.SendSignal(HostSignal.AfterExtraIteration); await extraIterationData.cleanupAction!(); // we run iteration cleanup after diagnosers are complete. var totalOperations = extraIterationData.invokeCount * Parameters.OperationsPerInvoke; var measurement = new Measurement(0, IterationMode.Workload, IterationStage.Extra, 1, totalOperations, clockSpan.GetNanoseconds()); - await Host.WriteLineAsync(measurement.ToString()); + Host.WriteLine(measurement.ToString()); workGcHasDone = gcStats.WithTotalOperations(totalOperations); measurements.Add(measurement); } diff --git a/src/BenchmarkDotNet/Engines/HostExtensions.cs b/src/BenchmarkDotNet/Engines/HostExtensions.cs index 5e610705c9..41272610a4 100644 --- a/src/BenchmarkDotNet/Engines/HostExtensions.cs +++ b/src/BenchmarkDotNet/Engines/HostExtensions.cs @@ -1,20 +1,19 @@ using JetBrains.Annotations; -using System.Threading.Tasks; namespace BenchmarkDotNet.Engines { public static class HostExtensions { [StringFormatMethod("messageFormat")] - public static ValueTask WriteLineAsync(this IHost host, string messageFormat, params object[] args) - => host.WriteLineAsync(string.Format(messageFormat, args)); + public static void WriteLine(this IHost host, string messageFormat, params object[] args) + => host.WriteLine(string.Format(messageFormat, args)); - public static ValueTask BeforeAnythingElseAsync(this IHost host) => host.SendSignalAsync(HostSignal.BeforeAnythingElse); + public static void BeforeAnythingElse(this IHost host) => host.SendSignal(HostSignal.BeforeAnythingElse); - public static ValueTask BeforeMainRunAsync(this IHost host) => host.SendSignalAsync(HostSignal.BeforeActualRun); + public static void BeforeMainRun(this IHost host) => host.SendSignal(HostSignal.BeforeActualRun); - public static ValueTask AfterMainRunAsync(this IHost host) => host.SendSignalAsync(HostSignal.AfterActualRun); + public static void AfterMainRun(this IHost host) => host.SendSignal(HostSignal.AfterActualRun); - public static ValueTask AfterAllAsync(this IHost host) => host.SendSignalAsync(HostSignal.AfterAll); + public static void AfterAll(this IHost host) => host.SendSignal(HostSignal.AfterAll); } } diff --git a/src/BenchmarkDotNet/Engines/IHost.cs b/src/BenchmarkDotNet/Engines/IHost.cs index 5cad8437a9..25f0b4b957 100644 --- a/src/BenchmarkDotNet/Engines/IHost.cs +++ b/src/BenchmarkDotNet/Engines/IHost.cs @@ -1,7 +1,6 @@ using JetBrains.Annotations; using System; using System.ComponentModel; -using System.Threading.Tasks; namespace BenchmarkDotNet.Engines; @@ -9,11 +8,11 @@ namespace BenchmarkDotNet.Engines; [EditorBrowsable(EditorBrowsableState.Never)] public interface IHost : IDisposable { - ValueTask WriteLineAsync(); - ValueTask WriteLineAsync(string message); + void WriteLine(); + void WriteLine(string message); - ValueTask SendSignalAsync(HostSignal hostSignal); - ValueTask SendErrorAsync(string message); + void SendSignal(HostSignal hostSignal); + void SendError(string message); - ValueTask ReportResultsAsync(RunResults runResults); + void ReportResults(RunResults runResults); } diff --git a/src/BenchmarkDotNet/Engines/NoAcknowledgementConsoleHost.cs b/src/BenchmarkDotNet/Engines/NoAcknowledgementConsoleHost.cs index d4ad568bdc..7a88851644 100644 --- a/src/BenchmarkDotNet/Engines/NoAcknowledgementConsoleHost.cs +++ b/src/BenchmarkDotNet/Engines/NoAcknowledgementConsoleHost.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Threading.Tasks; using BenchmarkDotNet.Attributes.CompilerServices; using BenchmarkDotNet.Validators; @@ -14,23 +13,23 @@ internal sealed class NoAcknowledgementConsoleHost : IHost public NoAcknowledgementConsoleHost() => outWriter = Console.Out; - public async ValueTask WriteAsync(string message) - => await outWriter.WriteAsync(message); + public void WriteAsync(string message) + => outWriter.Write(message); - public async ValueTask WriteLineAsync() - => await outWriter.WriteLineAsync(); + public void WriteLine() + => outWriter.WriteLine(); - public async ValueTask WriteLineAsync(string message) - => await outWriter.WriteLineAsync(message); + public void WriteLine(string message) + => outWriter.WriteLine(message); - public ValueTask SendSignalAsync(HostSignal hostSignal) - => WriteLineAsync(Engine.Signals.ToMessage(hostSignal)); + public void SendSignal(HostSignal hostSignal) + => WriteLine(Engine.Signals.ToMessage(hostSignal)); - public ValueTask SendErrorAsync(string message) - => WriteLineAsync($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + public void SendError(string message) + => WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); - public ValueTask ReportResultsAsync(RunResults runResults) - => runResults.WriteAsync(this); + public void ReportResults(RunResults runResults) + => runResults.Print(outWriter); public void Dispose() { diff --git a/src/BenchmarkDotNet/Engines/RunResults.cs b/src/BenchmarkDotNet/Engines/RunResults.cs index 9d4dbc4ae1..d6e08fa25e 100644 --- a/src/BenchmarkDotNet/Engines/RunResults.cs +++ b/src/BenchmarkDotNet/Engines/RunResults.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Threading.Tasks; using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Mathematics; using BenchmarkDotNet.Reports; @@ -67,19 +67,15 @@ public IEnumerable GetAllMeasurements() yield return measurement; } - internal async ValueTask WriteAsync(IHost host) + public void Print(TextWriter outWriter) { foreach (var measurement in GetWorkloadResultMeasurements()) - { - await host.WriteLineAsync(measurement.ToString()); - } + outWriter.WriteLine(measurement.ToString()); if (!GCStats.Equals(GcStats.Empty)) - { - await host.WriteLineAsync(GCStats.ToOutputLine()); - } + outWriter.WriteLine(GCStats.ToOutputLine()); - await host.WriteLineAsync(); + outWriter.WriteLine(); } // TODO: improve diff --git a/src/BenchmarkDotNet/Engines/TcpHost.cs b/src/BenchmarkDotNet/Engines/TcpHost.cs index 205008371d..01b2ded576 100644 --- a/src/BenchmarkDotNet/Engines/TcpHost.cs +++ b/src/BenchmarkDotNet/Engines/TcpHost.cs @@ -8,7 +8,6 @@ using System.Net.Sockets; using System.Text; using System.Threading; -using System.Threading.Tasks; #nullable enable @@ -40,16 +39,16 @@ public void Dispose() inReader.Dispose(); } - public async ValueTask WriteAsync(string message) + public void WriteAsync(string message) => outWriter.Write(message); - public async ValueTask WriteLineAsync() + public void WriteLine() => outWriter.WriteLine(); - public async ValueTask WriteLineAsync(string message) + public void WriteLine(string message) => outWriter.WriteLine(message); - public async ValueTask SendSignalAsync(HostSignal hostSignal) + public void SendSignal(HostSignal hostSignal) { if (hostSignal == HostSignal.AfterAll) { @@ -69,13 +68,13 @@ public async ValueTask SendSignalAsync(HostSignal hostSignal) } } - public async ValueTask SendErrorAsync(string message) + public void SendError(string message) => outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); - public ValueTask ReportResultsAsync(RunResults runResults) - => runResults.WriteAsync(this); + public void ReportResults(RunResults runResults) + => runResults.Print(outWriter); - public static async ValueTask GetHostAsync(string[] args) + public static IHost GetHost(string[] args) { for (int i = 0; i < args.Length; i++) { @@ -83,19 +82,7 @@ public static async ValueTask GetHostAsync(string[] args) { int port = int.Parse(args[i + 1]); var client = new TcpClient(); -#if NETSTANDARD2_0 - await client.ConnectAsync(IPAddress.Loopback, port); -#else - try - { - using var cts = new CancellationTokenSource(ConnectionTimeout); - await client.ConnectAsync(IPAddress.Loopback, port, cts.Token); - } - catch (OperationCanceledException) - { - throw new TimeoutException($"The connection to the host process timed out after {ConnectionTimeout}."); - } -#endif + client.Connect(IPAddress.Loopback, port); return new TcpHost(client); } } diff --git a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt index 22ab9b1bb5..0d08de2cbc 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkProgram.txt @@ -25,11 +25,11 @@ namespace BenchmarkDotNet.Autogenerated private static async global::System.Threading.Tasks.ValueTask MainCore(global::System.String[] args) { // This variable name is used by CodeGenerator.GetBenchmarkRunCall, do NOT change it! - global::BenchmarkDotNet.Engines.IHost host = await global::BenchmarkDotNet.Engines.TcpHost.GetHostAsync(args); + global::BenchmarkDotNet.Engines.IHost host = global::BenchmarkDotNet.Engines.TcpHost.GetHost(args); // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! - await global::BenchmarkDotNet.Engines.HostExtensions.BeforeAnythingElseAsync(host); + global::BenchmarkDotNet.Engines.HostExtensions.BeforeAnythingElse(host); try { @@ -42,32 +42,32 @@ namespace BenchmarkDotNet.Autogenerated if (args.Length == 0) { - await host.WriteLineAsync("You have not specified benchmark id (an integer) so the first benchmark will be executed."); + host.WriteLine("You have not specified benchmark id (an integer) so the first benchmark will be executed."); } $BenchmarkRunCall$ return 0; } catch (global::System.Exception oom) when (oom is global::System.OutOfMemoryException || oom is global::System.Reflection.TargetInvocationException reflection && reflection.InnerException is global::System.OutOfMemoryException) { - await host.WriteLineAsync(); - await host.WriteLineAsync("OutOfMemoryException!"); - await host.WriteLineAsync("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); - await host.WriteLineAsync("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); - await host.WriteLineAsync("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); - await host.WriteLineAsync(); - await host.WriteLineAsync(oom.ToString()); + host.WriteLine(); + host.WriteLine("OutOfMemoryException!"); + host.WriteLine("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); + host.WriteLine("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); + host.WriteLine("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); + host.WriteLine(); + host.WriteLine(oom.ToString()); return -1; } catch(global::System.Exception ex) { - await host.WriteLineAsync(); - await host.WriteLineAsync(ex.ToString()); + host.WriteLine(); + host.WriteLine(ex.ToString()); return -1; } finally { - await global::BenchmarkDotNet.Engines.HostExtensions.AfterAllAsync(host); + global::BenchmarkDotNet.Engines.HostExtensions.AfterAll(host); host.Dispose(); } diff --git a/src/BenchmarkDotNet/Templates/BenchmarkType.txt b/src/BenchmarkDotNet/Templates/BenchmarkType.txt index 5bd846834c..9535d3c01f 100644 --- a/src/BenchmarkDotNet/Templates/BenchmarkType.txt +++ b/src/BenchmarkDotNet/Templates/BenchmarkType.txt @@ -6,16 +6,16 @@ { global::BenchmarkDotNet.Autogenerated.Runnable_$ID$ instance = new global::BenchmarkDotNet.Autogenerated.Runnable_$ID$(); - await host.WriteLineAsync(); + host.WriteLine(); foreach (global::System.String infoLine in global::BenchmarkDotNet.Environments.BenchmarkEnvironmentInfo.GetCurrent().ToFormattedString()) { - await host.WriteLineAsync($"// {infoLine}"); + host.WriteLine($"// {infoLine}"); } global::BenchmarkDotNet.Jobs.Job job = new global::BenchmarkDotNet.Jobs.Job(); // use full name to avoid naming conflicts, #778 $JobSetDefinition$; job.Freeze(); - await host.WriteLineAsync($"// Job: {job.DisplayInfo}"); - await host.WriteLineAsync(); + host.WriteLine($"// Job: {job.DisplayInfo}"); + host.WriteLine(); global::System.Collections.Generic.IEnumerable errors = global::BenchmarkDotNet.Validators.BenchmarkProcessValidator.Validate(job, instance); if (await global::BenchmarkDotNet.Validators.ValidationErrorReporter.ReportIfAnyAsync(errors, host)) @@ -55,7 +55,7 @@ }; global::BenchmarkDotNet.Engines.RunResults results = await new $EngineFactoryType$().Create(engineParameters).RunAsync(); - await host.ReportResultsAsync(results); // printing costs memory, do this after runs + host.ReportResults(results); // printing costs memory, do this after runs instance.__TrickTheJIT__(); // compile the method for disassembler, but without actual run of the benchmark ;) await compositeInProcessDiagnoserHandler.HandleAsync(global::BenchmarkDotNet.Engines.BenchmarkSignal.AfterEngine); diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs index dab329691a..fc592c2782 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs @@ -20,7 +20,7 @@ public static async ValueTask Run(IHost host, ExecuteParameters parameters) { // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! - await host.BeforeAnythingElseAsync(); + host.BeforeAnythingElse(); try { @@ -34,25 +34,25 @@ public static async ValueTask Run(IHost host, ExecuteParameters parameters) } catch (Exception oom) when (oom is OutOfMemoryException || oom is TargetInvocationException reflection && reflection.InnerException is OutOfMemoryException) { - await host.WriteLineAsync(); - await host.WriteLineAsync("OutOfMemoryException!"); - await host.WriteLineAsync("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); - await host.WriteLineAsync("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); - await host.WriteLineAsync("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); - await host.WriteLineAsync(); - await host.WriteLineAsync(oom.ToString()); + host.WriteLine(); + host.WriteLine("OutOfMemoryException!"); + host.WriteLine("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); + host.WriteLine("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); + host.WriteLine("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); + host.WriteLine(); + host.WriteLine(oom.ToString()); return -1; } catch (Exception ex) { - await host.WriteLineAsync(); - await host.WriteLineAsync(ex.ToString()); + host.WriteLine(); + host.WriteLine(ex.ToString()); return -1; } finally { - await host.AfterAllAsync(); + host.AfterAll(); } } @@ -63,14 +63,14 @@ private static async ValueTask RunCore(Type runnableType, IHost host, ExecutePar var instance = Activator.CreateInstance(runnableType); FillMembers(instance, benchmarkCase); - await host.WriteLineAsync(); + host.WriteLine(); foreach (string infoLine in BenchmarkEnvironmentInfo.GetCurrent().ToFormattedString()) { - await host.WriteLineAsync($"// {infoLine}"); + host.WriteLine($"// {infoLine}"); } var job = new Job().Apply(benchmarkCase.Job).Freeze(); - await host.WriteLineAsync($"// Job: {job.DisplayInfo}"); - await host.WriteLineAsync(); + host.WriteLine($"// Job: {job.DisplayInfo}"); + host.WriteLine(); var errors = BenchmarkProcessValidator.Validate(job, instance); if (await ValidationErrorReporter.ReportIfAnyAsync(errors, host)) @@ -114,7 +114,7 @@ private static async ValueTask RunCore(Type runnableType, IHost host, ExecutePar .ResolveValue(InfrastructureMode.EngineFactoryCharacteristic, InfrastructureResolver.Instance) .Create(engineParameters) .RunAsync(); - await host.ReportResultsAsync(results); + host.ReportResults(results); runnableType.GetMethod(TrickTheJitCoreMethodName).Invoke(instance, []); diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/InProcessHost.cs b/src/BenchmarkDotNet/Toolchains/InProcess/InProcessHost.cs index 934fb0c8a1..30d03be735 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/InProcessHost.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/InProcessHost.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Text; -using System.Threading.Tasks; using BenchmarkDotNet.Attributes.CompilerServices; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; @@ -54,14 +54,14 @@ public InProcessHost(BenchmarkCase benchmarkCase, ILogger logger, IDiagnoser? di /// Passes text to the host. /// Text to write. - public async ValueTask WriteAsync(string message) => logger.Write(message); + public void WriteAsync(string message) => logger.Write(message); /// Passes new line to the host. - public async ValueTask WriteLineAsync() => logger.WriteLine(); + public void WriteLine() => logger.WriteLine(); /// Passes text (new line appended) to the host. /// Text to write. - public async ValueTask WriteLineAsync(string message) + public void WriteLine(string message) { logger.WriteLine(message); if (message.StartsWith(CompositeInProcessDiagnoser.HeaderKey)) // Captures both header and results @@ -72,17 +72,21 @@ public async ValueTask WriteLineAsync(string message) /// Sends notification signal to the host. /// The signal to send. - public async ValueTask SendSignalAsync(HostSignal hostSignal) => diagnoser?.Handle(hostSignal, diagnoserActionParameters!); + public void SendSignal(HostSignal hostSignal) => diagnoser?.Handle(hostSignal, diagnoserActionParameters!); - public async ValueTask SendErrorAsync(string message) => logger.WriteLine(LogKind.Error, $"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + public void SendError(string message) => logger.WriteLine(LogKind.Error, $"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); /// Submits run results to the host. /// The run results. - public async ValueTask ReportResultsAsync(RunResults runResults) + public void ReportResults(RunResults runResults) { RunResults = runResults; - await runResults.WriteAsync(this); + using (var w = new StringWriter()) + { + runResults.Print(w); + logger.Write(w.GetStringBuilder().ToString()); + } } // Keep in sync with Broker and WasmExecutor. diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs index f7d195a95c..faf182727a 100644 --- a/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs +++ b/src/BenchmarkDotNet/Toolchains/InProcess/NoEmit/InProcessNoEmitRunner.cs @@ -26,7 +26,7 @@ public static async ValueTask Run(IHost host, ExecuteParameters parameters) { // the first thing to do is to let diagnosers hook in before anything happens // so all jit-related diagnosers can catch first jit compilation! - await host.BeforeAnythingElseAsync(); + host.BeforeAnythingElse(); try { @@ -46,25 +46,25 @@ public static async ValueTask Run(IHost host, ExecuteParameters parameters) } catch (Exception oom) when (oom is OutOfMemoryException || oom is TargetInvocationException reflection && reflection.InnerException is OutOfMemoryException) { - await host.WriteLineAsync(); - await host.WriteLineAsync("OutOfMemoryException!"); - await host.WriteLineAsync("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); - await host.WriteLineAsync("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); - await host.WriteLineAsync("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); - await host.WriteLineAsync(); - await host.WriteLineAsync(oom.ToString()); + host.WriteLine(); + host.WriteLine("OutOfMemoryException!"); + host.WriteLine("BenchmarkDotNet continues to run additional iterations until desired accuracy level is achieved. It's possible only if the benchmark method doesn't have any side-effects."); + host.WriteLine("If your benchmark allocates memory and keeps it alive, you are creating a memory leak."); + host.WriteLine("You should redesign your benchmark and remove the side-effects. You can use `OperationsPerInvoke`, `IterationSetup` and `IterationCleanup` to do that."); + host.WriteLine(); + host.WriteLine(oom.ToString()); return -1; } catch (Exception ex) { - await host.WriteLineAsync(); - await host.WriteLineAsync(ex.ToString()); + host.WriteLine(); + host.WriteLine(ex.ToString()); return -1; } finally { - await host.AfterAllAsync(); + host.AfterAll(); } } @@ -125,11 +125,11 @@ public static async ValueTask RunCore(IHost host, ExecuteParameters parameters) FillMembers(instance, benchmarkCase); - await host.WriteLineAsync(); + host.WriteLine(); foreach (string infoLine in BenchmarkEnvironmentInfo.GetCurrent().ToFormattedString()) - await host.WriteLineAsync("// {0}", infoLine); - await host.WriteLineAsync("// Job: {0}", job.DisplayInfo); - await host.WriteLineAsync(); + host.WriteLine("// {0}", infoLine); + host.WriteLine("// Job: {0}", job.DisplayInfo); + host.WriteLine(); var errors = BenchmarkProcessValidator.Validate(job, instance); if (await ValidationErrorReporter.ReportIfAnyAsync(errors, host)) @@ -178,7 +178,7 @@ public static async ValueTask RunCore(IHost host, ExecuteParameters parameters) .ResolveValue(InfrastructureMode.EngineFactoryCharacteristic, InfrastructureResolver.Instance)! .Create(engineParameters) .RunAsync(); - await host.ReportResultsAsync(results); // printing costs memory, do this after runs + host.ReportResults(results); // printing costs memory, do this after runs await compositeInProcessDiagnoserHandler.HandleAsync(BenchmarkSignal.AfterEngine); } diff --git a/src/BenchmarkDotNet/Validators/ValidationErrorReporter.cs b/src/BenchmarkDotNet/Validators/ValidationErrorReporter.cs index c5c96f87ab..f5c122f1af 100644 --- a/src/BenchmarkDotNet/Validators/ValidationErrorReporter.cs +++ b/src/BenchmarkDotNet/Validators/ValidationErrorReporter.cs @@ -15,7 +15,7 @@ public static async ValueTask ReportIfAnyAsync(IEnumerable Date: Sat, 14 Feb 2026 07:41:03 -0500 Subject: [PATCH 26/33] Dispose client when process exited early. --- src/BenchmarkDotNet/Loggers/Broker.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 46cc729d7c..d6f43bc679 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -20,6 +20,7 @@ internal class Broker : IDisposable private readonly Process process; private readonly CompositeInProcessDiagnoser compositeInProcessDiagnoser; private TcpListener? tcpListener; + private TcpClient? client; private enum Result { @@ -56,6 +57,7 @@ public void Dispose() process.Exited -= OnProcessExited; Interlocked.Exchange(ref tcpListener, null)?.Stop(); + Interlocked.Exchange(ref client, null)?.Dispose(); } private void OnProcessExited(object? sender, EventArgs e) @@ -78,20 +80,21 @@ private async ValueTask ProcessDataCore() try { #if NETSTANDARD2_0 - using var client = await tcpListener.AcceptTcpClientAsync(); + this.client = await tcpListener.AcceptTcpClientAsync(); #else - TcpClient client; try { using var cts = new CancellationTokenSource(TcpHost.ConnectionTimeout); - client = await tcpListener.AcceptTcpClientAsync(cts.Token); + this.client = await tcpListener.AcceptTcpClientAsync(cts.Token); } catch (OperationCanceledException) { throw new TimeoutException($"The connection to the benchmark process timed out after {TcpHost.ConnectionTimeout}."); } - using var _ = client; #endif + if (this.client is not { } client) + return Result.EarlyProcessExit; + using var stream = client.GetStream(); using StreamReader reader = new(stream, TcpHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); From 7f2cd0622f73f2c5c5094608755ee344dfd28c7d Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sat, 14 Feb 2026 14:26:06 -0500 Subject: [PATCH 27/33] Backport Task.WaitAsync and StreamReader.ReadLineAsync with cancelation token. Cancel in process exited event instead of dispose. --- .../Extensions/TaskExtensions.cs | 95 +++++ .../Helpers/CancelableStreamReader.cs | 371 ++++++++++++++++++ src/BenchmarkDotNet/Loggers/Broker.cs | 40 +- 3 files changed, 488 insertions(+), 18 deletions(-) create mode 100644 src/BenchmarkDotNet/Extensions/TaskExtensions.cs create mode 100644 src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs diff --git a/src/BenchmarkDotNet/Extensions/TaskExtensions.cs b/src/BenchmarkDotNet/Extensions/TaskExtensions.cs new file mode 100644 index 0000000000..a11da51954 --- /dev/null +++ b/src/BenchmarkDotNet/Extensions/TaskExtensions.cs @@ -0,0 +1,95 @@ +#if NETSTANDARD2_0 +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace BenchmarkDotNet.Extensions; + +internal static class TaskExtensions +{ + public static async Task WaitAsync(this Task task, CancellationToken cancellationToken) + { + if (task.IsCompleted) + { + await task; + return; + } + cancellationToken.ThrowIfCancellationRequested(); + + var timeoutTaskSource = new TaskCompletionSource(); + using var _ = cancellationToken.Register(() => timeoutTaskSource.TrySetCanceled(cancellationToken), false); + await await Task.WhenAny(task, timeoutTaskSource.Task); + } + + public static async Task WaitAsync(this Task task, TimeSpan timeout) + { + if (task.IsCompleted) + { + await task; + return; + } + + var timeoutTaskSource = new TaskCompletionSource(); + using var cts = new CancellationTokenSource(timeout); + using var _ = cts.Token.Register(() => timeoutTaskSource.SetException(new TimeoutException()), false); + await await Task.WhenAny(task, timeoutTaskSource.Task); + } + + public static async Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken cancellationToken) + { + if (task.IsCompleted) + { + await task; + return; + } + cancellationToken.ThrowIfCancellationRequested(); + + var timeoutTaskSource = new TaskCompletionSource(); + using var timeoutCts = new CancellationTokenSource(timeout); + using var _ = cancellationToken.Register(() => timeoutTaskSource.TrySetCanceled(cancellationToken), false); + using var __ = timeoutCts.Token.Register(() => timeoutTaskSource.TrySetException(new TimeoutException()), false); + await await Task.WhenAny(task, timeoutTaskSource.Task); + } + + public static async Task WaitAsync(this Task task, CancellationToken cancellationToken) + { + if (task.IsCompleted) + { + return await task; + } + cancellationToken.ThrowIfCancellationRequested(); + + var timeoutTaskSource = new TaskCompletionSource(); + using var _ = cancellationToken.Register(() => timeoutTaskSource.TrySetCanceled(cancellationToken), false); + return await await Task.WhenAny(task, timeoutTaskSource.Task); + } + + public static async Task WaitAsync(this Task task, TimeSpan timeout) + { + if (task.IsCompleted) + { + return await task; + } + + var timeoutTaskSource = new TaskCompletionSource(); + using var cts = new CancellationTokenSource(timeout); + using var _ = cts.Token.Register(() => timeoutTaskSource.SetException(new TimeoutException()), false); + return await await Task.WhenAny(task, timeoutTaskSource.Task); + } + + public static async Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken cancellationToken) + { + if (task.IsCompleted) + { + return await task; + } + cancellationToken.ThrowIfCancellationRequested(); + + var timeoutTaskSource = new TaskCompletionSource(); + using var timeoutCts = new CancellationTokenSource(timeout); + using var _ = cancellationToken.Register(() => timeoutTaskSource.TrySetCanceled(cancellationToken), false); + using var __ = timeoutCts.Token.Register(() => timeoutTaskSource.TrySetException(new TimeoutException()), false); + return await await Task.WhenAny(task, timeoutTaskSource.Task); + } +} +#endif diff --git a/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs b/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs new file mode 100644 index 0000000000..2906fac4c9 --- /dev/null +++ b/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs @@ -0,0 +1,371 @@ +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable + +namespace BenchmarkDotNet.Helpers; + +internal sealed class CancelableStreamReader : IDisposable +{ +#if NET7_0_OR_GREATER + private readonly StreamReader _reader; + + public CancelableStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize = -1, bool leaveOpen = false) + => _reader = new(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen); + + public ValueTask ReadLineAsync(CancellationToken cancellationToken) + => _reader.ReadLineAsync(cancellationToken); + + public void Dispose() + => _reader.Dispose(); +#else + // Impl copied from https://github.com/dotnet/runtime/blob/407f9f1476709c3e5aea25511b330e5c1df13fb8/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs + // slightly adjusted to work in netstandard2.0. + + private const int DefaultBufferSize = 1024; + private const int MinBufferSize = 128; + + private readonly Stream _stream; + private Encoding _encoding; + private Decoder _decoder; + private readonly byte[] _byteBuffer; + private char[] _charBuffer; + private int _charPos; + private int _charLen; + private int _byteLen; + private int _bytePos; + private int _maxCharsPerBuffer; + private bool _disposed; + private bool _detectEncoding; + private bool _checkPreamble; + private readonly bool _closable; + + public CancelableStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize = -1, bool leaveOpen = false) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + if (!stream.CanRead) throw new ArgumentException("Stream is not readable", nameof(stream)); + + if (bufferSize == -1) + { + bufferSize = DefaultBufferSize; + } + if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize)); + + _stream = stream; + _encoding = encoding ??= Encoding.UTF8; + _decoder = encoding.GetDecoder(); + if (bufferSize < MinBufferSize) + { + bufferSize = MinBufferSize; + } + + _byteBuffer = new byte[bufferSize]; + _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize); + _charBuffer = new char[_maxCharsPerBuffer]; + _detectEncoding = detectEncodingFromByteOrderMarks; + + int preambleLength = encoding.GetPreamble().Length; + _checkPreamble = preambleLength > 0 && preambleLength <= bufferSize; + + _closable = !leaveOpen; + } + + ~CancelableStreamReader() + => Dispose(false); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + _disposed = true; + + if (_closable) + { + try + { + if (disposing) + { + _stream.Close(); + } + } + finally + { + _charPos = 0; + _charLen = 0; + } + } + } + + public async ValueTask ReadLineAsync(CancellationToken cancellationToken) + { + if (_charPos == _charLen && (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) == 0) + { + return null; + } + + string retVal; + char[]? arrayPoolBuffer = null; + int arrayPoolBufferPos = 0; + + do + { + char[] charBuffer = _charBuffer; + int charLen = _charLen; + int charPos = _charPos; + + Debug.Assert(charPos < charLen, "ReadBuffer returned > 0 but didn't bump _charLen?"); + + int idxOfNewline = charBuffer.AsSpan(charPos, charLen - charPos).IndexOfAny('\r', '\n'); + if (idxOfNewline >= 0) + { + if (arrayPoolBuffer is null) + { + retVal = new string(charBuffer, charPos, idxOfNewline); + } + else + { + retVal = new string(arrayPoolBuffer, 0, arrayPoolBufferPos) + new string(charBuffer, charPos, idxOfNewline); + ArrayPool.Shared.Return(arrayPoolBuffer); + } + + charPos += idxOfNewline; + char matchedChar = charBuffer[charPos++]; + _charPos = charPos; + + if (matchedChar == '\r') + { + if (charPos < charLen || (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) > 0) + { + if (_charBuffer[_charPos] == '\n') + { + _charPos++; + } + } + } + + return retVal; + } + + if (arrayPoolBuffer is null) + { + arrayPoolBuffer = ArrayPool.Shared.Rent(charLen - charPos + 80); + } + else if ((arrayPoolBuffer.Length - arrayPoolBufferPos) < (charLen - charPos)) + { + char[] newBuffer = ArrayPool.Shared.Rent(checked(arrayPoolBufferPos + charLen - charPos)); + arrayPoolBuffer.AsSpan(0, arrayPoolBufferPos).CopyTo(newBuffer); + ArrayPool.Shared.Return(arrayPoolBuffer); + arrayPoolBuffer = newBuffer; + } + charBuffer.AsSpan(charPos, charLen - charPos).CopyTo(arrayPoolBuffer.AsSpan(arrayPoolBufferPos)); + arrayPoolBufferPos += charLen - charPos; + } + while (await ReadBufferAsync(cancellationToken).ConfigureAwait(false) > 0); + + if (arrayPoolBuffer is not null) + { + retVal = new string(arrayPoolBuffer, 0, arrayPoolBufferPos); + ArrayPool.Shared.Return(arrayPoolBuffer); + } + else + { + retVal = string.Empty; + } + + return retVal; + } + + private async ValueTask ReadBufferAsync(CancellationToken cancellationToken) + { + _charLen = 0; + _charPos = 0; + byte[] tmpByteBuffer = _byteBuffer; + Stream tmpStream = _stream; + + if (!_checkPreamble) + { + _byteLen = 0; + } + + bool eofReached = false; + + do + { + if (_checkPreamble) + { + Debug.Assert(_bytePos <= _encoding.GetPreamble().Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); + int tmpBytePos = _bytePos; + int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos, cancellationToken).ConfigureAwait(false); + Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); + + if (len == 0) + { + eofReached = true; + break; + } + + _byteLen += len; + } + else + { + Debug.Assert(_bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); + _byteLen = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length, cancellationToken).ConfigureAwait(false); + Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! Bug in stream class."); + + if (_byteLen == 0) + { + eofReached = true; + break; + } + } + + if (IsPreamble()) + { + continue; + } + + if (_detectEncoding && _byteLen >= 2) + { + DetectEncoding(); + } + + Debug.Assert(_charPos == 0 && _charLen == 0, "We shouldn't be trying to decode more data if we made progress in an earlier iteration."); + _charLen = _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, 0, flush: false); + } while (_charLen == 0); + + if (eofReached) + { + Debug.Assert(_charPos == 0 && _charLen == 0, "We shouldn't be looking for EOF unless we have an empty char buffer."); + _charLen = _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, 0, flush: true); + _bytePos = 0; + _byteLen = 0; + } + + return _charLen; + } + + private bool IsPreamble() + { + if (!_checkPreamble) + { + return false; + } + + return IsPreambleWorker(); + + bool IsPreambleWorker() + { + Debug.Assert(_checkPreamble); + ReadOnlySpan preamble = _encoding.GetPreamble(); + + Debug.Assert(_bytePos < preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length. Are two threads using this StreamReader at the same time?"); + int len = Math.Min(_byteLen, preamble.Length); + + for (int i = _bytePos; i < len; i++) + { + if (_byteBuffer[i] != preamble[i]) + { + _bytePos = 0; + _checkPreamble = false; + return false; + } + } + _bytePos = len; + + Debug.Assert(_bytePos <= preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); + + if (_bytePos == preamble.Length) + { + CompressBuffer(preamble.Length); + _bytePos = 0; + _checkPreamble = false; + _detectEncoding = false; + } + + return _checkPreamble; + } + } + + private void DetectEncoding() + { + Debug.Assert(_byteLen >= 2, "Caller should've validated that at least 2 bytes were available."); + + byte[] byteBuffer = _byteBuffer; + _detectEncoding = false; + bool changedEncoding = false; + + ushort firstTwoBytes = BinaryPrimitives.ReadUInt16LittleEndian(byteBuffer); + if (firstTwoBytes == 0xFFFE) + { + _encoding = Encoding.BigEndianUnicode; + CompressBuffer(2); + changedEncoding = true; + } + else if (firstTwoBytes == 0xFEFF) + { + if (_byteLen < 4 || byteBuffer[2] != 0 || byteBuffer[3] != 0) + { + _encoding = Encoding.Unicode; + CompressBuffer(2); + changedEncoding = true; + } + else + { + _encoding = Encoding.UTF32; + CompressBuffer(4); + changedEncoding = true; + } + } + else if (_byteLen >= 3 && firstTwoBytes == 0xBBEF && byteBuffer[2] == 0xBF) + { + _encoding = Encoding.UTF8; + CompressBuffer(3); + changedEncoding = true; + } + else if (_byteLen >= 4 && firstTwoBytes == 0 && byteBuffer[2] == 0xFE && byteBuffer[3] == 0xFF) + { + _encoding = new UTF32Encoding(bigEndian: true, byteOrderMark: true); + CompressBuffer(4); + changedEncoding = true; + } + else if (_byteLen == 2) + { + _detectEncoding = true; + } + + if (changedEncoding) + { + _decoder = _encoding.GetDecoder(); + int newMaxCharsPerBuffer = _encoding.GetMaxCharCount(byteBuffer.Length); + if (newMaxCharsPerBuffer > _maxCharsPerBuffer) + { + _charBuffer = new char[newMaxCharsPerBuffer]; + } + _maxCharsPerBuffer = newMaxCharsPerBuffer; + } + } + + private void CompressBuffer(int n) + { + Debug.Assert(_byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length. Are two threads using this StreamReader at the same time?"); + byte[] byteBuffer = _byteBuffer; + _ = byteBuffer.Length; + new ReadOnlySpan(byteBuffer, n, _byteLen - n).CopyTo(byteBuffer); + _byteLen -= n; + } +#endif +} diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index d6f43bc679..84f1217995 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -8,6 +8,8 @@ using System.Threading.Tasks; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Running; #nullable enable @@ -19,8 +21,8 @@ internal class Broker : IDisposable private readonly ILogger logger; private readonly Process process; private readonly CompositeInProcessDiagnoser compositeInProcessDiagnoser; - private TcpListener? tcpListener; - private TcpClient? client; + private readonly CancellationTokenSource cancellationTokenSource = new(); + private readonly TcpListener tcpListener; private enum Result { @@ -56,8 +58,7 @@ public void Dispose() { process.Exited -= OnProcessExited; - Interlocked.Exchange(ref tcpListener, null)?.Stop(); - Interlocked.Exchange(ref client, null)?.Dispose(); + cancellationTokenSource.Cancel(throwOnFirstException: false); } private void OnProcessExited(object? sender, EventArgs e) @@ -74,36 +75,35 @@ internal async ValueTask ProcessData() private async ValueTask ProcessDataCore() { - if (process.HasExited || this.tcpListener is not { } tcpListener) + if (process.HasExited || cancellationTokenSource.IsCancellationRequested) return Result.EarlyProcessExit; try { -#if NETSTANDARD2_0 - this.client = await tcpListener.AcceptTcpClientAsync(); -#else +#if NET6_0_OR_GREATER + TcpClient client; + using var timeoutCts = new CancellationTokenSource(TcpHost.ConnectionTimeout); + using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, timeoutCts.Token); try { - using var cts = new CancellationTokenSource(TcpHost.ConnectionTimeout); - this.client = await tcpListener.AcceptTcpClientAsync(cts.Token); + client = await tcpListener.AcceptTcpClientAsync(linkedCts.Token); } - catch (OperationCanceledException) + catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested) { throw new TimeoutException($"The connection to the benchmark process timed out after {TcpHost.ConnectionTimeout}."); } + using var _ = client; +#else + using var client = await tcpListener.AcceptTcpClientAsync().WaitAsync(TcpHost.ConnectionTimeout, cancellationTokenSource.Token); #endif - if (this.client is not { } client) - return Result.EarlyProcessExit; - using var stream = client.GetStream(); - - using StreamReader reader = new(stream, TcpHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); + using CancelableStreamReader reader = new(stream, TcpHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); // Flush the data to the Stream after each write, otherwise the client will wait for input endlessly! using StreamWriter writer = new(stream, TcpHost.UTF8NoBOM, bufferSize: 1) { AutoFlush = true }; while (true) { - var line = await reader.ReadLineAsync(); + var line = await reader.ReadLineAsync(cancellationTokenSource.Token); if (line == null) return Result.EndOfStream; @@ -129,7 +129,7 @@ private async ValueTask ProcessDataCore() var resultsStringBuilder = new StringBuilder(); for (int i = 0; i < resultsLinesCount;) { - line = await reader.ReadLineAsync(); + line = await reader.ReadLineAsync(cancellationTokenSource.Token); if (line == null) return Result.EndOfStream; @@ -177,6 +177,10 @@ private async ValueTask ProcessDataCore() { return Result.EarlyProcessExit; } + catch (OperationCanceledException) + { + return Result.EarlyProcessExit; + } static bool IsEarlyExitCode(SocketError error) => error is SocketError.ConnectionReset From ade6d302461027964151a8d374731d6440481763 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Sun, 15 Feb 2026 00:36:02 -0500 Subject: [PATCH 28/33] Use synchronous APIs on Full Framework + Windows Arm. --- .../Helpers/CancelableStreamReader.cs | 44 ++++++++++++++++--- src/BenchmarkDotNet/Loggers/Broker.cs | 4 +- .../Portability/RuntimeInformation.cs | 2 + 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs b/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs index 2906fac4c9..ea43bcc9ac 100644 --- a/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs +++ b/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs @@ -1,4 +1,5 @@ -using System; +using BenchmarkDotNet.Extensions; +using System; using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; @@ -14,16 +15,16 @@ namespace BenchmarkDotNet.Helpers; internal sealed class CancelableStreamReader : IDisposable { #if NET7_0_OR_GREATER - private readonly StreamReader _reader; + private readonly StreamReader _defaultReader; public CancelableStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize = -1, bool leaveOpen = false) - => _reader = new(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen); + => _defaultReader = new(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen); public ValueTask ReadLineAsync(CancellationToken cancellationToken) - => _reader.ReadLineAsync(cancellationToken); + => _defaultReader.ReadLineAsync(cancellationToken); public void Dispose() - => _reader.Dispose(); + => _defaultReader.Dispose(); #else // Impl copied from https://github.com/dotnet/runtime/blob/407f9f1476709c3e5aea25511b330e5c1df13fb8/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs // slightly adjusted to work in netstandard2.0. @@ -31,6 +32,11 @@ public void Dispose() private const int DefaultBufferSize = 1024; private const int MinBufferSize = 128; + // Full Framework on Windows Arm runs on a compatibility layer rather than native. + // This difference was observed to cause hangs with async stream APIs over TcpClient that doesn't happen on other native supported environments. + // In that environment we change the async read to the default sync read via Task.Run. + private readonly StreamReader _defaultReader; + private readonly Stream _stream; private Encoding _encoding; private Decoder _decoder; @@ -51,6 +57,18 @@ public CancelableStreamReader(Stream stream, Encoding encoding, bool detectEncod if (stream == null) throw new ArgumentNullException(nameof(stream)); if (!stream.CanRead) throw new ArgumentException("Stream is not readable", nameof(stream)); + if (Portability.RuntimeInformation.IsFullFrameworkCompatibilityLayer) + { + _defaultReader = new(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize == -1 ? DefaultBufferSize : bufferSize, leaveOpen); + _closable = true; + _stream = null!; + _encoding = null!; + _decoder = null!; + _byteBuffer = null!; + _charBuffer = null!; + return; + } + if (bufferSize == -1) { bufferSize = DefaultBufferSize; @@ -74,6 +92,7 @@ public CancelableStreamReader(Stream stream, Encoding encoding, bool detectEncod _checkPreamble = preambleLength > 0 && preambleLength <= bufferSize; _closable = !leaveOpen; + _defaultReader = null!; } ~CancelableStreamReader() @@ -99,7 +118,14 @@ private void Dispose(bool disposing) { if (disposing) { - _stream.Close(); + if (Portability.RuntimeInformation.IsFullFrameworkCompatibilityLayer) + { + _defaultReader.Dispose(); + } + else + { + _stream.Close(); + } } } finally @@ -112,6 +138,12 @@ private void Dispose(bool disposing) public async ValueTask ReadLineAsync(CancellationToken cancellationToken) { + if (Portability.RuntimeInformation.IsFullFrameworkCompatibilityLayer) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.Run(() => _defaultReader.ReadLine(), cancellationToken).WaitAsync(cancellationToken); + } + if (_charPos == _charLen && (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) == 0) { return null; diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 84f1217995..4807bd831b 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -94,7 +94,9 @@ private async ValueTask ProcessDataCore() } using var _ = client; #else - using var client = await tcpListener.AcceptTcpClientAsync().WaitAsync(TcpHost.ConnectionTimeout, cancellationTokenSource.Token); + using var client = Portability.RuntimeInformation.IsFullFrameworkCompatibilityLayer + ? await Task.Run(() => tcpListener.AcceptTcpClient(), cancellationTokenSource.Token).WaitAsync(TcpHost.ConnectionTimeout, cancellationTokenSource.Token) + : await tcpListener.AcceptTcpClientAsync().WaitAsync(TcpHost.ConnectionTimeout, cancellationTokenSource.Token); #endif using var stream = client.GetStream(); using CancelableStreamReader reader = new(stream, TcpHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); diff --git a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs index ab6604b957..707460f46d 100644 --- a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs +++ b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs @@ -45,6 +45,8 @@ internal static class RuntimeInformation FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); #endif + public static readonly bool IsFullFrameworkCompatibilityLayer = IsFullFramework && GetCurrentPlatform() is not (Platform.X86 or Platform.X64); + [SupportedOSPlatformGuard("browser")] #if NET6_0_OR_GREATER public static readonly bool IsWasm = OperatingSystem.IsBrowser(); From 4656437667aaac7986ffd34056e6bff5801bcb3b Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 16 Feb 2026 00:02:54 -0500 Subject: [PATCH 29/33] Add logs to find where the freeze is occurring. --- src/BenchmarkDotNet/Engines/TcpHost.cs | 25 +++++++++++++++++++------ src/BenchmarkDotNet/Loggers/Broker.cs | 8 ++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/TcpHost.cs b/src/BenchmarkDotNet/Engines/TcpHost.cs index 01b2ded576..7a029edb5a 100644 --- a/src/BenchmarkDotNet/Engines/TcpHost.cs +++ b/src/BenchmarkDotNet/Engines/TcpHost.cs @@ -39,14 +39,19 @@ public void Dispose() inReader.Dispose(); } - public void WriteAsync(string message) - => outWriter.Write(message); - public void WriteLine() - => outWriter.WriteLine(); + { + Console.Error.WriteLine($"[TcpHost] Before WriteLine"); + outWriter.WriteLine(); + Console.Error.WriteLine($"[TcpHost] After WriteLine"); + } public void WriteLine(string message) - => outWriter.WriteLine(message); + { + Console.Error.WriteLine($"[TcpHost] Before WriteLine: {message}"); + outWriter.WriteLine(message); + Console.Error.WriteLine($"[TcpHost] After WriteLine: {message}"); + } public void SendSignal(HostSignal hostSignal) { @@ -57,10 +62,14 @@ public void SendSignal(HostSignal hostSignal) Thread.Sleep(1); } + Console.Error.WriteLine($"[TcpHost] Before SendSignal: {hostSignal}"); outWriter.WriteLine(Engine.Signals.ToMessage(hostSignal)); + Console.Error.WriteLine($"[TcpHost] After SendSignal: {hostSignal}"); // Read the response from Parent process. string? acknowledgment = inReader.ReadLine(); + Console.Error.WriteLine($"[TcpHost] After acknowledgment: {acknowledgment}"); + if (acknowledgment != Engine.Signals.Acknowledgment && !(acknowledgment is null && hostSignal == HostSignal.AfterAll)) // an early EOF, but still valid { @@ -69,7 +78,11 @@ public void SendSignal(HostSignal hostSignal) } public void SendError(string message) - => outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + { + Console.Error.WriteLine($"[TcpHost] Before SendError: {message}"); + outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); + Console.Error.WriteLine($"[TcpHost] After SendError: {message}"); + } public void ReportResults(RunResults runResults) => runResults.Print(outWriter); diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index 4807bd831b..a0cdb8f5c7 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -105,7 +105,10 @@ private async ValueTask ProcessDataCore() while (true) { + Console.Error.WriteLine($"[Broker] Before ReadLineAsync"); var line = await reader.ReadLineAsync(cancellationTokenSource.Token); + Console.Error.WriteLine($"[Broker] After ReadLineAsync: {line}"); + if (line == null) return Result.EndOfStream; @@ -131,7 +134,10 @@ private async ValueTask ProcessDataCore() var resultsStringBuilder = new StringBuilder(); for (int i = 0; i < resultsLinesCount;) { + Console.Error.WriteLine($"[Broker] Before InProcessDiagnoser ReadLineAsync"); line = await reader.ReadLineAsync(cancellationTokenSource.Token); + Console.Error.WriteLine($"[Broker] After InProcessDiagnoser ReadLineAsync: {line}"); + if (line == null) return Result.EndOfStream; @@ -155,7 +161,9 @@ private async ValueTask ProcessDataCore() { Diagnoser?.Handle(signal, DiagnoserActionParameters); + Console.Error.WriteLine($"[Broker] Before Write Acknowledgment"); writer.WriteLine(Engine.Signals.Acknowledgment); + Console.Error.WriteLine($"[Broker] After Write Acknowledgment"); if (signal == HostSignal.AfterAll) { From 4126c961cb3e7192ac7ed5b25ae1b86082eb1046 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 16 Feb 2026 06:38:57 -0500 Subject: [PATCH 30/33] Check `awaiter.IsCompleted` instead of `valueTask.IsCompleted`. Moved `isCompleted` read/write inside lock body. --- .../Engines/BenchmarkSynchronizationContext.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs index f68ffd4426..97e03728db 100644 --- a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs +++ b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs @@ -40,7 +40,7 @@ internal sealed class BenchmarkDotNetSynchronizationContext : SynchronizationCon private (SendOrPostCallback d, object? state)[] queue = new (SendOrPostCallback d, object? state)[1]; private (SendOrPostCallback d, object? state)[] executing = new (SendOrPostCallback d, object? state)[1]; private int queueCount = 0; - volatile private bool isCompleted; + private bool isCompleted; internal BenchmarkDotNetSynchronizationContext(SynchronizationContext? previousContext) { @@ -99,7 +99,7 @@ internal T ExecuteUntilComplete(ValueTask valueTask) ThrowIfDisposed(); var awaiter = valueTask.GetAwaiter(); - if (valueTask.IsCompleted) + if (awaiter.IsCompleted) { return awaiter.GetResult(); } @@ -114,6 +114,11 @@ internal T ExecuteUntilComplete(ValueTask valueTask) (SendOrPostCallback d, object? state)[] executing; lock (syncRoot) { + if (isCompleted) + { + return awaiter.GetResult(); + } + count = queueCount; queueCount = 0; executing = queue; @@ -133,11 +138,6 @@ internal T ExecuteUntilComplete(ValueTask valueTask) continue; } - if (isCompleted) - { - return awaiter.GetResult(); - } - if (!spinner.NextSpinWillYield) { spinner.SpinOnce(); @@ -156,9 +156,9 @@ internal T ExecuteUntilComplete(ValueTask valueTask) private void OnCompleted() { - isCompleted = true; lock (syncRoot) { + isCompleted = true; Monitor.Pulse(syncRoot); } } From ec874c7efec4581c4846443f9927c3224b55ea4a Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 16 Feb 2026 07:01:46 -0500 Subject: [PATCH 31/33] Fix forever hang race condition. --- .../Engines/BenchmarkSynchronizationContext.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs index 97e03728db..aa1d5d7d91 100644 --- a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs +++ b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs @@ -40,7 +40,7 @@ internal sealed class BenchmarkDotNetSynchronizationContext : SynchronizationCon private (SendOrPostCallback d, object? state)[] queue = new (SendOrPostCallback d, object? state)[1]; private (SendOrPostCallback d, object? state)[] executing = new (SendOrPostCallback d, object? state)[1]; private int queueCount = 0; - private bool isCompleted; + volatile private bool isCompleted; internal BenchmarkDotNetSynchronizationContext(SynchronizationContext? previousContext) { @@ -110,15 +110,15 @@ internal T ExecuteUntilComplete(ValueTask valueTask) var spinner = new SpinWait(); while (true) { + if (isCompleted) + { + return awaiter.GetResult(); + } + int count; (SendOrPostCallback d, object? state)[] executing; lock (syncRoot) { - if (isCompleted) - { - return awaiter.GetResult(); - } - count = queueCount; queueCount = 0; executing = queue; @@ -147,6 +147,11 @@ internal T ExecuteUntilComplete(ValueTask valueTask) // Yield the thread and wait for completion or for a posted callback. lock (syncRoot) { + // Check again inside the lock so we won't miss the pulse from a race condition and wait forever. + if (isCompleted) + { + return awaiter.GetResult(); + } Monitor.Wait(syncRoot); } // Reset the spinner. From d88b50e87d39881acadfc0a9898b2d407aa57208 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Mon, 16 Feb 2026 15:27:53 -0500 Subject: [PATCH 32/33] Remove extra logs and revert IsFullFrameworkCompatibilityLayer. --- src/BenchmarkDotNet/Engines/TcpHost.cs | 22 ++----------- .../Helpers/CancelableStreamReader.cs | 33 +------------------ src/BenchmarkDotNet/Loggers/Broker.cs | 10 +----- .../Portability/RuntimeInformation.cs | 2 -- 4 files changed, 5 insertions(+), 62 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/TcpHost.cs b/src/BenchmarkDotNet/Engines/TcpHost.cs index 7a029edb5a..b742a71d30 100644 --- a/src/BenchmarkDotNet/Engines/TcpHost.cs +++ b/src/BenchmarkDotNet/Engines/TcpHost.cs @@ -40,18 +40,10 @@ public void Dispose() } public void WriteLine() - { - Console.Error.WriteLine($"[TcpHost] Before WriteLine"); - outWriter.WriteLine(); - Console.Error.WriteLine($"[TcpHost] After WriteLine"); - } + => outWriter.WriteLine(); public void WriteLine(string message) - { - Console.Error.WriteLine($"[TcpHost] Before WriteLine: {message}"); - outWriter.WriteLine(message); - Console.Error.WriteLine($"[TcpHost] After WriteLine: {message}"); - } + => outWriter.WriteLine(message); public void SendSignal(HostSignal hostSignal) { @@ -62,14 +54,10 @@ public void SendSignal(HostSignal hostSignal) Thread.Sleep(1); } - Console.Error.WriteLine($"[TcpHost] Before SendSignal: {hostSignal}"); outWriter.WriteLine(Engine.Signals.ToMessage(hostSignal)); - Console.Error.WriteLine($"[TcpHost] After SendSignal: {hostSignal}"); // Read the response from Parent process. string? acknowledgment = inReader.ReadLine(); - Console.Error.WriteLine($"[TcpHost] After acknowledgment: {acknowledgment}"); - if (acknowledgment != Engine.Signals.Acknowledgment && !(acknowledgment is null && hostSignal == HostSignal.AfterAll)) // an early EOF, but still valid { @@ -78,11 +66,7 @@ public void SendSignal(HostSignal hostSignal) } public void SendError(string message) - { - Console.Error.WriteLine($"[TcpHost] Before SendError: {message}"); - outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); - Console.Error.WriteLine($"[TcpHost] After SendError: {message}"); - } + => outWriter.WriteLine($"{ValidationErrorReporter.ConsoleErrorPrefix} {message}"); public void ReportResults(RunResults runResults) => runResults.Print(outWriter); diff --git a/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs b/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs index ea43bcc9ac..860dc2685e 100644 --- a/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs +++ b/src/BenchmarkDotNet/Helpers/CancelableStreamReader.cs @@ -32,11 +32,6 @@ public void Dispose() private const int DefaultBufferSize = 1024; private const int MinBufferSize = 128; - // Full Framework on Windows Arm runs on a compatibility layer rather than native. - // This difference was observed to cause hangs with async stream APIs over TcpClient that doesn't happen on other native supported environments. - // In that environment we change the async read to the default sync read via Task.Run. - private readonly StreamReader _defaultReader; - private readonly Stream _stream; private Encoding _encoding; private Decoder _decoder; @@ -57,18 +52,6 @@ public CancelableStreamReader(Stream stream, Encoding encoding, bool detectEncod if (stream == null) throw new ArgumentNullException(nameof(stream)); if (!stream.CanRead) throw new ArgumentException("Stream is not readable", nameof(stream)); - if (Portability.RuntimeInformation.IsFullFrameworkCompatibilityLayer) - { - _defaultReader = new(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize == -1 ? DefaultBufferSize : bufferSize, leaveOpen); - _closable = true; - _stream = null!; - _encoding = null!; - _decoder = null!; - _byteBuffer = null!; - _charBuffer = null!; - return; - } - if (bufferSize == -1) { bufferSize = DefaultBufferSize; @@ -92,7 +75,6 @@ public CancelableStreamReader(Stream stream, Encoding encoding, bool detectEncod _checkPreamble = preambleLength > 0 && preambleLength <= bufferSize; _closable = !leaveOpen; - _defaultReader = null!; } ~CancelableStreamReader() @@ -118,14 +100,7 @@ private void Dispose(bool disposing) { if (disposing) { - if (Portability.RuntimeInformation.IsFullFrameworkCompatibilityLayer) - { - _defaultReader.Dispose(); - } - else - { - _stream.Close(); - } + _stream.Close(); } } finally @@ -138,12 +113,6 @@ private void Dispose(bool disposing) public async ValueTask ReadLineAsync(CancellationToken cancellationToken) { - if (Portability.RuntimeInformation.IsFullFrameworkCompatibilityLayer) - { - cancellationToken.ThrowIfCancellationRequested(); - return await Task.Run(() => _defaultReader.ReadLine(), cancellationToken).WaitAsync(cancellationToken); - } - if (_charPos == _charLen && (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) == 0) { return null; diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs index a0cdb8f5c7..815a475a4f 100644 --- a/src/BenchmarkDotNet/Loggers/Broker.cs +++ b/src/BenchmarkDotNet/Loggers/Broker.cs @@ -94,9 +94,7 @@ private async ValueTask ProcessDataCore() } using var _ = client; #else - using var client = Portability.RuntimeInformation.IsFullFrameworkCompatibilityLayer - ? await Task.Run(() => tcpListener.AcceptTcpClient(), cancellationTokenSource.Token).WaitAsync(TcpHost.ConnectionTimeout, cancellationTokenSource.Token) - : await tcpListener.AcceptTcpClientAsync().WaitAsync(TcpHost.ConnectionTimeout, cancellationTokenSource.Token); + using var client = await tcpListener.AcceptTcpClientAsync().WaitAsync(TcpHost.ConnectionTimeout, cancellationTokenSource.Token); #endif using var stream = client.GetStream(); using CancelableStreamReader reader = new(stream, TcpHost.UTF8NoBOM, detectEncodingFromByteOrderMarks: false); @@ -105,9 +103,7 @@ private async ValueTask ProcessDataCore() while (true) { - Console.Error.WriteLine($"[Broker] Before ReadLineAsync"); var line = await reader.ReadLineAsync(cancellationTokenSource.Token); - Console.Error.WriteLine($"[Broker] After ReadLineAsync: {line}"); if (line == null) return Result.EndOfStream; @@ -134,9 +130,7 @@ private async ValueTask ProcessDataCore() var resultsStringBuilder = new StringBuilder(); for (int i = 0; i < resultsLinesCount;) { - Console.Error.WriteLine($"[Broker] Before InProcessDiagnoser ReadLineAsync"); line = await reader.ReadLineAsync(cancellationTokenSource.Token); - Console.Error.WriteLine($"[Broker] After InProcessDiagnoser ReadLineAsync: {line}"); if (line == null) return Result.EndOfStream; @@ -161,9 +155,7 @@ private async ValueTask ProcessDataCore() { Diagnoser?.Handle(signal, DiagnoserActionParameters); - Console.Error.WriteLine($"[Broker] Before Write Acknowledgment"); writer.WriteLine(Engine.Signals.Acknowledgment); - Console.Error.WriteLine($"[Broker] After Write Acknowledgment"); if (signal == HostSignal.AfterAll) { diff --git a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs index 707460f46d..ab6604b957 100644 --- a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs +++ b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs @@ -45,8 +45,6 @@ internal static class RuntimeInformation FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); #endif - public static readonly bool IsFullFrameworkCompatibilityLayer = IsFullFramework && GetCurrentPlatform() is not (Platform.X86 or Platform.X64); - [SupportedOSPlatformGuard("browser")] #if NET6_0_OR_GREATER public static readonly bool IsWasm = OperatingSystem.IsBrowser(); From e4bd4a461b135b0465954f92d62b862102ea1032 Mon Sep 17 00:00:00 2001 From: Tim Cassell Date: Tue, 17 Feb 2026 02:28:11 -0500 Subject: [PATCH 33/33] Actual fix forever hang race condition. --- .../BenchmarkSynchronizationContext.cs | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs index aa1d5d7d91..0610105265 100644 --- a/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs +++ b/src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs @@ -110,19 +110,35 @@ internal T ExecuteUntilComplete(ValueTask valueTask) var spinner = new SpinWait(); while (true) { - if (isCompleted) - { - return awaiter.GetResult(); - } - int count; (SendOrPostCallback d, object? state)[] executing; lock (syncRoot) { + if (isCompleted) + { + return awaiter.GetResult(); + } + count = queueCount; queueCount = 0; executing = queue; queue = this.executing; + + if (count == 0) + { + if (spinner.NextSpinWillYield) + { + // Yield the thread and wait for completion or for a posted callback. + // Thread-safety note: isCompleted and queueCount must be checked inside the lock body + // before calling Monitor.Wait to avoid missing the pulse and waiting forever. + Monitor.Wait(syncRoot); + goto ResetAndContinue; + } + else + { + goto SpinAndContinue; + } + } } this.executing = executing; for (int i = 0; i < count; ++i) @@ -131,31 +147,13 @@ internal T ExecuteUntilComplete(ValueTask valueTask) executing[i] = default; d(state); } - if (count > 0) - { - // Reset spinner after any posted callback is executed. - spinner = new(); - continue; - } - - if (!spinner.NextSpinWillYield) - { - spinner.SpinOnce(); - continue; - } - // Yield the thread and wait for completion or for a posted callback. - lock (syncRoot) - { - // Check again inside the lock so we won't miss the pulse from a race condition and wait forever. - if (isCompleted) - { - return awaiter.GetResult(); - } - Monitor.Wait(syncRoot); - } - // Reset the spinner. + ResetAndContinue: spinner = new(); + continue; + + SpinAndContinue: + spinner.SpinOnce(); } }