Chapel 中的共享内存 n 体模拟
Shared memory n-body simulation in Chapel
我正在尝试重新实现 Peter Pacheco 的并行编程简介的第 6.1.6 章中介绍的 n 体模拟的共享内存实现。在那一章中,它是使用 OpenMP 实现的。
这是我使用 OpenMP. And here is a serial implementation using Chapel 的并行实现。我在使用 Chapel 实现共享内存并行实现时遇到问题。由于无法在 forall
循环中获取线程的等级,因此我无法使用与 OpenMP 实现中相同的方法。我将不得不使用 coforall
循环,创建任务并手动分发迭代。这似乎不切实际,表明在 Chapel 中有更优雅的方法来解决这个问题。
我正在寻找有关如何使用 Chapel 提供的工具更好地解决此问题的指导和建议。
我的建议是在 forall 循环中的 forces
上使用 (+) reduce intent,这将为每个任务提供自己的 forces
私有副本,然后 ( sum) 在任务完成时将它们各自的副本减少回原始 forces
变量。这将通过将以下 with 子句附加到您的 forall 循环来完成:
forall q in 0..#n_bodies with (+ reduce forces) {
在这里,我寻找其他方法使代码更优雅一些,并建议针对此问题从二维数组更改为数组的数组,以便折叠一堆类似代码的三重奏x、y、z 组件的语句简化为单个语句。我还使用了您的 pDomain
变量并为 [0..#3] real
创建了一个类型别名,以删除代码中的一些冗余。哦,我删除了 Math
和 IO
模块的 use
,因为它们在 Chapel 程序中自动使用。
这是离开我的地方:
config const filename = "input.txt";
config const iterations = 100;
config const out_filename = "out.txt";
const X = 0;
const Y = 1;
const Z = 2;
const G = 6.67e-11;
config const dt = 0.1;
// Read input file, initialize bodies
var f = open(filename, iomode.r);
var reader = f.reader();
var n_bodies = reader.read(int);
const pDomain = {0..#n_bodies};
type vec3 = [0..#3] real;
var forces: [pDomain] vec3;
var velocities: [pDomain] vec3;
var positions: [pDomain] vec3;
var masses: [pDomain] real;
for i in pDomain {
positions[i] = reader.read(vec3);
velocities[i] = reader.read(vec3);
masses[i] = reader.read(real);
}
f.close();
reader.close();
for i in 0..#iterations {
// Reset forces
forces = [0.0, 0.0, 0.0];
forall q in pDomain with (+ reduce forces) {
for k in pDomain {
if k <= q {
continue;
}
var diff = positions[q] - positions[k];
var dist = sqrt(diff[X]**2 + diff[Y]**2 + diff[Z]**2);
var dist_cubed = dist**3;
var tmp = -G * masses[q] * masses[k] / dist_cubed;
var force_qk = tmp * diff;
forces[q] += force_qk;
forces[k] -= force_qk;
}
}
forall q in pDomain {
positions[q] += dt * velocities[q];
velocities[q] += dt / masses[q] * forces[q];
}
}
var outf = open(out_filename, iomode.cw);
var writer = outf.writer();
for q in pDomain {
writer.writeln("%er %er %er %er %er %er".format(positions[q][X], positions[q][Y], positions[q][Z], velocities[q][X], velocities[q][Y], velocities[q][Z]));
}
writer.close();
outf.close();
您可以考虑进行的另一项更改是用以下整个数组语句替换更新位置和速度的 forall 循环:
positions += dt * velocities;
velocities += dt / masses * forces;
其中主要的权衡是 forall 将使用单个并行循环以融合的方式实现语句,而整个数组语句则不会(至少在当前版本的 1.18 版编译器中)。
我正在尝试重新实现 Peter Pacheco 的并行编程简介的第 6.1.6 章中介绍的 n 体模拟的共享内存实现。在那一章中,它是使用 OpenMP 实现的。
这是我使用 OpenMP. And here is a serial implementation using Chapel 的并行实现。我在使用 Chapel 实现共享内存并行实现时遇到问题。由于无法在 forall
循环中获取线程的等级,因此我无法使用与 OpenMP 实现中相同的方法。我将不得不使用 coforall
循环,创建任务并手动分发迭代。这似乎不切实际,表明在 Chapel 中有更优雅的方法来解决这个问题。
我正在寻找有关如何使用 Chapel 提供的工具更好地解决此问题的指导和建议。
我的建议是在 forall 循环中的 forces
上使用 (+) reduce intent,这将为每个任务提供自己的 forces
私有副本,然后 ( sum) 在任务完成时将它们各自的副本减少回原始 forces
变量。这将通过将以下 with 子句附加到您的 forall 循环来完成:
forall q in 0..#n_bodies with (+ reduce forces) {
在这里,我寻找其他方法使代码更优雅一些,并建议针对此问题从二维数组更改为数组的数组,以便折叠一堆类似代码的三重奏x、y、z 组件的语句简化为单个语句。我还使用了您的 pDomain
变量并为 [0..#3] real
创建了一个类型别名,以删除代码中的一些冗余。哦,我删除了 Math
和 IO
模块的 use
,因为它们在 Chapel 程序中自动使用。
这是离开我的地方:
config const filename = "input.txt";
config const iterations = 100;
config const out_filename = "out.txt";
const X = 0;
const Y = 1;
const Z = 2;
const G = 6.67e-11;
config const dt = 0.1;
// Read input file, initialize bodies
var f = open(filename, iomode.r);
var reader = f.reader();
var n_bodies = reader.read(int);
const pDomain = {0..#n_bodies};
type vec3 = [0..#3] real;
var forces: [pDomain] vec3;
var velocities: [pDomain] vec3;
var positions: [pDomain] vec3;
var masses: [pDomain] real;
for i in pDomain {
positions[i] = reader.read(vec3);
velocities[i] = reader.read(vec3);
masses[i] = reader.read(real);
}
f.close();
reader.close();
for i in 0..#iterations {
// Reset forces
forces = [0.0, 0.0, 0.0];
forall q in pDomain with (+ reduce forces) {
for k in pDomain {
if k <= q {
continue;
}
var diff = positions[q] - positions[k];
var dist = sqrt(diff[X]**2 + diff[Y]**2 + diff[Z]**2);
var dist_cubed = dist**3;
var tmp = -G * masses[q] * masses[k] / dist_cubed;
var force_qk = tmp * diff;
forces[q] += force_qk;
forces[k] -= force_qk;
}
}
forall q in pDomain {
positions[q] += dt * velocities[q];
velocities[q] += dt / masses[q] * forces[q];
}
}
var outf = open(out_filename, iomode.cw);
var writer = outf.writer();
for q in pDomain {
writer.writeln("%er %er %er %er %er %er".format(positions[q][X], positions[q][Y], positions[q][Z], velocities[q][X], velocities[q][Y], velocities[q][Z]));
}
writer.close();
outf.close();
您可以考虑进行的另一项更改是用以下整个数组语句替换更新位置和速度的 forall 循环:
positions += dt * velocities;
velocities += dt / masses * forces;
其中主要的权衡是 forall 将使用单个并行循环以融合的方式实现语句,而整个数组语句则不会(至少在当前版本的 1.18 版编译器中)。