Управление поведением FPU в программе OpenMP? - PullRequest
4 голосов
/ 08 февраля 2010

У меня есть большая программа на C ++, которая изменяет управляющее слово FPU (используя _controlfp()). Он демаскирует некоторые исключения FPU и устанавливает SEHTranslator для получения типизированных исключений C ++. Я использую VC ++ 9.0.

Я бы хотел использовать OpenMP (v.2.0) для распараллеливания некоторых наших вычислительных циклов. Я уже успешно применил его к одному, но численные результаты немного отличаются (хотя я понимаю, что это также может быть связано с выполнением расчетов в другом порядке). Я предполагаю, что это потому, что состояние FPU зависит от потока. Есть ли способ заставить потоки OpenMP наследовать это состояние от основного потока? Или есть какой-то способ указать с помощью OpenMP, что новые потоки выполняют определенную функцию, которая устанавливает правильное состояние? Какой идиоматический способ справиться с этой ситуацией?

Ответы [ 3 ]

1 голос
/ 09 февраля 2010
  1. Как вы уже указали, операции double / float не являются ассоциативными / коммутативными / распределяются как действительные числа в математике. В частности, умножение / деление огромного числа / очень маленького числа может привести к заметным ошибкам точности при изменении порядка вычислений.

  2. Состояние FPU должно зависеть от потока, так как состояние представляется в виде регистра, а состояние регистра (= контекст) зависит от потока.

  3. Неоднозначно говорить, что порожденные потоки наследуют состояние главного потока , поскольку состояние неясно в этом контексте. Если вы имеете в виду статус регистрации, то это не так.

  4. Мое предложение состоит в том, почему бы вам просто не установить управляющее слово FPU для каждой нити? Например, перед порождением потока OpenMP, т. Е. Перед параллельным-для, сохраните текущее управляющее слово FPU в глобальной переменной с помощью _status87 . Затем поместите операторы, которые читают глобальную переменную и устанавливают новое значение параллельно для итерации. Поскольку он доступен только для чтения глобальной переменной, вам не нужно беспокоиться о гонке данных.

unsigned int saved_status = _status87();
#pragma omp parallel for (...)
for (int i = 0; i < N; ++i)
{
  _controlfp(saved_status, ...);

  ..
}
0 голосов
/ 09 февраля 2010

Я пришел к выводу, что у меня нет проблем. Различия в результатах обусловлены порядком вычислений, а не состоянием FPU в разных потоках (мы не меняем точность или режимы округления). Что касается маскирования исключений FPU, различающегося в рабочих потоках, то это не проблема, потому что, если рабочий поток выполняет операцию, которая приведет к исключению, этот результат (теперь NaN или Inf и т. Д.) В конечном итоге будет «учитывать» основной поток и исключение будут выброшены.

Кроме того, исключение должно быть перехвачено в том же потоке OpenMP, который его выдал. Это означает, что я хочу, чтобы только главный поток мог генерировать исключения в любом случае.

0 голосов
/ 09 февраля 2010

Вероятность заключается в том, что это связано с упорядочением операций с плавающей запятой. Мы все полагаемся на то, что наши операции являются ассоциативными и коммутативными, но прискорбная истина заключается в том, что операции с плавающей запятой не являются коммутативными, поэтому при их распараллеливании результаты могут отличаться, поскольку порядок рандомизируется.

Попробуйте запустить ваши циклы задом наперед и посмотреть, отличается ли результат.

Если у вас есть потребности для каждого потока, OMP предоставляет гарантии об итерациях циклов, выпадающих на одни и те же потоки, т. Е. Если цикл равен от 1 до N на четырехъядерном ядре, итерации от 1 до N / 4 будут выполняться на одном и том же нить.

-Rick

...