【发布时间】:2021-04-04 07:51:01
【问题描述】:
有没有办法删除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
但遗憾的是,我无法为我的项目启用 unsafe。 “非不安全”的世界有解决方案吗?
【问题讨论】:
-
您是否分析过代码并得出边界检查实际上会大大减慢速度的结论? stackoverflow.com/questions/16713076/…
-
@trenki 是的,
unsafe版本比普通版本更快。但正如我所说,在我的项目中启用 unsafe 对我来说很难。也很难包含基准,因为它有很多依赖项,并且清除它们并将它们包含在我的问题中会花费太多时间(+ 我不认为更改代码会给我们带来准确的结果)。跨度> -
这样短的方法在编译过程中被内联并且没有开销SharpLab。
-
@Hrant。你是对的。我不习惯阅读asm,所以没听懂。做了一些基准测试 1_000_000 个整数:直接求和
0 .. ar.Length花了 803μs,fsumlength - 1 .. 0891μs 并且没有检查优化0 .. length我的 PC 上的 918μs