C# PLINQ AsParallel().ForAll()访问资源
假设我在X,Y空间中有许多C# PLINQ AsParallel().ForAll()访问资源,c#,multithreading,linq,plinq,C#,Multithreading,Linq,Plinq,假设我在X,Y空间中有许多粒子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)Partic
粒子
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;
}
}
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;
});
}
这是可行的,性能也不错,因为它是O(n),但它“令人尴尬地平行”。看看我的PLINQ实现:
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;
}
}
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
上的+=
操作相互竞争,即使它们已经相当原子化了
我能帮你修一下吗?我不能锁定它们,因为它们不是对象,但我不确定我是否愿意,因为锁定不是很昂贵吗?使用
Particles.AsParallel().ForAll(p =>
{
Interlocked.Add(avgX, p.X);
Interlocked.Add(avgY, p.Y);
}
执行线程安全的原子加法。有关更多信息,请参阅的文档。您可以通过并行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)运算,如果这部分占用了大量时间,我会非常惊讶。实际上,并行化这个O(N)算法不会带来更好的性能,由于您的内存访问量与计算指令大致相同。更好的解决方案可能是执行并行求和,自动为您处理此问题。互锁。只添加重载(ref int,int)
和(ref long,long)
,但是我使用的是double,我不知道JIT编译器是否会这样做,但是它很可能会通过修改来更改指令。关于CLR和扩展(如SSEYes)的更多信息,但该算法仍然是内存受限的,因此CPU仍然大部分空闲,但它所做的少量工作现在均匀地分布在其内核中。@MathiasBecher:O(N)这样的问题实际上非常适合并行性。如果您的问题规模越来越大,您可以在大多数情况下向其投入更多硬件,以达到与以前类似的水平。我同意,除非你有N=10^7这样的东西,否则它可能有点微不足道。没有“相当原子化”这样的东西,一个操作要么是原子的,要么不是。