MPI_Allreduce: очень странные ложные результаты

#c #debugging #c 11 #parallel-processing #mpi

#c #отладка #c 11 #параллельная обработка #mpi

Вопрос:

У меня огромный код с очень странной ошибкой MPI. Распараллеливание MPI тривиально, просто вычисляя среднее значение где-то в коде, которое может быть выделено как :

 // Declaration
std::vector<double> local;
std::vector<double> global;
unsigned int size;
/* ... huge computations here ... */
size = 10000; // approximately
local.resize(size);
global.resize(size);
local.shrink_to_fit();
global.shrink_to_fit();
/* ... some operations to fill in the local vector ... */
MPI_Allreduce(local.data(), global.data(), int(size), 
              MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
/* ... some final operations here ... */
 

Но если я построю значения global , в векторе будет около 10 ошибочных значений (более 10000 ), распределенных случайным образом (но всегда между значением ~1000th и ~2000th ), которые :

  • не одинаковы для всех задач MPI (~ 80% задач имеют одинаковые ошибочные значения, но оставшиеся ~ 20% имеют разные)
  • не совпадают для 2 разных запусков (не воспроизводятся)

Я протестировал программу с valgrind включенным 1 процессором (просто удалив MPI_Init , MPI_Allreduce и MPI_Finalize ), и никаких утечек памяти или каких-либо проблем нет (по крайней мере, это то, что valgrind мне говорит).

Нормальное количество задач для параллельной версии равно 1024 , и программа C 11 компилируется с. g 4.8.1

Valgrind это была моя последняя надежда, и я абсолютно не представляю, что происходит. Любая идея или предложение приветствуются!

Примечание: проблема может быть не в самом коде (конфигурации MPI, оборудовании и т. Д.), Поэтому Любая идея о том, что тестировать, приветствуется.

Комментарии:

1. Я немного смущен вашим вопросом. MPI_ALLREDUCE обычно просто возвращает одно значение (сумма локальных здесь). Вы пытаетесь сохранить его в появившемся массиве.

2. @WesleyBland — если вы предоставляете операции сокращения массив входных данных, она работает с каждым элементом независимо и генерирует массив сокращений одинакового размера. Мне потребовалось некоторое время, чтобы понять это изначально, потому что все примеры (в том числе в книгах) обычно просто используют скаляры.

3. Это действительно странно. Происходит что-то, чего не должно быть, поэтому мое первое предложение — самостоятельно изменить ряд вещей, которые не должны иметь значения, и посмотреть, сможем ли мы сузить его. Если вы удалите вычисления и просто заполните векторы фиктивными данными, вы все еще видите проблему? После этого, как насчет того, чтобы просто использовать одномерные массивы вместо векторов? На каком наименьшем количестве процессоров вы начинаете видеть это? Меняет ли что-нибудь настройка коллективных алгоритмов (в OpenMPI, —mca coll …)? Если вы можете сделать короткий репродуктор, я с удовольствием посмотрю…

4. Блин, ты прав @JonathanDursi. Тогда игнорируйте все комментарии от меня.

5. @JonathanDursi Проблема решена (смотрите Объяснение ниже, если вам интересно)

Ответ №1:

Просто для информации, после 2 дней исследований проблема решена. MPI_Allreduce не происходит сбой при несоответствии размера буфера, но вместо этого приводит к странному поведению. Я обнаружил это, заменив их на MPI_Reduce MPI_Bcast MPI_Reduce сбой из-за несоответствия размера буфера). Ошибка не может быть найдена, используя только информацию, указанную в вопросе: ключ был в строке :

 size = 10000; // approximately
 

и слово approximately , потому что в моем реальном коде этот размер зависит от множества вычислений и численного интегрирования дифференциальных уравнений. Но в этом коде используется гибридное распараллеливание MPI std::thread . И некоторые части интеграций зависят от std::thread , а последние цифры интеграций зависят от непрогнозируемого порядка операций. И финал size во многом зависит от этого. Итак, поскольку каждая задача MPI выполняла свои потоки в разных порядках, size она немного отличалась для двух или трех задач MPI … вызывая неопределенное поведение (но без сбоев) MPI_Allreduce .

Комментарии:

1. Вместо Valgrind вы должны были использовать MUST (здесь разрабатывается shameless plug — MUST).