ant*_*rds 11 c# memory compilation
假设我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);
}
}
Run Code Online (Sandbox Code Playgroud)
尽管此代码按预期工作,但是如果未在循环中修改true变量value(除非通过valueBytes跨度间接修改),则指示的检查是否可以保证在编译为IL和JIT编译时仍可生存,而没有优化为?我可以依靠读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
Run Code Online (Sandbox Code Playgroud)
注意ldloca.s操作码。此操作将局部变量的地址加载到计算堆栈上。
虽然我无法向您提供证明这一点的官方链接,但我很确定 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
Run Code Online (Sandbox Code Playgroud)
尝试编写一些不产生ldloca.s操作码的代码(例如仅++value在循环中),该value变量很可能会成为仅寄存器变量。
如果您以从未写入的方式修改代码value(初始化除外),那么 JIT 编译器实际上将完全消除检查和变量本身:
LOOP:
// Console.WriteLine(0)
xor ecx,ecx
call CONSOLE_WRITE_LINE
// while (true)
jmp LOOP
Run Code Online (Sandbox Code Playgroud)
但有趣的是,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
Run Code Online (Sandbox Code Playgroud)
同样,我的答案中的 IL 和汇编代码是特定于平台和编译器的(甚至是特定于 CLR 的)。我无法向您提供证明文件。但我很确定没有编译器会优化获取其地址的局部变量,甚至在调用方法/函数时将其用作参数。
也许 Roslyn 和 RyuJIT 团队的人可以给你更好的答案。