N-Body问题:双循环的高效并行化

时间:2012-05-09 22:55:55

标签: math optimization parallel-processing

关于N体问题的一个非常普遍的问题是使用双循环来计算粒子之间的相互作用。考虑到n个粒子的N体问题,可以写出循环

for (i = 0, i < n; i++)
    for (j = i+1, j < n; j++)
        // calculate interaction

我的问题是如何使用不同的线程并行化这个循环。目标是每个线程“理想地”必须计算相同数量的交互。

我的想法是在不同的时间间隔分隔外循环,i循环,比如a_k = a(k),其中k = 1,2,...,p其中p是我们想要的线程数把问题分成了。

所以,循环可以写成

for (k = 1, k < p; k++)
    for (i = a(k), i < a(k+1); i++)
        for (j = i+1, j < n; j++)
            // calculate interaction

最外循环,即k循环,是要并行化的循环。

因为最内循环的交互次数,j循环是n-(i + 1),每个线程计算的交互次数是

\ sum_ {i = a(k)} ^ {a(k + 1)} n - (i + 1)

这意味着人们希望找到离散函数a_k,使得函数

f [a_k] = \ sum_ {i = a(k)} ^ {a(k + 1)} n - (i + 1)

边界条件a(1)= 0且a(p)= n是常数函数,因此强制每个线程上的交互次数相同。

我尝试过使用不同的“启发式”(例如a_k多项式,指数,对数),到目前为止还没有人给我一个满意的答案。这个问题的直接解决方案对我来说并不明显。

对于小p,这个问题可以放在“最小化问题”上,其中基本上每个a_k都是一个变量来最小化函数

f(a_1,a_2,a_3,...)= sum(| f [a_k] - n / p | ^ 2)

但是您可能猜到,对于更高的p值,这是无效的(甚至收敛)。

有没有人知道如何解决这个问题?

4 个答案:

答案 0 :(得分:3)

(对不起,如果没有明确表达,那在我脑子里就有意义了。)

当将所有数字从1加到N时,您会注意到N + 1 =(N - 1)+ 2 =(N - 2)+ 3等。

那么,如果每个线程使用一个小i和一个大i,那么总和会加起来怎么办?

或者,说你想要总是使用5个线程。线程1将执行前10%和最后10%,线程2将执行第二个10%和倒数第二个10%,依此类推。 “早期”和“后期”部分的每个配对都会增加相同的交互总数。

编辑:

从另一篇文章窃取图表......

   0 1 2 3 4 5 6 7 8

0  - A B C D D C B A
1    - B C D D C B A  
2      - C D D C B A
3        - D D C B A  
4          - D C B A
5            - C B A
6              - B A
7                - A
8                  -

这是否更清楚地表明了我的意思?

答案 1 :(得分:3)

您可以将对象划分为大约kN/k组的k*(k + 1)/2组,并使用它将您的初始三角形交互分析为 0 1 2 3 4 5 6 7 8 -- N=9; k=3; N/k=3 0 - A A B B B C C C 1 - A B B B C C C -- diagonal pieces: A, D, F 2 - B B B C C C 3 - D D E E E -- non-diagonal pieces: B, C, E 4 - D E E E 5 - E E E 6 - F F 7 - F 8 - 个:

(N/k)*(N/k - 1)/2

这个观点很复杂,有两种:沿对角线的那些(有(N/k)*(N/k)元素的三角形)和不是k*k/2的正方形。元件)。但是,由于对角线块的大小约为方块的一半,因此您可以为每个线程分配两个以平衡负载 - 总共2*N/k个大致相等的任务。

此方法的一个优点是每个任务只需要访问{{1}}实体的数据,这可以使其显着更加缓存。

答案 2 :(得分:2)

假设您的编译器支持OpenMP,为什么不能简单地尝试

#pragma omp parallel for schedule(dynamic) // or: schedule(guided)
for (i = 0; i < n; i++)
    for (j = i+1; j < n; j++)
        // calculate interaction

甚至(你需要通过基准来了解哪一个表​​现更好)

#pragma omp parallel
const int stride = omp_get_num_threads() + 1; 
for (i = omp_get_thread_num(); i < n; i += stride)
    for (j = i+1; j < n; j++)
        // calculate interaction

答案 3 :(得分:0)

今天我刚刚找到了解决方案。在有人确认之前我不接受它

为了使f [a_k]成为关于k的常数函数,则

f [a_ {k + 1}] - f [a_k] = 0

对于k = 1,2,3,...,p-1,

必须为真。

我们可以使用我在问题上发布的定义来扩展这个等式,我们得到一个关于a_k,k = 1,2,3,...的“p”2阶阶代数方程组,页。我没有看到任意p的封闭解决方案,但可以通过解析解决每个p。

我已经确认:

  1. 总和,当使用a_k时,我计算的是n(n-1)/ 2,这个问题的相互作用总数。

  2. 对于p = 2,3,4,5和10,每个线程的交互次数确实是恒定的(其中p = 10需要一些时间来计算mathematica®)。

  3. 修改

    在详细检查了p的不同值的解后,我达到了一般闭合解

    a_k = 1 /(2 p)( - p + 2 p n - sqrt [p ^ 2 + 4 p(p + 1 - k)(n - 1)n])

    对每个p> = 2,n> 1都有效。

    这就完成了答案。