3

假设我在 X,Y 空间中有许多Particles,并且我想对它们进行归一化,使得 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,因为它们不是对象,但我不确定我是否想要,因为锁定不是很昂贵吗?

4

3 回答 3

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) 操作,如果这部分花费了任何大量时间,我会感到非常惊讶。

于 2012-11-19T01:13:21.937 回答
1

采用

Particles.AsParallel().ForAll(p =>
{
    Interlocked.Add(avgX, p.X);
    Interlocked.Add(avgY, p.Y);
}

做一个线程安全的原子加法。有关详细信息,请参阅Interlocked Class的文档。

于 2012-11-19T01:09:11.780 回答
0

实际上,并行化这个 O(n) 算法不会带来更好的性能,因为您的内存访问量与计算指令大致相同。

于 2012-11-19T01:18:50.407 回答