Симуляция n-тела с общей памятью в часовне
Я пытаюсь повторно реализовать реализацию совместной памяти для симуляции n-body, представленной Питером Пачеко в главе 6.1.6 "Введение в параллельное программирование". В этой главе это было реализовано с использованием OpenMP.
Вот моя параллельная реализация с использованием OpenMP. А вот и последовательная реализация с использованием Chapel. У меня проблемы с реализацией параллельной реализации совместно используемой памяти с помощью Chapel. Поскольку нет способа получить ранг потока в forall
цикл, я не могу использовать тот же подход, что и в реализации OpenMP. Я должен был бы использовать coforall
цикл, создавать задачи и распределять итерации вручную. Это не кажется практичным и предполагает, что есть более элегантный способ решить эту проблему в часовне.
Я ищу рекомендации и предложения о том, как лучше решить эту проблему с помощью инструментов, предоставляемых Chapel.
1 ответ
Мое предложение будет использовать (+) уменьшить намерение forces
в вашем цикле, который даст каждой задаче свою собственную частную копию forces
а затем (сумма) уменьшить их отдельные копии обратно в оригинал forces
переменная по мере выполнения задач. Это можно сделать, прикрепив следующее предложение with к вашей петле forall:
forall q in 0..#n_bodies with (+ reduce forces) {
В то время как здесь, я искал другие способы сделать код немного более элегантным и предложил бы перейти от двумерного массива к массиву массивов для этой проблемы, чтобы свернуть кучу трио подобных операторов кода для x, y z компонентов до одного оператора. Я также использовал ваш pDomain
переменная и создал псевдоним типа для [0..#3] real
для того, чтобы удалить некоторую избыточность в коде. Ох, и я удалил use
с Math
а также IO
модули, потому что они автоматически используются в программах 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();
Еще одно изменение, которое вы могли бы рассмотреть, - заменить цикл Форалла, который обновляет позиции и скорости, следующими операторами целого массива:
positions += dt * velocities;
velocities += dt / masses * forces;
где основной компромисс был бы в том, что forall будет реализовывать операторы слитно, используя один параллельный цикл, в то время как операторы из целого массива не будут (по крайней мере, в текущей версии компилятора версии 1.18).