假设我在X,Y空间中有多个Particle
s,我想将它们全部归一化,使得平均X和Y为0.
串行实施:
public void Normalise()
{
double avgX = 0.0;
double avgY = 0.0;
foreach (Particle p in Particles)
{
avgX += p.X;
avgY += p.Y;
}
avgX /= (double)Particles.Count;
avgY /= (double)Particles.Count;
foreach (Particle p in Particles)
{
p.X -= avgX;
p.Y -= avgY;
}
}
这很有效,而且性能也不错,因为它是O(n),但它“令人尴尬地平行”。看看我的PLINQ实现:
public void PNormalise()
{
double avgX = 0.0;
double avgY = 0.0;
Particles.AsParallel().ForAll(p =>
{
avgX += p.X;
avgY += p.Y;
});
avgX /= (double)Particles.Count;
avgY /= (double)Particles.Count;
Particles.AsParallel().ForAll(p =>
{
p.X -= avgX;
p.Y -= avgY;
});
}
我不确定这里的表现,但我认为它会更好。问题是,粒子都是随机跳动的。我只能假设+=
和avgX
上的avgY
操作相互竞争,即使它们已经相当原子化了。
我能做些什么来解决它吗?我不能lock
他们因为他们不是对象,但我不确定我是不是想要,因为锁定不是很贵?
答案 0 :(得分:5)
您可以通过Parallel LINQ的常规机制来绕过对锁(或原子操作)的需求:
var avgX = Particles.AsParallel().Average(p => p.X);
var avgY = Particles.AsParallel().Average(p => p.Y);
Particles.AsParallel().ForAll(p => { p.X -= avgX; p.Y -= avgY });
由于数字的总和是一个O(N)操作,如果这部分占用了很长一段时间,我会感到非常惊讶。
答案 1 :(得分:1)
使用
Particles.AsParallel().ForAll(p =>
{
Interlocked.Add(avgX, p.X);
Interlocked.Add(avgY, p.Y);
}
做一个线程安全的原子加法。有关详细信息,请参阅Interlocked Class。
的文档答案 2 :(得分:0)
实际上,并行化这个O(n) - 算法不会产生更好的性能,因为你有与计算指令大致相同的内存访问量。