KEMBAR78
Nullable<T> interface check / dispatch is comparatively very slow · Issue #50915 · dotnet/runtime · GitHub
Skip to content

Nullable<T> interface check / dispatch is comparatively very slow #50915

@stephentoub

Description

@stephentoub

Repro:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Runtime.CompilerServices;

[MemoryDiagnoser]
[DisassemblyDiagnoser]
public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);

    private S _s = default(S);
    private C _c = new C();
    private S? _ns = (S?)default(S);

    [Benchmark] public int Struct() => CallM(_s);
    [Benchmark] public int Class() => CallM(_c);
    [Benchmark] public int Nullable() => CallM(_ns);
    [Benchmark] public int NullableSpecialized() => CallMSpecial(_ns);

    static int CallM<T>(T t)
    {
        if (t is IMyInterface)
        {
            return ((IMyInterface)t).M();
        }

        return 0;
    }

    static int CallMSpecial<T>(T? t) where T : struct
    {
        if (t.HasValue)
        {
            return CallM(t.GetValueOrDefault());
        }

        return 0;
    }
}

interface IMyInterface
{
    int M();
}

struct S : IMyInterface
{
    public int M() => 42;
}

class C : IMyInterface
{
    public int M() => 42;
}
Method Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated Code Size
Struct 0.4807 ns 0.0044 ns 0.0037 ns - - - - 16 B
Class 3.0842 ns 0.0045 ns 0.0038 ns - - - - 86 B
Nullable 88.6692 ns 0.5664 ns 0.4729 ns 0.0076 - - 48 B 132 B
NullableSpecialized 1.5149 ns 0.0087 ns 0.0077 ns - - - - 64 B

Is there anything we can do to make the nullable case faster and less inclined to box without needing a special code path to handle it (e.g. my CallMSpecial, which in my current actual code is another overload so that a nullable binds to it more tightly).

Here's what the codegen for the nullable case looks like:

; Program.CallM[[System.Nullable`1[[S, Benchmarks]], System.Private.CoreLib]](System.Nullable`1<S>)
       sub       rsp,28
       mov       [rsp+30],rcx
       lea       rdx,[rsp+30]
       mov       rcx,offset MT_System.Nullable`1[[S, Benchmarks]]
       call      CORINFO_HELP_BOX_NULLABLE
       mov       rdx,rax
       mov       rcx,offset MT_IMyInterface
       call      CORINFO_HELP_ISINSTANCEOFINTERFACE
       test      rax,rax
       je        short M01_L00
       lea       rdx,[rsp+30]
       mov       rcx,offset MT_System.Nullable`1[[S, Benchmarks]]
       call      CORINFO_HELP_BOX_NULLABLE
       mov       rdx,rax
       mov       rcx,offset MT_IMyInterface
       call      CORINFO_HELP_CHKCASTINTERFACE
       mov       rcx,rax
       mov       r11,7FFCDCEE05D0
       call      qword ptr [7FFCDD2705D0]
       nop
       add       rsp,28
       ret
M01_L00:
       xor       eax,eax
       add       rsp,28
       ret
; Total bytes of code 122

category:cq
theme:basic-cq
skill-level:intermediate
cost:medium
impact:small

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

Status

Optimizations

Relationships

None yet

Development

No branches or pull requests

Issue actions