有没有办法删除C#
中的数组边界检查?
以下是我想达到目标:
public static int F(int[] M, int i)
{
return M[i]; // I can guarantee that [i] will never be outside of [0, M.Length]
}
在这个函数调用之前,我有一个已经检查边界的逻辑(其中有一些额外的逻辑)。我想删除的是以下几行:
Program.F(Int32[], Int32)
L0000: sub rsp, 0x28
L0004: cmp edx, [rcx+8] ; I don't need this line
L0007: jae short L0015 ; I don't need this line
L0009: movsxd rax, edx
L000c: mov eax, [rcx+rax*4+0x10]
L0010: add rsp, 0x28
L0014: ret
L0015: call 0x00007ffc8877bc70 ; I don't need this line
L001a: int3 ; I don't need this line
提问
有没有办法删除这些指令?
注意事项
- 我试图放一个if检查,希望编译器会得到它,但它使情况变得更糟。
public static int G(int[] M, int i)
{
if (i >= 0 && i < M.Length)
return M[i];
return -1;
}
这产生:
Program.G(Int32[], Int32)
L0000: sub rsp, 0x28
L0004: test edx, edx
L0006: jl short L001f
L0008: mov eax, [rcx+8]
L000b: cmp eax, edx
L000d: jle short L001f
L000f: cmp edx, eax
L0011: jae short L0029
L0013: movsxd rax, edx
L0016: mov eax, [rcx+rax*4+0x10]
L001a: add rsp, 0x28
L001e: ret
L001f: mov eax, 0xffffffff
L0024: add rsp, 0x28
L0028: ret
L0029: call 0x00007ffc8877bc70
L002e: int3
你也看到了没什么用
- 我能做的是:使用
unsafe
:
public static unsafe int H(int* M, int i)
{
return M[i];
}
这就是我一直在寻找的东西:
Program.H(Int32*, Int32)
L0000: movsxd rax, edx
L0003: mov eax, [rcx+rax*4]
L0006: ret
但是很遗憾我不能为我的项目启用不安全。在“非不安全”的世界里有解决方案吗?
2条答案
按热度按时间5hcedyr01#
实际上有一种方法。在csFastFloat存储库中偶然发现的。
这里的想法是使用MemoryMarshall.GetArrayDataReference来获取数组中第一个元素的引用,然后添加shift来获取实际值:
而不限于仅
unmanaged
结构。通过不安全访问,它甚至可以在大数据(超过百万项)上执行速度提高10%
基准位于此gist中
k4emjkb12#
根据这个博客:
https://devblogs.microsoft.com/dotnet/performance_improvements_in_net_7/#bounds-check-elimination
net7中的PGO特性极大地优化了边界检查。但是我怀疑任何人都能从理论上计算出它在你的情况下是如何工作的,因为我认为它是不确定的。
我将设置以下参数,然后使用和不使用基准: