Я реализую программу MPI на C, которая выполняет SOR (последовательное перенапряжение) в сетке. При тестировании я обнаружил нечто совершенно неожиданное, а именно, что адрес оператора &
выглядит очень медленным. Я не могу показать весь код здесь, и он также слишком длинный, но соответствующие части следующие.
double maxdiff, diff;
do {
maxdiff = 0.0;
/* inner loops updating maxdiff a lot */
/* diff is used as a receive buffer here */
MPI_Allreduce(&maxdiff, &diff, 1, MPI_DOUBLE, MPI_MAX, my_comm);
maxdiff = diff;
} while(maxdiff > stopdiff);
Здесь stopdiff - это магическая ценность. Медленное поведение появляется в операции MPI_Allreduce()
. Странно то, что эта операция даже очень медленная при работе только на одном узле, хотя в этом случае связь не требуется. Когда я закомментирую операцию, время выполнения определенной проблемы на одном узле уменьшается с 290 секунд до всего 225 секунд. Кроме того, когда я заменяю операцию вызовом MPI_Allreduce()
, используя другие фиктивные переменные, я также получаю 225 секунд. Таким образом, похоже, что он специально получает адреса maxdiff
и diff
, что вызывает замедление.
Я обновил программу, сделав две дополнительные double
переменные, используемые в качестве временных буферов отправки / получения, следующим образом.
send_buf = maxdiff;
MPI_Allreduce(&send_buf, &recv_buf, 1, MPI_DOUBLE, MPI_MAX, my_comm);
maxdiff = recv_buf;
Это также заставило программу работать за 225 секунд вместо 290. Мой вопрос, очевидно, как это может быть?
У меня есть подозрение: программа компилируется с использованием gcc с уровнем оптимизации O3, поэтому я подозреваю, что компилятор выполняет некоторую оптимизацию, которая делает опорную операцию очень медленной. Например, возможно, переменные хранятся в регистрах процессора, потому что они так часто используются в цикле, и из-за этого они должны быть сброшены обратно в память всякий раз, когда запрашивается их адрес. Тем не менее, я не могу выяснить через поиск в Google, какая оптимизация может вызвать эту проблему, и я хотел бы быть уверен в этой проблеме. У кого-нибудь есть идея, что может быть причиной этого?
Заранее спасибо!
Я должен добавить здесь другую важную информацию. Конкретная проблема при запуске заполняет память довольно плохо. Он использует 3 ГБ памяти, а узлы имеют 4 ГБ ОЗУ. Я также наблюдаю, что замедление ухудшается при больших размерах проблем, так как ОЗУ заполняется, поэтому величина нагрузки на ОЗУ, по-видимому, является фактором проблемы. Кроме того, как ни странно, когда я добавляю MPI_Allreduce()
только один раз после цикла, а не внутри цикла, замедление все еще наблюдается в неоптимизированной версии программы, и это все равно так же плохо. Программа не запускается так быстрее.
Как указано ниже, это часть вывода сборки gcc. К сожалению, у меня недостаточно опыта сборки, чтобы решить эту проблему. Это версия с добавленными буферами отправки и получения, поэтому версия работает за 225 секунд, а не за 290.
incl %r13d
cmpl $1, %r13d
jle .L394
movl 136(%rsp), %r9d
fldl 88(%rsp)
leaq 112(%rsp), %rsi
leaq 104(%rsp), %rdi
movl $100, %r8d
movl $11, %ecx
movl $1, %edx
fstpl 104(%rsp)
call MPI_Allreduce
fldl 112(%rsp)
incl 84(%rsp)
fstpl 40(%rsp)
movlpd 40(%rsp), %xmm3
ucomisd 96(%rsp), %xmm3
jbe .L415
movl 140(%rsp), %ebx
xorl %ebp, %ebp
jmp .L327
Вот то, что я считаю соответствующей частью в программе без дополнительных буферов отправки и получения, поэтому версия, которая запускается за 290 секунд.
incl %r13d
cmpl $1, %r13d
jle .L314
movl 120(%rsp), %r9d
leaq 96(%rsp), %rsi
leaq 88(%rsp), %rdi
movl $100, %r8d
movl $11, %ecx
movl $1, %edx
call MPI_Allreduce
movlpd 96(%rsp), %xmm3
incl 76(%rsp)
ucomisd 80(%rsp), %xmm3
movsd %xmm3, 88(%rsp)
jbe .L381
movl 124(%rsp), %ebx
jmp .L204