Подтвердить что ты не робот

OpenMP: огромные различия в производительности между Visual С++ 2008 и 2010

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

При переносе на VC2010 я вижу очень странную производительность hog: под VC2010 мое приложение занимает почти 100% процессор, в то время как он занимает всего 10% под VC2008.

Если я сравниваю только код обработки, я не получаю никакой разницы между VC2010 и VC2008, разница возникает при использовании функций сбора.

Я сократил код, необходимый для воспроизведения проблемы, до простого цикла, который выполняет следующие действия:

  for (int i=0; i<1000; ++i)
  {
    GetImage(buffer);//wait for image
    Copy2Array(buffer, my_array);

    long long sum = 0;//do some simple OpenMP parallel loop
    #pragma omp parallel for reduction(+:sum)
    for (int j=0; j<size; ++j)
      sum += my_array[j];
  }

Этот цикл потребляет 5% процессора с 2008 годом и 70% с 2010 годом.

Я сделал некоторое профилирование, которое показывает, что в 2010 году большую часть времени тратится на OpenMP vcomp100.dll!_vcomp::PartialBarrierN::Block

Я также сделал несколько профилей concurrency:

В 2008 году обработка обрабатывается по 3 рабочим потокам, которые очень легко активны, поскольку время обработки намного хуже, чем время ожидания изображения.

Те же потоки появляются в 2010 году, но все они на 100% заняты функцией PartialBarrierN::Block. Поскольку у меня есть четыре ядра, они потребляют 75% работы, что примерно соответствует тому, что я вижу при загрузке процессора.

Таким образом, похоже, что существует конфликт между OpenMP и библиотекой приобретения Matrox (патентованной). Но это ошибка VS2010 или Matrox? Я могу что-нибудь сделать? Использование VС++ 2010 является обязательным для меня, поэтому я не могу просто придерживаться 2008 года.

Большое спасибо

ОБНОВЛЕНИЕ СОСТОЯНИЯ

Использование новой структуры concurrency, как было предложено DeadMG, приводит к 40% CPU. Профилирование показывает, что время тратится на обработку, поэтому он не показывает ошибку, которую я вижу с помощью OpenMP, но производительность в моем случае намного хуже, чем OpenMP.

ОБНОВЛЕНИЕ СОСТОЯНИЯ 2

Я установил оценочную версию новейшего Intel С++. Он показывает точно такие же проблемы с производительностью!!

Я перешел на форум MSDN

ОБНОВЛЕНИЕ СОСТОЯНИЯ 3

Протестировано на Windows 7 64 бит и XP 32 бит с точно такими же результатами (на той же машине)

4b9b3361

Ответ 1

В 2010 году OpenMP каждый рабочий поток выполняет отсрочку ожидания около 200 мс после завершения задачи. В моем случае ожидания ввода-вывода и повторяющейся задачи OpenMP она массово загружает CPU.

Решение состоит в том, чтобы изменить это поведение; Для этого Intel С++ имеет процедуру расширения, kmp_set_blocktime(). Однако Visual 2010 не имеет такой возможности.

В этот примечание Autodesk они говорят о проблеме для Intel С++. Этот компилятор впервые представил поведение, но позволяет его изменить (см. Выше). Visual 2010 переключился на него, но... без обходного пути, такого как Intel.

Итак, чтобы подвести итог, перешел на Intel С++ и с помощью kmp_set_blocktime(0) решил его.

Благодаря Джону Лилли из DataLever Corporation на другой поток MSDN

Проблема была отправлена ​​на MS Connect и получила обратную связь "не исправляет".

Ответ 2

Вы можете попробовать новый Concurrency Runtime, который поставляется с VS2010, только начиная с вашего тестового образца.

То есть

for (int i=0; i<1000; ++i)
  {
    GetImage(buffer);//wait for image
    Copy2Array(buffer, my_array);

    long long sum = 0;//do some simple OpenMP parallel loop
    #pragma omp parallel for reduction(+:sum)
    for (int j=0; j<size; ++j)
      sum += my_array[j];
  }

станет

for (int i=0; i<1000; ++i)
  {
    GetImage(buffer);//wait for image
    Copy2Array(buffer, my_array);

    Concurrency::combinable<int> combint;
    Concurrency::parallel_for(0, size / 1000, [&](int j) {
      for(int i = 0; i < 1000; i++)
          combint.local() += my_array[(j * 1000) + i];
    });
    combint.combine([](int a, int b) { return a + b; });
  }

Ответ 3

С OpenMP 3.0 спинвейт можно отключить через OMP_WAIT_POLICY:

_putenv_s( "OMP_WAIT_POLICY", "PASSIVE" );

Эффект в основном такой же, как с kmp_set_blocktime(0), но поскольку мы устанавливаем переменную окружения OMP_WAIT_POLICY во время выполнения, это будет влиять только на текущий процесс и дочерние процессы.

Конечно, OMP_WAIT_POLICY также может быть задан приложением запуска, например. Блендер обрабатывает его таким образом.

Исправление для VC2010 доступно здесь, более поздние версии, такие как VC2013, поддерживают его напрямую.

Ответ 4

Я тестировал другую плату сбора, и проблема идентична, поэтому виновником является VС++ 2010. Microsoft внесла изменения в реализацию OpenMP, которые закручивают программы, подобные моим, в виде потока на форумах MSDN.