有没有办法删除数组边界检查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]
}
Run Code Online (Sandbox Code Playgroud)
在这个函数调用之前,我有一个逻辑已经检查了边界(其中有一些额外的逻辑)。我要删除的内容如下:
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
Run Code Online (Sandbox Code Playgroud)
有没有办法删除这些指令?
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
Run Code Online (Sandbox Code Playgroud)
这会生成:
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
Run Code Online (Sandbox Code Playgroud)
如你所见,这没有帮助。
unsafe:public static int G(int[] M, int i)
{
if (i >= 0 && i < M.Length)
return M[i];
return -1;
}
Run Code Online (Sandbox Code Playgroud)
这会生成我正在寻找的内容:
Program.H(Int32*, Int32)
L0000: movsxd rax, edx
L0003: mov eax, [rcx+rax*4]
L0006: ret
Run Code Online (Sandbox Code Playgroud)
但遗憾的是我无法为我的项目启用不安全。“非不安全”世界有解决办法吗?
其实还有办法。在csFastFloat存储库中偶然发现了它。
\n这里的想法是使用MemoryMarshall.GetArrayDataReference获取对数组中第一项的引用,然后添加移位以获取实际值:
\n[MethodImpl(MethodImplOptions.AggressiveInlining)]\nstatic T FastAccessValue<T>(T[] ar, int index)\n{\n ref T tableRef = ref MemoryMarshal.GetArrayDataReference(ar);\n return Unsafe.Add(ref tableRef, (nint)index);\n}\nRun Code Online (Sandbox Code Playgroud)\n哪个是安全(?)相当于不安全版本
\n[MethodImpl(MethodImplOptions.AggressiveInlining)]\nstatic unsafe T FastAccessValueUnsafe<T>(T[] ar, int index) where T : unmanaged\n{\n fixed(T* ptr = ar)\n {\n return ptr[index];\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n不限于unmanaged结构。
通过不安全的访问,它甚至可以在大数据(超过百万个项目)上执行速度提高 10%
\npublic int SumUnsafe(int[] ints, int length)\n{\n int sum = 0;\n for (int i = 0; i < length; i++)\n {\n sum += FastAccessValue(ints, i);\n }\n return sum;\n}\nRun Code Online (Sandbox Code Playgroud)\npublic int SumDirect(int[] ints, int length)\n{\n int sum = 0;\n for (int i = 0; i < ints.Length; i++)\n {\n sum += ints[i];\n }\n return sum;\n}\nRun Code Online (Sandbox Code Playgroud)\n| 方法 | 整数 | 长度 | 意思是 | 错误 | 标准差 | 代码大小 |
|---|---|---|---|---|---|---|
| 直接求和 | Int32[100000] | 100000 | 80.13 \xce\xbcs | 0.748 \xce\xbcs | 0.700 \xce\xbcs | 29 乙 |
| 总和不安全 | Int32[100000] | 100000 | 81.99 \xce\xbcs | 0.535 \xce\xbcs | 0.446 \xce\xbcs | 33乙 |
| 直接求和 | Int32[1000000] | 1000000 | 854.73\xce\xbcs | 5.216 \xce\xbcs | 第4.624章 | 29 乙 |
| 总和不安全 | Int32[1000000] | 1000000 | 795.10\xce\xbcs | 2.680 \xce\xbcs | 第2.238章 | 33乙 |
| 直接求和 | Int32[10000000] | 10000000 | 10,104.72\xce\xbcs | 27.199 \xce\xbcs | 22.712\xce\xbcs | 29 乙 |
| 总和不安全 | Int32[10000000] | 10000000 | 9,126.06 \xce\xbcs | 30.329 \xce\xbcs | 26.886 \xce\xbcs | 33乙 |
基准位于这个要点
\n