Нет ускорения с бесполезными printf с использованием OpenMP

Я только что написал свою первую программу OpenMP, которая распараллеливает простой цикл for. Я запустил код на своем двухъядерном компьютере и увидел некоторое ускорение при переходе от 1 потока к 2 потокам. Однако я запустил тот же код на школьном Linux-сервере и не увидел ускорения. Попробовав разные вещи, я наконец понял, что удаление некоторых бесполезных операторов printf привело к значительному ускорению кода. Ниже представлена ​​основная часть кода, который я распараллелил:

#pragma omp parallel for private(i)
for(i = 2; i <= n; i++)
{
  printf("useless statement");
  prime[i-2] = is_prime(i);
}

Я предполагаю, что реализация printf имеет значительные накладные расходы, которые OpenMP должен дублировать с каждым потоком. Что вызывает эти накладные расходы и почему OpenMP не может их преодолеть?


person t2k32316    schedule 26.04.2010    source источник
comment
Не могу сопротивляться; слышали о Prime Sieve? en.wikipedia.org/wiki/Generating_primes#Prime_sieves   -  person Will    schedule 26.04.2010
comment
Какой компилятор вы используете? Какая версия?   -  person osgx    schedule 29.12.2010


Ответы (4)


Предполагаю, но, может быть, стандартный вывод защищен блокировкой?

В общем, printf — дорогостоящая операция, потому что она взаимодействует с другими ресурсами (такими как файлы, консоль и т.п.).

Мой эмпирический опыт показывает, что printf очень медленный на консоли Windows, сравнительно намного быстрее на консоли Linux, но все же быстрее, если перенаправить в файл или /dev/null.

Я обнаружил, что отладка printf может серьезно повлиять на производительность моих приложений, и я использую ее экономно.

Попробуйте запустить приложение, перенаправленное в файл или в /dev/null, чтобы увидеть, оказывает ли это какое-либо заметное влияние; это поможет сузить круг проблем.

Конечно, если printfs бесполезны, то почему они вообще в цикле?

person Will    schedule 26.04.2010
comment
Printf были там для целей отладки. - person t2k32316; 28.04.2010
comment
Да, glibc защищает стандартный вывод блокировками, так как буферизует его. - person osgx; 29.12.2010

Чтобы немного расширить ответ @Will...

Я не знаю, защищен ли stdout блокировкой, но я почти уверен, что запись в него сериализуется в какой-то точке программного стека. С включенными операторами printf OP, вероятно, синхронизирует выполнение большого количества последовательных записей в стандартный вывод, а не параллельное выполнение цикла.

Я предлагаю OP изменить оператор printf, чтобы включить i, посмотреть, что произойдет.

Что касается явного ускорения на двухъядерной машине — было ли оно статистически значимым?

person High Performance Mark    schedule 26.04.2010
comment
Кажущееся ускорение не было продемонстрировано статистически сильным образом. Я только что запустил его снова, и у меня было несколько прогонов, которые длились около 9 секунд с 1 потоком и 7 секунд с 2 потоками. Но когда я запускал его на серверах моей школы, никакого заметного ускорения не было (пока я не избавился от операторов printf). Я думаю, что мой главный вопрос сейчас просто из любопытства: что происходит за кулисами с printf, что может помешать OMP распараллелить цикл for? Я полагаю, это просто, как вы сказали: в конце концов это должно быть сериализовано... - person t2k32316; 28.04.2010
comment
+1; Уилл прав в том, что printf() — очень дорогая операция, но на самом деле она помогла бы ускориться, при прочих равных условиях; независимые дорогостоящие задачи хорошо распараллеливаются. Главный вопрос заключается в том, действительно ли они независимы. Как говорит Марк, они обращаются к общему ресурсу (stdout), поэтому на каком-то уровне, в конце концов, материал должен быть сериализован, а сериализация обязательно убивает параллельную производительность. - person Jonathan Dursi; 26.12.2010

У вас есть параллельный цикл for, но расписание не указано.

#pragma omp parallel for private(i)
for(i = 2; i <= n; i++)

В стандарте OpenMP 3.0 определено несколько типов планирования. Их можно изменить, установив для переменной среды OMP_SCHEDULE значение type[,chunk], где

  • тип – один из следующих: статический, динамический, управляемый или автоматический.
  • chunk – необязательное положительное целое число, указывающее размер фрагмента.

Другой способ изменить вид расписания — вызвать функцию openmp omp_set_schedule

Функция is_prime может быть довольно быстрой. /Я предлагаю/

  prime[i-2] = is_prime(i);

Таким образом, проблема может быть связана с неправильным режимом планирования, когда перед барьером от планирования выполняется небольшое число.

И внутри printf есть 2 части /я считаю glibc популярной реализацией Linux libc/

  1. Разберите строку формата и поместите все параметры в буфер
  2. Запись буфера в файловый дескриптор (в буфер FILE, так как stdout буферизуется glibc по умолчанию)

Первую часть printf можно выполнять параллельно, но вторая часть является критической секцией и заблокирована с помощью _IO_flockfile.

person osgx    schedule 29.12.2010

Каковы были ваши тайминги - было ли это намного медленнее с printf? В некоторых жестких циклах printf может занимать большую часть общего времени вычислений; например, если is_prime() работает довольно быстро, и поэтому производительность больше определяется количеством вызовов printf, чем количеством (параллелизованных) вызовов is_prime().

person Janik Zikovsky    schedule 26.12.2010