值类型的"is T x"的实现和性能

  • 本文关键字:实现 性能 is 类型 c# .net cil
  • 更新时间 :
  • 英文 :


我的情况与此类似:

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是否是相同的类型(因此兼容(,因为两者都是值类型。专门用于TTryGetValue应该返回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,并且应忽略所有其他代码,包括检查。是这样吗?

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

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  

这是使用 ValueType 的限制之一 - 此类操作变得一团糟。如果这对您来说是一个常见的用例,也许这不应该是一个结构体?

最新更新