IL_*_*ent 10 .net c# .net-core
码:
internal class Program
{
private static void Main(string[] args)
{
const int iterCount = 999999999;
var sum1 = 0;
var sum2 = 0;
using (new Dis())
{
var sw = DateTime.Now;
for (var i = 0; i < iterCount; i++)
sum1 += i;
Console.WriteLine(sum1);
Console.WriteLine(DateTime.Now - sw);
}
using (new Dis())
{
var sw = DateTime.Now;
for (var i = 0; i < iterCount; i++)
sum2 += i;
Console.WriteLine(sum2);
Console.WriteLine(DateTime.Now - sw);
}
Console.ReadLine();
}
private class Dis : IDisposable
{
public void Dispose(){}
}
}
Run Code Online (Sandbox Code Playgroud)
相同使用中的两个相同的块.
输出:
2051657985
00:00:00.3690996
2051657985
00:00:02.2640266
Run Code Online (Sandbox Code Playgroud)
第二块需要2.2秒!但是如果要摆脱使用,持续时间变得相同(~0.3秒,就像第一个一样).我试过.net framework 4.5和.net core 1.1,在发布中,结果是一样的.
任何人都可以解释这种行为吗?
Han*_*ant 12
您必须查看抖动生成的机器代码以查看根本原因.使用工具>选项>调试>常规>取消选中抑制JIT优化选项.切换到发布版本.在第一个和第二个循环上设置断点.当它命中时使用Debug> Windows> Disassembly.
您将看到for循环体的机器代码:
sum1 += i;
00000035 add esi,eax
Run Code Online (Sandbox Code Playgroud)
和:
sum2 += i;
000000d9 add dword ptr [ebp-24h],eax
Run Code Online (Sandbox Code Playgroud)
或者换句话说,sum1变量存储在CPU寄存器中esi.但是sum2变量存储在内存中,在方法的堆栈帧上.巨大的,巨大的差异.寄存器非常快,内存很慢.堆栈帧的内存将位于L1缓存中,在访问该缓存的现代机器上具有3个周期的延迟.存储缓冲区将很快被大量写入所淹没,并导致处理器停止.
找到一种将变量保存在CPU寄存器中的方法是主要的抖动优化任务之一.但这有局限性,特别是x86几乎没有可用的寄存器.当它们全部用完时,抖动没有选择,只能使用内存.请注意,该using语句在引擎盖下有一个额外的隐藏局部变量,这就是它产生影响的原因.
理想情况下,抖动优化器可以更好地选择如何分配寄存器.将它们用于循环变量(它所做的)和和变量.一个提前编译器可以做到这一点,有足够的时间来执行代码分析.但是,即时编译器在严格的时间限制下运行.
基本的对策是:
最后一个子弹对遗留的x64抖动(目标.NET 3.5使用它)有效,但不适用于4.6中首次提供的x64抖动重写(又名RYuJIT).重写是必要的,因为遗留抖动花费了太多时间来优化代码.令人失望的是,RyuJIT确实有令人失望的诀窍,我认为它的优化者可以在这里做得更好.
| 归档时间: |
|
| 查看次数: |
206 次 |
| 最近记录: |