【发布时间】:2016-07-18 09:51:33
【问题描述】:
我尝试使用Parallel.For() 计算列表的平均值。我决定反对它,因为它比简单的串行版本慢四倍。然而,我对它不会产生与连续剧完全相同的结果这一事实很感兴趣,我认为了解原因会很有启发性。
我的代码是:
public static double Mean(this IList<double> list)
{
double sum = 0.0;
Parallel.For(0, list.Count, i => {
double initialSum;
double incrementedSum;
SpinWait spinWait = new SpinWait();
// Try incrementing the sum until the loop finds the initial sum unchanged so that it can safely replace it with the incremented one.
while (true) {
initialSum = sum;
incrementedSum = initialSum + list[i];
if (initialSum == Interlocked.CompareExchange(ref sum, incrementedSum, initialSum)) break;
spinWait.SpinOnce();
}
});
return sum / list.Count;
}
当我在 2000000 个点的随机序列上运行代码时,我得到的结果在最后 2 位数字与序列平均值不同。
我搜索了 stackoverflow 并找到了这个:VB.NET running sum in nested loop inside Parallel.for Synclock loses information。然而,我的情况与那里描述的情况不同。有一个线程局部变量temp 是不准确的原因,但我使用根据教科书Interlocked.CompareExchange() 模式更新的单个总和(我希望)。由于性能不佳,这个问题当然没有实际意义(这让我感到惊讶,但我知道开销),但我很好奇是否可以从这个案例中学到一些东西。
感谢您的想法。
【问题讨论】:
-
你期待什么?你看起来很多比较交换操作什么都不做。不健康。
-
浮点加法不是关联的。您不能简单地并行化顺序求和并期望相同的结果。我根本看不出您的特定情况应该与链接的问题有何不同-碰巧比较(不)相等的部分总和仍然不能保证最终结果相同,因为(这是基本问题)您仍在重新排列术语的顺序。
-
@TomTom:你是对的,但问题是价值的差异。
-
@Jeroen Mostert:首先我想说他们测试了关联性,我测试了交换性,但仔细想想,你是对的。总是涉及关联性,因为先前的总和表示每次运行时不同的分组。感谢您的见解,并对愚蠢的问题表示歉意。
-
这不是一个愚蠢的问题;看看为什么这不起作用非常有用。如果您需要准确的结果并且您的范围不是太大,请使用
decimal。您可以利用并行 LINQ:list.AsParallel().Sum()。
标签: c# parallel-processing mean interlocked