Closed4

ModuleInitializerとstaticコンストラクターで性能比較

kenichiudakenichiuda
Benchmark.cs
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using System;
using System.Runtime.CompilerServices;

namespace StaticCtorBenchMark
{
    internal class BenchmarkConfig : ManualConfig
    {
        public BenchmarkConfig()
        {
            AddJob(Job.ShortRun.WithRuntime(ClrRuntime.Net48));
            AddJob(Job.ShortRun.WithRuntime(CoreRuntime.Core31));
            AddJob(Job.ShortRun.WithRuntime(CoreRuntime.Core50));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            _ = BenchmarkSwitcher.FromTypes(new[] { typeof(StaticCtorVsModuleInit) }).Run();
        }
    }

    [Config(typeof(BenchmarkConfig))]
    [DisassemblyDiagnoser]
    public unsafe class StaticCtorVsModuleInit
    {
        internal class StaticCtor
        {
            public static Random r;

            static StaticCtor()
            {
                r = new Random();
            }
            public static int NextInt() => r.Next();

        }
        internal class ModuleInit
        {
            public static Random r;

            [ModuleInitializer]
            internal static void Init()
            {
                r = new Random();
            }
            public static int NextInt() => r.Next();
        }

        [Benchmark()]
        public int ModuleInitAccess() => ModuleInit.NextInt();

        [Benchmark(Baseline = true)]
        public int StaticCtorAccess() => StaticCtor.NextInt();
    }

}

#if !NET5_0
namespace System.Runtime.CompilerServices
{
    [System.AttributeUsage(AttributeTargets.Method, Inherited = false)]
    internal sealed class ModuleInitializerAttribute : System.Attribute
    {
    }
}
#endif
StaticCtorBenchMark.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
    <LangVersion>9.0</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
  </ItemGroup>

</Project>
kenichiudakenichiuda

あんまり変わらない。

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
AMD Ryzen 7 3700X, 1 CPU, 8 logical and 8 physical cores
.NET Core SDK=6.0.100-preview.1.21103.13
  [Host]   : .NET Core 5.0.3 (CoreCLR 5.0.321.7212, CoreFX 5.0.321.7212), X64 RyuJIT
  ShortRun : .NET Framework 4.8 (4.8.4341.0), X64 RyuJIT

Job=ShortRun  IterationCount=3  LaunchCount=1
WarmupCount=3

|           Method |       Runtime |     Mean |     Error |    StdDev | Ratio | Code Size |
|----------------- |-------------- |---------:|----------:|----------:|------:|----------:|
| ModuleInitAccess |      .NET 4.8 | 6.992 ns | 0.7303 ns | 0.0400 ns |  0.97 |      33 B |
| StaticCtorAccess |      .NET 4.8 | 7.203 ns | 0.4652 ns | 0.0255 ns |  1.00 |      40 B |
|                  |               |          |           |           |       |           |
| ModuleInitAccess | .NET Core 3.1 | 6.898 ns | 0.7025 ns | 0.0385 ns |  1.05 |      27 B |
| StaticCtorAccess | .NET Core 3.1 | 6.545 ns | 0.5424 ns | 0.0297 ns |  1.00 |      27 B |
|                  |               |          |           |           |       |           |
| ModuleInitAccess | .NET Core 5.0 | 6.618 ns | 0.2620 ns | 0.0144 ns |  1.00 |      27 B |
| StaticCtorAccess | .NET Core 5.0 | 6.628 ns | 0.3717 ns | 0.0204 ns |  1.00 |      27 B |
kenichiudakenichiuda

.NET Framework 4.8 (4.8.4341.0), X64 RyuJIT

; StaticCtorBenchMark.StaticCtorVsModuleInit.ModuleInitAccess()
       sub       rsp,28
       mov       rcx,254B0339F38
       mov       rcx,[rcx]
       mov       rax,[rcx]
       mov       rax,[rax+40]
       call      qword ptr [rax+28]
       nop
       add       rsp,28
       ret
; Total bytes of code 33

.NET Framework 4.8 (4.8.4341.0), X64 RyuJIT

; StaticCtorBenchMark.StaticCtorVsModuleInit.StaticCtorAccess()
       mov       rax,offset StaticCtorBenchMark.StaticCtorVsModuleInit+StaticCtor.NextInt()
       jmp       rax
; Total bytes of code 13
; StaticCtorBenchMark.StaticCtorVsModuleInit+StaticCtor.NextInt()
       mov       rcx,1E672CA9F30
       mov       rcx,[rcx]
       mov       rax,[rcx]
       mov       rax,[rax+40]
       mov       rax,[rax+28]
       jmp       rax
; Total bytes of code 27

.NET Core 3.1.12 (CoreCLR 4.700.21.6504, CoreFX 4.700.21.6905), X64 RyuJIT

; StaticCtorBenchMark.StaticCtorVsModuleInit.ModuleInitAccess()
       mov       rcx,1902B387398
       mov       rcx,[rcx]
       mov       rax,[rcx]
       mov       rax,[rax+40]
       mov       rax,[rax+28]
       jmp       rax
; Total bytes of code 27

.NET Core 3.1.12 (CoreCLR 4.700.21.6504, CoreFX 4.700.21.6905), X64 RyuJIT

; StaticCtorBenchMark.StaticCtorVsModuleInit.StaticCtorAccess()
       mov       rcx,22090007390
       mov       rcx,[rcx]
       mov       rax,[rcx]
       mov       rax,[rax+40]
       mov       rax,[rax+28]
       jmp       rax
; Total bytes of code 27

.NET Core 5.0.3 (CoreCLR 5.0.321.7212, CoreFX 5.0.321.7212), X64 RyuJIT

; StaticCtorBenchMark.StaticCtorVsModuleInit.ModuleInitAccess()
       mov       rcx,17A10007328
       mov       rcx,[rcx]
       mov       rax,[rcx]
       mov       rax,[rax+40]
       mov       rax,[rax+28]
       jmp       rax
; Total bytes of code 27

.NET Core 5.0.3 (CoreCLR 5.0.321.7212, CoreFX 5.0.321.7212), X64 RyuJIT

; StaticCtorBenchMark.StaticCtorVsModuleInit.StaticCtorAccess()
       mov       rcx,1A94F9C7320
       mov       rcx,[rcx]
       mov       rax,[rcx]
       mov       rax,[rax+40]
       mov       rax,[rax+28]
       jmp       rax
; Total bytes of code 27
このスクラップは2021/03/07にクローズされました