我正在編寫一個程序來說明多線程程序中緩存爭用的影響。我的第一步是創建一個long
的數組,並顯示如何修改相鄰的項目會導致爭用。這是該計劃。爲什麼數組的併發修改很慢?
const long maxCount = 500000000;
const int numThreads = 4;
const int Multiplier = 1;
static void DoIt()
{
long[] c = new long[Multiplier * numThreads];
var threads = new Thread[numThreads];
// Create the threads
for (int i = 0; i < numThreads; ++i)
{
threads[i] = new Thread((s) =>
{
int x = (int)s;
while (c[x] > 0)
{
--c[x];
}
});
}
// start threads
var sw = Stopwatch.StartNew();
for (int i = 0; i < numThreads; ++i)
{
int z = Multiplier * i;
c[z] = maxCount;
threads[i].Start(z);
}
// Wait for 500 ms and then access the counters.
// This just proves that the threads are actually updating the counters.
Thread.Sleep(500);
for (int i = 0; i < numThreads; ++i)
{
Console.WriteLine(c[Multiplier * i]);
}
// Wait for threads to stop
for (int i = 0; i < numThreads; ++i)
{
threads[i].Join();
}
sw.Stop();
Console.WriteLine();
Console.WriteLine("Elapsed time = {0:N0} ms", sw.ElapsedMilliseconds);
}
我跑的Visual Studio 2010,程序在Release模式編譯,.NET 4.0目標, 「任何CPU」,並且沒有附加的調試器(Ctrl + F5鍵)在64位運行時執行。
該程序在我的系統上運行約1700毫秒,使用單線程。有了兩個線程,它需要超過25秒。鑑於差異是緩存爭用,我設置Multipler = 8
並再次運行。結果是12秒,所以爭論至少是部分的問題。
增加Multiplier
超過8不會提高性能。
爲了比較,similar program that doesn't use an array只需約2200與兩個線程毫秒時的變量是相鄰的。當我分離變量時,兩個線程版本的運行時間與單線程版本相同。
如果問題是數組索引的開銷,你希望它在單線程版本出現。在我看來,修改陣列時會發生某種互斥,但我不知道它是什麼。
看着生成的IL並不是很有啓發性。也沒有看到拆卸。反彙編確實顯示了一些對(我認爲)運行時庫的調用,但我無法進入它們。
我不精通的WinDbg或其他低級別的調試工具,這些天。自從我需要它以來已經很長時間了。所以我很難過。
我唯一的假設現在是運行時代碼中設置「髒」標誌在每次寫入。看起來像這樣的事情是需要的,以便支持拋出異常,如果數組在枚舉時被修改。但我輕易承認,我沒有直接的證據來支持這一假設。
有人可以告訴我是什麼導致了這個大的放緩?
僅供參考陣列不拋出文章,如果他們同時列舉了修改。 (實施將在其權利範圍內,但實際上CLR實施不這樣做。) – 2011-12-29 20:05:46
@Eric:感謝您的信息。這很好。 – 2011-12-29 20:15:52