C#中是否有限制等价物
Is there restrict equivalent in C#
我有以下函数(我稍微清理了一下以便更容易理解)它获取目标数组获取索引 n
处的元素添加到它 src1[i]
然后将它与 src2[i]
相乘(没什么特别的):
static void F(long[] dst, long[] src1, long[] src2, ulong n)
{
dst[n] += src1[n];
dst[n] *= src2[n];
}
否这会生成以下 ASM
:
<Program>$.<<Main>$>g__F|0_0(Int64[], Int64[], Int64[], UInt64)
L0000: sub rsp, 0x28
L0004: test r9, r9
L0007: jl short L0051
L0009: mov rax, r9
L000c: mov r9d, [rcx+8]
L0010: movsxd r9, r9d
L0013: cmp rax, r9
L0016: jae short L0057
L0018: lea rcx, [rcx+rax*8+0x10]
L001d: mov r9, rcx
L0020: mov r10, [r9]
L0023: mov r11d, [rdx+8]
L0027: movsxd r11, r11d
L002a: cmp rax, r11
L002d: jae short L0057
L002f: add r10, [rdx+rax*8+0x10]
L0034: mov [r9], r10
L0037: mov edx, [r8+8]
L003b: movsxd rdx, edx
L003e: cmp rax, rdx
L0041: jae short L0057
L0043: imul r10, [r8+rax*8+0x10]
L0049: mov [rcx], r10
L004c: add rsp, 0x28
L0050: ret
L0051: call 0x00007ffc9dadb710
L0056: int3
L0057: call 0x00007ffc9dadbc70
L005c: int3
尽你所能,它添加了一堆东西,因为我可以保证 n
将在合法范围之间:我可以使用指针。
static unsafe void G(long* dst, long* src1, long* src2, ulong n)
{
dst[n] += src1[n];
dst[n] *= src2[n];
}
现在这生成更简单 ASM
:
<Program>$.<<Main>$>g__G|0_1(Int64*, Int64*, Int64*, UInt64)
L0000: lea rax, [rcx+r9*8]
L0004: mov rcx, rax
L0007: mov rdx, [rdx+r9*8]
L000b: add [rcx], rdx
L000e: mov rdx, [rax] ; loads the value again?
L0011: imul rdx, [r8+r9*8]
L0016: mov [rax], rdx
L0019: ret
您可能已经注意到,那里有一个额外的 MOV
(我想,至少我无法解释为什么会在那里)。
问题
- 如何删除该行?在
C
中,如果我没记错的话,我可以使用关键字 restrict
。 C#中有这样的关键字吗?遗憾的是我在互联网上找不到任何东西。
备注
- 这里是SharpLablink.
- 这是
C
示例:
void
f(int64_t *dst,
int64_t *src1,
int64_t *src2,
uint64_t n) {
dst[n] += src1[n];
dst[n] *= src2[n];
}
void
g(int64_t *restrict dst,
int64_t *restrict src1,
int64_t *restrict src2,
uint64_t n) {
dst[n] += src1[n];
dst[n] *= src2[n];
}
这会生成:
f:
mov r10, rdx
lea rdx, [rcx+r9*8]
mov rax, QWORD PTR [rdx]
add rax, QWORD PTR [r10+r9*8]
mov QWORD PTR [rdx], rax ; this is strange. It loads the value back to [RDX]?
; shouldn't that be other way around? I don't know.
imul rax, QWORD PTR [r8+r9*8]
mov QWORD PTR [rdx], rax
ret
g:
mov r10, rdx
lea rdx, [rcx+r9*8]
mov rax, QWORD PTR [rdx]
add rax, QWORD PTR [r10+r9*8]
imul rax, QWORD PTR [r8+r9*8]
mov QWORD PTR [rdx], rax
ret
这里是 Godbolt link.
这个:
dst[n] = (dst[n] + src1[n]) * src2[n];
删除多余的 mov
。
在 C#
中没有 C
语言的 restrict
限定词的等价物。
在C#
ECMA-334:2017 语言规范中,在23. Unsafe Code
章中,没有语法来指定只能通过特定指针访问内存的一部分。并且没有语法来指定指针指向的内存区域不重叠。因此没有这样的等价物。这可能是因为 C# 是一种托管语言,允许使用 pointers/unmanaged 内存的 unsafe
语法是 C# 中的一种边缘情况。指针上的 restrict
将是边缘情况的边缘情况。
我有以下函数(我稍微清理了一下以便更容易理解)它获取目标数组获取索引 n
处的元素添加到它 src1[i]
然后将它与 src2[i]
相乘(没什么特别的):
static void F(long[] dst, long[] src1, long[] src2, ulong n)
{
dst[n] += src1[n];
dst[n] *= src2[n];
}
否这会生成以下 ASM
:
<Program>$.<<Main>$>g__F|0_0(Int64[], Int64[], Int64[], UInt64)
L0000: sub rsp, 0x28
L0004: test r9, r9
L0007: jl short L0051
L0009: mov rax, r9
L000c: mov r9d, [rcx+8]
L0010: movsxd r9, r9d
L0013: cmp rax, r9
L0016: jae short L0057
L0018: lea rcx, [rcx+rax*8+0x10]
L001d: mov r9, rcx
L0020: mov r10, [r9]
L0023: mov r11d, [rdx+8]
L0027: movsxd r11, r11d
L002a: cmp rax, r11
L002d: jae short L0057
L002f: add r10, [rdx+rax*8+0x10]
L0034: mov [r9], r10
L0037: mov edx, [r8+8]
L003b: movsxd rdx, edx
L003e: cmp rax, rdx
L0041: jae short L0057
L0043: imul r10, [r8+rax*8+0x10]
L0049: mov [rcx], r10
L004c: add rsp, 0x28
L0050: ret
L0051: call 0x00007ffc9dadb710
L0056: int3
L0057: call 0x00007ffc9dadbc70
L005c: int3
尽你所能,它添加了一堆东西,因为我可以保证 n
将在合法范围之间:我可以使用指针。
static unsafe void G(long* dst, long* src1, long* src2, ulong n)
{
dst[n] += src1[n];
dst[n] *= src2[n];
}
现在这生成更简单 ASM
:
<Program>$.<<Main>$>g__G|0_1(Int64*, Int64*, Int64*, UInt64)
L0000: lea rax, [rcx+r9*8]
L0004: mov rcx, rax
L0007: mov rdx, [rdx+r9*8]
L000b: add [rcx], rdx
L000e: mov rdx, [rax] ; loads the value again?
L0011: imul rdx, [r8+r9*8]
L0016: mov [rax], rdx
L0019: ret
您可能已经注意到,那里有一个额外的 MOV
(我想,至少我无法解释为什么会在那里)。
问题
- 如何删除该行?在
C
中,如果我没记错的话,我可以使用关键字restrict
。 C#中有这样的关键字吗?遗憾的是我在互联网上找不到任何东西。
备注
- 这里是SharpLablink.
- 这是
C
示例:
void
f(int64_t *dst,
int64_t *src1,
int64_t *src2,
uint64_t n) {
dst[n] += src1[n];
dst[n] *= src2[n];
}
void
g(int64_t *restrict dst,
int64_t *restrict src1,
int64_t *restrict src2,
uint64_t n) {
dst[n] += src1[n];
dst[n] *= src2[n];
}
这会生成:
f:
mov r10, rdx
lea rdx, [rcx+r9*8]
mov rax, QWORD PTR [rdx]
add rax, QWORD PTR [r10+r9*8]
mov QWORD PTR [rdx], rax ; this is strange. It loads the value back to [RDX]?
; shouldn't that be other way around? I don't know.
imul rax, QWORD PTR [r8+r9*8]
mov QWORD PTR [rdx], rax
ret
g:
mov r10, rdx
lea rdx, [rcx+r9*8]
mov rax, QWORD PTR [rdx]
add rax, QWORD PTR [r10+r9*8]
imul rax, QWORD PTR [r8+r9*8]
mov QWORD PTR [rdx], rax
ret
这里是 Godbolt link.
这个:
dst[n] = (dst[n] + src1[n]) * src2[n];
删除多余的 mov
。
在 C#
中没有 C
语言的 restrict
限定词的等价物。
在C#
ECMA-334:2017 语言规范中,在23. Unsafe Code
章中,没有语法来指定只能通过特定指针访问内存的一部分。并且没有语法来指定指针指向的内存区域不重叠。因此没有这样的等价物。这可能是因为 C# 是一种托管语言,允许使用 pointers/unmanaged 内存的 unsafe
语法是 C# 中的一种边缘情况。指针上的 restrict
将是边缘情况的边缘情况。