我碰到一個差排在速度使用以下兩種結構:靜態構造函數的性能和爲什麼我們不能指定beforefieldinit
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)爲什麼用beforefieldinit
的結構和沒有一個之間的時間差? (我想JITer在for循環中做了一些額外的工作,但是,我不知道如何查看JITer的輸出以查看內容。)
2)爲什麼編譯器設計者a)沒有使所有結構體beforefieldinit
是默認的,並且b)不給開發者明確指定該行爲的能力?當然,這個是假設你不能,因爲我一直沒有找到辦法。
編輯:
。 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會知道類型構造函數已經被調用,並且不會在編譯第二個循環時發出代碼。
除了Motti的回答是: This code產生更好的效果,因爲在JIT編譯不同之處,DoSecondLoop
的JIT編譯不發出靜態構造函數檢查,因爲它檢測到它是在DoFirstLoop
以前那樣,使每個循環執行以相同的速度。 (〜3秒)
這裏需要大量的一些觀點。您測量的開銷是* 1納秒*。是的,這是關於測試+跳轉指令的作用。 –
@Hans我知道開銷很小。我從來沒有真正寫過這樣的代碼在生產中使用。我目前正在進行「CLR工作如何」的工作,我一直在討論不尋常的事情。我真的只是想知道爲什麼JITer根據編譯器發出的屬性來做出它所做的決定。我應該可以買一本書。 –