有没有办法去除 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
有没有办法去除这些指令?
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
但遗憾的是,我无法为我的项目启用 unsafe。 “非不安全”的世界有解决方案吗?
答案 0 :(得分:1)
遗憾的是我无法添加评论,没有不安全的方法(据我所知) 您可能应该尝试解决不让您添加不安全的问题
答案 1 :(得分:0)
其实是有办法的。在 csFastFloat 存储库中偶然发现它。
这里的想法是使用 MemoryMarshall.GetArrayDataReference 获取对数组中第一项的引用,然后添加移位以获取实际值:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static T FastAccessValue<T>(T[] ar, int index)
{
ref T tableRef = ref MemoryMarshal.GetArrayDataReference(ar);
return Unsafe.Add(ref tableRef, (nint)index);
}
哪个是安全的(?) 等价于不安全的版本
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static unsafe T FastAccessValueUnsafe<T>(T[] ar, int index) where T : unmanaged
{
fixed(T* ptr = ar)
{
return ptr[index];
}
}
不限于仅 unmanaged
结构。
在不安全访问的情况下,它在处理大数据(超过百万项)时的执行速度甚至提高了 10%
public int SumUnsafe(int[] ints, int length)
{
int sum = 0;
for (int i = 0; i < length; i++)
{
sum += FastAccessValue(ints, i);
}
return sum;
}
public int SumDirect(int[] ints, int length)
{
int sum = 0;
for (int i = 0; i < ints.Length; i++)
{
sum += ints[i];
}
return sum;
}
方法 | 整数 | 长度 | 平均 | 错误 | StdDev | 代码大小 |
---|---|---|---|---|---|---|
SumDirect | Int32[100000] | 100000 | 80.13 μs | 0.748 μs | 0.700 μs | 29 B |
求和不安全 | Int32[100000] | 100000 | 81.99 μs | 0.535 μs | 0.446 μs | 33 B |
SumDirect | Int32[1000000] | 1000000 | 854.73 μs | 5.216 μs | 4.624 μs | 29 B |
求和不安全 | Int32[1000000] | 1000000 | 795.10 μs | 2.680 μs | 2.238 μs | 33 B |
SumDirect | Int32[10000000] | 10000000 | 10,104.72 μs | 27.199 μs | 22.712 μs | 29 B |
求和不安全 | Int32[10000000] | 10000000 | 9,126.06 μs | 30.329 μs | 26.886 μs | 33 B |
基准位于此 gist