“ is T x”对于值类型的实现和性能

问题描述 投票:2回答:1

我有类似的情况:

interface IStorage
{
    bool TryGetValue<T>(out T result) where T : struct;
}

class Storage<T> : IStorage where T : struct
{
    readonly T value;

    public Storage(T val)
    {
        value = val;
    }

    public bool TryGetValue<T2>(out T2 result) where T2 : struct
    {
        if(value is T2 val)
        {
            result = val;
            return true;
        }
        result = default;
        return false;
    }
}

在程序中,传递实现IStorage的实例,可以查询它们的特定类型的值。我可以使用类似IStorage<T>的类型并对其进行类型测试以检查其是否支持该类型,但是由于还有其他实现IStorage的类型可以决定它们在运行时是否支持该类型,因此这会使代码变得更加混乱。

现在我想知道value is T2 val。其目的是检查TT2是否为相同类型(并因此兼容),因为它们都是值类型。 T专用于TryGetValue,应返回true,对于所有其他类型,应返回false

我不确定这是否是检查的最佳实现。解决问题基本上有两个常规步骤:

  • 确定TT2是否相同。
  • value重新解释为T2并返回。
  • 可以考虑解决此问题的其他两种解决方案:将值强制转换为object并检查并取消装箱,或选择__refvalue(__makeref(value), T2),但可能不能保证在所有平台上都可以使用。

现在value is T2 val看起来很漂亮,传达了很好的含义,但是我也想知道性能影响和可能的优化。当我反汇编该方法时,它变成这样:

  .locals init ([0] !!T2 val,
           [1] !T V_1)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      !0 value
  IL_0006:  dup
  IL_0007:  stloc.1
  IL_0008:  box        !T
  IL_000d:  isinst     !!T2
  IL_0012:  brfalse.s  IL_0029
  IL_0014:  ldloc.1
  IL_0015:  box        !T
  IL_001a:  unbox.any  !!T2
  IL_001f:  stloc.0
  IL_0020:  ldarg.1
  IL_0021:  ldloc.0
  IL_0022:  stobj      !!T2
  IL_0027:  ldc.i4.1
  IL_0028:  ret
  IL_0029:  ldarg.1
  IL_002a:  initobj    !!T2
  IL_0030:  ldc.i4.0
  IL_0031:  ret

因此,表达式不仅将值一次装箱,而且两次装箱,第一次是isinst,第二次是unbox.any,所以它不仅隐藏了装箱(通常被认为是非常昂贵的),但它执行了两次。

我有两个问题:是否有更好的方法来实现这种专业化?尽管看起来CIL代码效率很低,但是否有可能稍后在运行时通过JIT对其进行优化?

[在这种情况下,我希望运行时推断出TT2的唯一实例应该返回true,并且它应该忽略所有其他代码,包括检查。可能是这样吗?

我有类似的情况:接口IStorage {bool TryGetValue (输出T结果),其中T:struct; }类Storage :IStorage,其中T:struct {只读T值; ...

c# .net cil
1个回答
1
投票

“这个CIL代码虽然看起来效率很低,但稍后可能在运行时由JIT优化吗?” -不,似乎结果JITted代码也很肿,但需要更多测试才能验证。我的小4.8框架编译为if (value is T2 val) to

00007FFDEF110E3D  mov         rdx,qword ptr [rbp+90h]  
00007FFDEF110E44  add         rdx,8  
00007FFDEF110E48  vmovdqu     xmm0,xmmword ptr [rdx]  
00007FFDEF110E4D  vmovdqu     xmmword ptr [rbp+40h],xmm0  
00007FFDEF110E53  lea         rdx,[rbp+40h]  
00007FFDEF110E57  mov         rcx,7FFDEF006C68h  
00007FFDEF110E61  call        00007FFE4E642570  
00007FFDEF110E66  mov         qword ptr [rbp+30h],rax  
00007FFDEF110E6A  mov         rdx,qword ptr [rbp+30h]  
00007FFDEF110E6E  mov         rcx,7FFDEF006C68h  
00007FFDEF110E78  call        00007FFE4E643D00  
00007FFDEF110E7D  test        rax,rax  
00007FFDEF110E80  je          00007FFDEF110ED7  
00007FFDEF110E82  lea         rdx,[rbp+40h]  
00007FFDEF110E86  mov         rcx,7FFDEF006C68h  
00007FFDEF110E90  call        00007FFE4E642570  
00007FFDEF110E95  mov         qword ptr [rbp+28h],rax  
00007FFDEF110E99  mov         rdx,qword ptr [rbp+28h]  
00007FFDEF110E9D  mov         rcx,7FFDEF006C68h  
00007FFDEF110EA7  call        00007FFE4E643D00  
00007FFDEF110EAC  mov         qword ptr [rbp+20h],rax  
00007FFDEF110EB0  mov         rdx,qword ptr [rbp+20h]  
00007FFDEF110EB4  mov         rcx,7FFDEF006C68h  
00007FFDEF110EBE  call        00007FFE4E6BC030  
00007FFDEF110EC3  vmovdqu     xmm0,xmmword ptr [rax]  
00007FFDEF110EC8  vmovdqu     xmmword ptr [rbp+58h],xmm0  
00007FFDEF110ECE  mov         dword ptr [rbp+38h],1  
00007FFDEF110ED5  jmp         00007FFDEF110EDC  
00007FFDEF110ED7  xor         eax,eax  
00007FFDEF110ED9  mov         dword ptr [rbp+38h],eax  
00007FFDEF110EDC  mov         eax,dword ptr [rbp+38h]  
00007FFDEF110EDF  movzx       eax,al  
00007FFDEF110EE2  mov         dword ptr [rbp+54h],eax  
00007FFDEF110EE5  cmp         dword ptr [rbp+54h],0  
00007FFDEF110EE9  je          00007FFDEF110F08  
© www.soinside.com 2019 - 2024. All rights reserved.