假设我使用 MemoryMarshal.CreateSpan
来访问本地值类型的字节,例如以下(不是很有用)代码:
using System;
using System.Runtime.InteropServices;
// namespace and class boilerplate go here
private static void Main()
{
int value = 0;
Span<byte> valueBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref value, 1));
var random = new Random();
while (value >= 0) // the check in question
{
random.NextBytes(valueBytes);
Console.WriteLine(value);
}
}
虽然此代码按预期工作,但如果变量 value
为除了通过 valueBytes
跨度间接地在循环中修改吗?我可以依靠对 value
的读取来为我提供写入到 valueBytes
的内容,或者这是否容易被重新排序?还是我只是因为最近涉足了一点 C++ 而变得偏执?
(注意我知道还有其他方法可以达到上述代码的预期效果,这不是关于如何获得全范围32位随机整数的问题或关于某些更大应用程序的XY问题我正在尝试将此代码放入其中,不存在这样更大的应用程序)
最佳答案
我认为,唯一确定的答案可以由在 Roslyn 和 RyuJIT 方面实现编译器优化的人提供。
由于您使用的是 .NET Core,您当然可以深入研究源代码并自行找到答案。不过,这将是特定编译器版本的答案。
查看为您的代码段生成的 IL 代码:
// int value = 0;
ldc.i4.0
stloc.0
// MemoryMarshal.CreateSpan(ref value, 1)
ldloca.s 0
ldc.i4.1
call valuetype System.Span`1<!!0> System.Runtime.InteropServices.MemoryMarshal::CreateSpan<int32>(!!0&, int32)
// the rest is omitted
注意 ldloca.s
操作码。此操作loads the address of the local variable onto the evaluation stack .
虽然我无法为您提供官方链接来证明这一点,但我很确定 C# 和 JIT 编译器都不会优化掉那个局部变量——仅仅因为它的地址被使用了,所以这个局部变量有可能会通过其地址进行变异。
如果您查看生成的汇编代码,您将确切地看到:局部变量在那里并被放置到堆栈上,它不是一个仅限寄存器的变量。
// int value = 0;
xor ecx,ecx
mov dword ptr [rsp+3Ch],ecx
WHILE_LOOP_START:
// ... do stuff
// effectively: if (value >= 0) goto WHILE_LOOP_START
cmp dword ptr [rsp+3Ch],0
jge WHILE_LOOP_START
尝试编写一些不会产生 ldloca.s
操作码的代码(例如,在循环中只是 ++value
),value
变量很可能会成为仅限寄存器的变量。
如果您以永远不会写入 value
的方式修改代码(初始化除外),JIT 编译器实际上将完全消除检查和变量本身:
LOOP:
// Console.WriteLine(0)
xor ecx,ecx
call CONSOLE_WRITE_LINE
// while (true)
jmp LOOP
不过有趣的是,C# 编译器不会进行这种优化:
// int value = 0;
ldc.i4.0
stloc.0
br.s WHILE_CHECK
LOOP_START:
// Console.WriteLine(value)
ldloc.0
call void System.Console::WriteLine(int32)
WHILE_CHECK:
// effectively: if (value >= 0) goto LOOP_START
ldloc.0
ldc.i4.0
bge.s LOOP_START
同样,我的答案中的 IL 和汇编代码是特定于平台和编译器的(甚至是特定于 CLR 的)。我无法向您提供证明文件。但我很确定没有编译器会优化掉一个获得地址的局部变量,甚至在调用方法/函数时用作参数。
也许 Roslyn 和 RyuJIT 团队的人可以给你一个更好的答案。
关于c# - 通过 Span<T> 修改变量时,优化构建和 JIT 编译会产生问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55068872/