我正在编写N-Body模拟,为了简化计算,我将整个空间划分为多个统一大小的区域。
对于每个身体,我计算同一区域内所有其他身体的力量,而对于其他区域,我将质量和距离聚合在一起,这样就可以完成更少的工作。
我有一个List<Region>
和Region
定义public void Index()
,它总结了此次迭代时的总质量。
我的Space.Tick()
函数有两种变体:
public void Tick()
{
foreach (Region r in Regions)
r.Index();
}
这很快。对于20x20x20 = 8000个区域,每个区域有100个物体=总共800000个物体,这样只需要大约0.1秒。 CPU图表在我的四核上显示了25%的利用率,这正是我所期望的。
现在我编写这个多线程变体:
public void Tick()
{
Thread[] threads = new Thread[Environment.ProcessorCount];
foreach (Region r in Regions)
while (true)
{
bool queued = false;
for (int i = 0; i < threads.Length; i++)
if (threads[i] == null || !threads[i].IsAlive)
{
Region s = r;
threads[i] = new Thread(s.Index);
threads[i].Start();
queued = true;
break;
}
if (queued)
break;
}
}
如果不明显,请快速解释一下:threads
是一个4的数组,就我的CPU而言。它开始是4x null
。对于每个区域,我遍历所有4个Thread
对象(可能是null
)。当我找到null
或IsAlive
的{{1}}时,我会将Index()
的{{1}}和Region
排队。我将Start()
设置为queued
,以便我可以判断该区域已开始编制索引。
此代码大约需要7秒钟。这比你慢了70倍。我知道设置线程,找到一个空闲的线程等有一些开销,但我仍然期望我至少会获得某种性能提升。
我做错了什么?
答案 0 :(得分:3)
为什么不试试PLINQ?
Regions.AsParallel().ForAll(x=>x.Index());
PLINQ对我来说通常是超快的,并且它会根据你的环境进行缩放。如果它不应该是Parallel,它会做单线程。
所以,如果你必须有一个多维数组进入函数,你可以这样做:
Regions.AsParallel().Cast<Region>().ForAll(x=>x.Index());