【发布时间】:2011-11-16 18:04:18
【问题描述】:
我在使用以下两个结构时遇到了速度差异:
public struct NoStaticCtor
{
private static int _myValue = 3;
public static int GetMyValue() { return _myValue; }
}
public struct StaticCtor
{
private static int _myValue;
public static int GetMyValue() { return _myValue; }
static StaticCtor()
{
_myValue = 3;
}
}
class Program
{
static void Main(string[] args)
{
long numTimes = 5000000000; // yup, 5 billion
Stopwatch sw = new Stopwatch();
sw.Start();
for (long i = 0; i < numTimes; i++)
{
NoStaticCtor.GetMyValue();
}
sw.Stop();
Console.WriteLine("No static ctor: {0}", sw.Elapsed);
sw.Restart();
for (long i = 0; i < numTimes; i++)
{
StaticCtor.GetMyValue();
}
sw.Stop();
Console.WriteLine("with static ctor: {0}", sw.Elapsed);
}
}
产生结果:
Release (x86), no debugger attached:
No static ctor: 00:00:05.1111786
with static ctor: 00:00:09.9502592
Release (x64), no debugger attached:
No static ctor: 00:00:03.2595979
with static ctor: 00:00:14.5922220
编译器为NoStaticCtor 生成一个静态构造函数,它与StaticCtor 中显式声明的构造函数相同。我知道编译器只会在未明确定义静态构造函数时发出beforefieldinit。
他们产生几乎相同的 il 代码,除了一个区别,用beforefieldinit 声明结构,我觉得区别就在这里,因为我知道它决定了何时调用类型构造函数,虽然我不太明白为什么会有这样的差异。它假定它不是每次迭代都调用类型构造函数,因为类型构造函数只能被调用一次。1
所以,
1) 为什么带有beforefieldinit 的结构和不带beforefieldinit 的结构之间存在时间差? (我想 JITer 在 for 循环中做了一些额外的事情,但是,我不知道如何查看 JITer 的输出以了解什么。
2) 为什么编译器设计者 a) 没有将所有结构 beforefieldinit 设为默认值,并且 b) 没有让开发人员能够明确指定该行为?当然,这是假设你不能,因为我一直没能找到方法。
编辑:
1。 I modified the code,基本上每个循环都运行了第二次,期待改进,但并不多:
No static ctor: 00:00:03.3342359
with static ctor: 00:00:14.6139917
No static ctor: 00:00:03.2229995
with static ctor: 00:00:12.9524860
Press any key to continue . . .
我这样做是因为我虽然,嗯,也许,但不太可能,JITer 实际上在每次迭代时都调用类型构造函数。在我看来,JITer 会知道类型构造函数已被调用,并且在编译第二个循环时不会发出代码来执行此操作。
除了莫蒂的回答:
This code 产生更好的结果,由于 JITing 的不同,DoSecondLoop 的 JITing 不会发出静态 ctor 检查,因为它检测到它之前在 DoFirstLoop 中完成,导致每个循环以相同的速度执行。 (约 3 秒)
【问题讨论】:
-
这里需要对大数有一些看法。您测量的开销是一纳秒。是的,这就是测试+跳转指令所需要的。
-
@Hans 我知道开销很小。我永远不会真正编写像这样用于生产的代码。我目前正处于“CLR 如何工作”的阶段,并且一直在处理不寻常的事情。我真的只是想了解为什么 JITer 根据编译器发出的属性做出决定。我可能应该买一本书。
标签: c# .net performance clr