繁体   English   中英

为什么多线程会在这里表现更差?

[英]Why does multithreading give a worse performance here?

我通过bitarray设置枚举每秒都为false。

现在我想通过分割它分成两个线程。对于一些奇怪的原因,不过,每个线程来完成工作的量需要更多的64%的时间的时间加快这,我不知道为什么?

这可能是由于某种CPU缓存效应? 我该怎么做呢?

我以前用lambda表达式尝试了8个线程,但它总是大约1400毫秒,但是在单线程中我常常得到850毫秒。 此外,当我让一个线程完成所有工作时,我花了830毫秒。 我只是不明白,有人知道这里的原因吗?

码:

    class Program
    {
        static int count = 0x10000000;
        static int half = count / 2;
        static BitArray bitArray = new BitArray(count);

        static unsafe void Main(string[] args)
        {
            Stopwatch sw = Stopwatch.StartNew();

#if SINGLE
            for (int i = 0; i < bitArray.Count; i += 2)
                bitArray.Set(i, true);
#else
            Thread thread1 = new Thread(Thread1);
            Thread thread2 = new Thread(Thread2);
            thread1.Start();
            thread2.Start();
            thread1.Join();
            thread2.Join();
#endif
            sw.Stop();

            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.ReadLine();
        }

        static void Thread1()
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < half; i += 2)
                bitArray.Set(i, true);
            sw.Stop();
            Console.WriteLine("Thread1: {0}", sw.ElapsedMilliseconds);
        }

        static void Thread2()
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = half; i < count; i += 2)
                bitArray.Set(i, true);
            sw.Stop();
            Console.WriteLine("Thread2: {0}", sw.ElapsedMilliseconds);
        }
    }

BitArray不是一个线程安全的类。 你不应该这样使用它。 事实上,除了正确性之外,这很可能是缓慢的原因。 原因如下:

如果你查看BitArray的源代码,它包含一个int version字段,它在每次操作时都会更新,特别是你调用的Set()

这意味着每个线程不断更新相同的内存位置,这是一个巨大的性能杀手,因为所有内核在访问此位置时都必须进行通信和同步。 在这种情况下,多线程解决方案的性能比单核解决方案更差,这是完全合理的。

因为线程并不像看起来那么容易。

首先,正如文档所述 ,BitArrays不是线程安全的。 这意味着当多个线程同时使用时,它们可能并且将会出现不可预测的行为。

至于性能损失,可能是由于你的两个线程导致的总线流量增加,试图同时修改相同的内存位置,不断地使彼此的缓存无效。

即使您认为您的线程没有修改相同的位置,也可能不是这样。 例如,BitArray有一个Count属性。 每次将位设置为1时,线程很可能会修改一个计数器变量,即支持Count属性。 由于竞争条件和陈旧值,这种并发修改是危险的,并且可能会增加总线流量,如前所述。

问题是CPU内核的工作频率为2-3GHz,而RAM和内存总线的工作频率为1Ghz。 ram速度慢得多,RAM访问需要大约100个CPU周期。 如果打破CPU的缓存机制,很明显性能会下降。

编辑:更不用说线程创建和加入涉及很大的开销。 如果你的工作是830ms一次性。 您不太可能通过多线程获得重大改进。 您还应该尝试去除线程中的Stopwatches,因为它们也是开销。

我修改了您的代码,以便测试运行10次并报告结果。 使用您的代码,我看到单线程与多线程测试的相似时序(每个线程大约需要1200毫秒)。

但是,正如其他人所说的那样,不能保证从多个线程使用单个BitArray不会导致线程之间发生争用。

通过为每个线程提供自己的BitArray而不是使用共享的静态BitArray,可以最简单地证明这一点。 通过这种方法,我通常会看到每个线程大约需要450毫秒,尽管偶尔会看到更长的时间:

Thread2: 415
Thread1: 420
447
Thread2: 414
Thread1: 420
496
Thread1: 1185
Thread2: 1198
1249
Thread1: 417
Thread2: 421
455
Thread1: 420
Thread2: 415
455
Thread2: 413
Thread1: 417
491
Thread2: 413
Thread1: 417
508
Thread2: 417
Thread1: 441
526
Thread1: 420
Thread2: 415
465
Thread1: 940
Thread2: 1005
1087

最终我认为这表明:

  • 尽管代码设计,但线程之间的BitArray仍然存在争用效应
  • 即使每个线程都有单独的位数组,代码的时序仍然存在“随机”效应,这表明通过这样的微基准测试,您总是能够有效地进行基准测试,而不仅仅是您编写的代码。 您还可以使用GC,CPU缓存,上下文切换,核心跳跃,秒表不准确等等。
  • 如果您尝试编写的代码的真正目的是尽可能快地填充位数组,那么您可能希望更接近线路,更多手动方法,可能使用其他语言。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM