为什么并发修改数组这么慢?

Jim*_*hel 31 .net c# multithreading

我正在编写一个程序来说明多线程程序中缓存争用的影响.我的第一个切入是创建一个数组,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);
}
Run Code Online (Sandbox Code Playgroud)

我正在运行Visual Studio 2010,在发布模式下编译的程序,.NET 4.0目标,"任何CPU",并在没有附加调试器的情况下在64位运行时执行(Ctrl + F5).

该程序在我的系统上运行大约1,700毫秒,只有一个线程.使用两个线程,需要超过25秒.确定差异是缓存争用,我设置Multipler = 8并再次运行.结果是12秒,因此争用至少是问题的一部分.

增加到Multiplier8以上并不能提高性能.

为了比较,当变量相邻时,不使用数组类似程序仅需要大约2,200 ms和两个线程.当我分离变量时,两个线程版本在与单线程版本相同的时间内运行.

如果问题是数组索引开销,那么您希望它出现在单线程版本中.在我看来,在修改数组时会发生某种互斥,但我不知道它是什么.

看着生成的IL并不是很有启发性.也没有查看拆卸.反汇编确实显示了几个调用(我认为)运行时库,但我无法进入它们.

这些天我不熟悉windbg或其他低级调试工具.自从我需要它以来已经很长时间了.所以我很难过.

我现在唯一的假设是运行时代码在每次写入时都设置了"脏"标志.似乎需要这样的东西,以便在枚举时修改数组时支持抛出异常.但我很乐意承认,我没有直接证据来支持这一假设.

任何人都可以告诉我是什么导致了这次大幅放缓?

Nic*_*ler 36

你有错误的分享.我在这里写了一篇关于它的文章