我通过比特数设置枚举每秒都为假。
现在我想通过将其分成两个线程来加快速度......但是出于一些奇怪的原因,每个线程执行 HALF 工作量的时间需要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);
}
}
答案 0 :(得分:9)
BitArray不是线程安全的类。你不应该这样使用它。事实上,除了正确性之外,这很可能是缓慢的原因。原因如下:
如果查看BitArray的源代码,它会包含一个int version
字段,该字段会在每次操作时更新,特别是Set()
,您可以调用它。
这意味着每个线程不断更新相同的内存位置,这是巨大的性能杀手,因为所有内核在访问此位置时都必须进行通信和同步。在这种情况下,多线程解决方案的性能比单核解决方案更差。
答案 1 :(得分:2)
因为线程并不像看起来那么容易。
首先,正如documentation所述,BitArrays不是线程安全的。这意味着当多个线程同时使用时,它们可能并且将会出现不可预测的行为。
至于性能损失,可能是由于你的两个线程导致的总线流量增加,试图同时修改相同的内存位置,不断地使彼此的缓存无效。
即使您认为您的线程没有修改相同的位置,也可能不是这样。例如,BitArray有一个Count
属性。每次将一个位设置为1时,线程很可能会修改一个计数器变量,即支持Count
属性。由于race conditions和过时值,这种并发修改是危险的,并且可能会增加总线流量,如前所述。
问题是CPU内核工作在2-3GHz,而RAM和内存总线工作在1Ghz。 ram速度慢得多,RAM访问需要大约100个CPU周期。如果你打破了CPU的缓存机制,很明显性能会下降。
编辑:更不用说线程创建和加入涉及很大的开销。如果你的工作是830ms一次性。您不太可能通过多线程获得重大改进。您还应该尝试去掉线程中的Stopwatches,因为它们也是开销。
答案 2 :(得分:1)
我修改了您的代码,以便测试运行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
最终我认为这表明: