我正试图通过使用Parallel.For来加快计算时间。我有一个英特尔核心i7 Q840 CPU有8个核心,但我只设法获得4的性能比相比,顺序for循环。这是与Parallel.For一样好,还是可以对方法调用进行微调以提高性能?
下面是我的测试代码,顺序如下:
var loops = 200;
var perloop = 10000000;
var sum = 0.0;
for (var k = 0; k < loops; ++k)
{
var sumk = 0.0;
for (var i = 0; i < perloop; ++i) sumk += (1.0 / i) * i;
sum += sumk;
}平行的:
sum = 0.0;
Parallel.For(0, loops,
k =>
{
var sumk = 0.0;
for (var i = 0; i < perloop; ++i) sumk += (1.0 / i) * i;
sum += sumk;
});我正在并行化的循环涉及到使用“全局”定义的变量sum进行计算,但是这应该只相当于并行化循环中总时间的一小部分。
发布版本构建(“优化代码”标志集)顺序for Parallel.For 循环在我的计算机上需要33.7 s,而Parallel.For循环需要8.4 s,性能比只有4.0.。
在任务管理器中,我可以看到在顺序计算期间CPU利用率为10-11%,而在并行计算中仅为70%。我试图显式地设置
ParallelOptions.MaxDegreesOfParallelism = Environment.ProcessorCount但没有结果。我不清楚为什么不把所有的CPU功率分配给并行计算?

我注意到在SO 在此之前上也提出了一个类似的问题,结果更加令人失望。然而,这个问题也涉及第三方库中的次等并行化。我主要关注的是核心库中基本操作的并行化。
更新
我在一些评论中指出,我使用的CPU只有4个物理核,如果启用超线程,系统就可以看到这4个内核。为了这一点,我禁用了超线程并重新设定了基准。
在超级线程被禁用之后,我的计算速度更快了,无论是并行的,还是(我所认为的)顺序for循环。for循环期间的CPU利用率约为100%。45% ()并且在Parallel.For循环中100%。
for循环15.6 s的计算时间(是启用超线程时的两倍多),Parallel.For的计算时间是6.2s(比启用超线程时要好25%)。与Parallel.For的性能比现在只有2.5,运行在4个真正的核心上。
因此,尽管超级线程被禁用,但性能比预期仍然要低得多.另一方面,令人感兴趣的是,在for循环期间,CPU利用率如此之高?在这个循环中也会有某种内部并行化吗?
发布于 2012-06-01 08:22:10
即使在不使用锁的情况下,使用全局变量也会带来严重的同步问题。当您为变量赋值时,每个核心必须访问系统内存中相同的位置,或者等待另一个核心在访问它之前完成。您可以通过使用更轻的Interlocked.Add方法在操作系统级别上原子地向和添加一个值,从而避免无锁的损坏,但是仍然会因为争用而导致延迟。
正确的方法是更新线程局部变量,以创建部分和,并将它们添加到最后的单个全局和中。Parallel.For有一个重载,它就是这样做的。MSDN甚至有一个在如何:编写具有线程局部变量的Parallel.For循环上使用求和的示例。
int[] nums = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
// Use type parameter to make subtotal a long, not an int
Parallel.For<long>(0, nums.Length, () => 0, (j, loop, subtotal) =>
{
subtotal += nums[j];
return subtotal;
},
(x) => Interlocked.Add(ref total, x)
);每个线程更新自己的小计值,并在完成时使用Interlocked.Add更新全局总计。
发布于 2012-06-01 08:01:36
Parallel.For和Parallel.ForEach将使用它认为合适的某种程度的并行性,平衡设置和拆卸线程的成本以及它希望每个线程执行的工作。与以前的.NET版本相比,.NET 4.5对性能进行了几次改进(包括对需要拆分的线程数量做出更明智的决定)。
请注意,即使每个内核都要拆分一个线程,上下文开关、虚假共享问题、资源锁和其他问题也可能会阻止您实现线性可伸缩性(通常,不一定使用特定的代码示例)。
发布于 2014-04-12 20:11:04
我认为计算增益太低了,因为您的代码“太容易”在每次迭代中处理其他任务--因为parallel.for只是在每次迭代中创建新任务,所以这需要时间在线程中为它们服务。我会这样做:
int[] nums = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
Parallel.ForEach(
Partitioner.Create(0, nums.Length),
() => 0,
(part, loopState, partSum) =>
{
for (int i = part.Item1; i < part.Item2; i++)
{
partSum += nums[i];
}
return partSum;
},
(partSum) =>
{
Interlocked.Add(ref total, partSum);
}
);分区程序将为每个任务创建最佳的作业部分,使用线程服务任务的时间将更少。如果可以,请对此解决方案进行基准测试,并告诉我们它是否会更好地加快速度。
https://stackoverflow.com/questions/10846550
复制相似问题