После множества тупиков и сбоев мне удалось реализовать алгоритм в Chapel.Это можно найти здесь: https://github.com/novoselrok/parallel-algorithms/tree/75312981c4514b964d5efc59a45e5eb1b8bc41a6/nbody-bh/dm-chapel
Мне не удалось использовать большую часть необычного параллельного оборудования, которое предоставляет Часовня.Я полагался только на блочные распределенные массивы с sync
элементами.Я также реплицировал модель SPMD.
В main.chpl
Я настроил все необходимые массивы, которые будут использоваться для передачи данных.Каждый массив имеет соответствующий массив sync
, используемый для синхронизации.Затем каждый работник начинается со своей доли тел и ранее упомянутых массивов.Worker.chpl
содержит основную часть алгоритма.
Я заменил функциональность MPI_Comm_split
на пользовательскую функцию determineGroupPartners
, где я делаю то же самое вручную.Что касается MPI_Allreduce
, я нашел хороший небольшой шаблон, который мог бы использовать везде:
var localeSpace = {0..#numLocales};
var localeDomain = localeSpace dmapped Block(boundingBox=localeSpace);
var arr: [localeDomain] SomeType;
var arr$: [localeDomain] sync int; // stores the ranks of inteded receivers
var rank = here.id;
for i in 0..#numLocales-1 {
var intendedReceiver = (rank + i + 1) % numLocales;
var partner = ((rank - (i + 1)) + numLocales) % numLocales;
// Wait until the previous value is read
if (arr$[rank].isFull) {
arr$[rank];
}
// Store my value
arr[rank] = valueIWantToSend;
arr$[rank] = intendedReceiver;
// Am I the intended receiver?
while (arr$[partner].readFF() != rank) {}
// Read partner value
var partnerValue = arr[partner];
// Do something with partner value
arr$[partner]; // empty
// Reset write, which blocks until arr$[rank] is empty
arr$[rank] = -1;
}
Это несколько сложный способ реализации каналов FIFO (см. Julia RemoteChannel, где я получил вдохновение для этого шаблона).«).Обзор:
Сначала каждая локаль вычисляет своего предполагаемого получателя и своего партнера (где локаль будет читать значение)
Локаль проверяет, еслипредыдущее значение было прочитано
Locales сохраняет новое значение и «блокирует» значение, устанавливая arr$[rank]
с помощью получателя
Localeждет, пока его партнер установит значение и установит соответствующий предполагаемый получатель
Как только локаль станет предполагаемым получателем, она считывает значение партнера и выполняет с ним некоторую операцию
Затем локаль очищает / разблокирует значение, читая arr$[partner]
Наконец, оно сбрасывает arr$[rank]
, записывая -1
.Таким образом, мы также гарантируем, что значение, установленное локалью, было прочитано предполагаемым получателем
Я понимаю, что это может быть слишком сложным решением для этой проблемы.Вероятно, существует лучший алгоритм, который бы соответствовал глобальному представлению Chapels о параллельных вычислениях.Алгоритм, который я реализовал, пригоден для модели вычислений SPMD.
При этом я думаю, что Chapel по-прежнему хорошо справляется с работой. Здесь - это показатели производительности для Julia и C / MPI.По мере роста числа процессов производительность значительно возрастает.У меня не было возможности запустить тест на кластере с> 4 узлами, но я думаю, что Chapel получит респектабельные тесты.