OpenMP замедляет программу, а не ускоряет ее: ошибка в gcc? - PullRequest
1 голос
/ 09 мая 2011

Сначала я немного расскажу об этой проблеме, чтобы вы знали, что я пытаюсь сделать. Я помогал с разработкой определенного программного инструмента и обнаружил, что мы можем извлечь большую пользу из использования OpenMP для распараллеливания некоторых из самых больших циклов в этом программном обеспечении. Мы фактически распараллелили циклы успешно, и всего с двумя ядрами циклы выполнялись на 30% быстрее, что было хорошим улучшением. С другой стороны, мы заметили странное явление в функции, которая проходит через древовидную структуру с помощью рекурсивных вызовов. Программа фактически замедлилась при включенном OpenMP, а время выполнения этой функции удвоилось. Мы подумали, что, возможно, древовидная структура недостаточно сбалансирована для распараллеливания, и прокомментировали прагмы OpenMP в этой функции. Это, казалось, не влияло на время выполнения, хотя. В настоящее время мы используем GCC-компилятор 4.4.6 с включенным флагом -fopenmp для поддержки OpenMP. И вот текущая проблема:

Если мы не используем прагмы omp в коде, все работает нормально. Но если мы добавим только следующее к началу основной функции программы, время выполнения функции перемещения по дереву увеличится вдвое с 35 до 75 секунд:

//beginning of main function
...
#pragma omp parallel
{
#pragma omp single
{}
}
//main function continues
...

Кто-нибудь знает, почему это происходит? Я не понимаю, почему программа так сильно тормозит от использования прагм OpenMP. Если мы уберем все прагмы omp, время выполнения функции обхода дерева снова сократится до 35 секунд. Я предполагаю, что это какая-то ошибка компилятора, так как сейчас у меня нет других объяснений.

Ответы [ 4 ]

1 голос
/ 12 мая 2011

Спасибо всем. Мы смогли решить эту проблему сегодня, связавшись с TCMalloc, одним из предложенных ejd решений. Время выполнения сразу же сократилось, и мы смогли увеличить время выполнения примерно на 40% по сравнению с версией без многопоточности. Мы использовали 2 ядра. Кажется, что при использовании OpenMP в Unix с GCC, вы также должны выбрать замену стандартному решению для управления памятью. В противном случае программа может просто замедлиться.

1 голос
/ 10 мая 2011

Во-первых, OpenMP часто снижает производительность с первой попытки.Это может быть сложно использовать omp параллельно, если вы не понимаете это наизнанку.Я могу помочь, если вы можете рассказать мне немного больше о структуре программы, в частности, следующие вопросы, отмеченные ????

//beginning of main function
...
#pragma omp parallel
{

???? What goes here, is this a loop? if so, for loop, while loop?

#pragma omp single
   { 

     ???? What goes here, how long does it run? 
  }
}

//main function continues
....
???? Does performance of this code reduce or somewhere else?

Спасибо.

1 голос
/ 11 мая 2011

Я провел еще несколько тестов и создал небольшую тестовую программу, чтобы проверить, может ли проблема быть связана с работой памяти. Мне не удалось воспроизвести проблему пустой параллельной-единственной области, вызывающей замедление программы в моей небольшой тестовой программе, но я смог воспроизвести замедление, распараллелив некоторые вызовы malloc.

При запуске тестовой программы в Windows 7 64-bit с 2 ядрами ЦП заметное замедление не было вызвано использованием флага -fopenmp с компилятором gcc (g ++) и запуском скомпилированной программы по сравнению с запуском программы без OpenMP служба поддержки.

Выполнение того же в 64-битной версии Kubuntu 11.04 на том же компьютере, тем не менее, увеличило производительность в 4 раза по сравнению с версией без OpenMP. Эта проблема возникает только в Unix-системах, а не в Windows.

Источник моей тестовой программы ниже. Я также загрузил zipped-source для win и unix-версии, а также источник ассемблера для win и unix-версии как с OpenMP-поддержкой, так и без нее. Этот zip можно скачать здесь http://www.2shared.com/file/0thqReHk/omp_speed_test_2011_05_11.html

#include <stdio.h>
#include <windows.h>
#include <list>
#include <sys/time.h>
//#include <cstdlib>

using namespace std;

int main(int argc, char* argv[])
{
//  #pragma omp parallel
//  #pragma omp single
//  {}

  int start = GetTickCount();
  /*
  struct timeval begin, end;
  int usecs;
  gettimeofday(&begin, NULL);
  */
  list<void *> pointers;

  #pragma omp parallel for default(shared)
  for(int i=0; i< 10000; i++)
    //pointers.push_back(calloc(20000, sizeof(void *)));
    pointers.push_back(malloc(20000));

  for(list<void *>::iterator i = pointers.begin(); i!= pointers.end(); i++)
    free(*i);

  /*
  gettimeofday(&end, NULL);
  if (end.tv_usec < begin.tv_usec) {
    end.tv_usec += 1000000;
    begin.tv_sec += 1;
  }
  usecs = (end.tv_sec - begin.tv_sec) * 1000000;
  usecs += (end.tv_usec - begin.tv_usec);
  */

  printf("It took %d milliseconds to finish the memory operations", GetTickCount() - start);
  //printf("It took %d milliseconds to finish the memory operations", usecs/1000);

  return 0;
  }

Что остается без ответа сейчас, что я могу сделать, чтобы избежать таких проблем на Unix-платформе.

1 голос
/ 09 мая 2011

Не все, что может быть распараллелено, должно быть распараллелено. Если вы используете один, то только один поток выполняет его, а остальные должны ждать, пока регион не будет завершен. Они могут или вращаться, ждать или спать. Большинство реализаций начинаются с ожидания ожидания, надеясь, что отдельная область не займет слишком много времени, и ожидающие потоки могут увидеть завершение быстрее, чем в режиме ожидания. Спин-ожидание пожирает много процессорных циклов. Вы можете попытаться указать, что ожидание должно быть пассивным - но это только в OpenMP V3.0 и является лишь подсказкой для реализации (так что это может не иметь никакого эффекта). По сути, если у вас нет много работы в параллельной области, которая может компенсировать сингл, сингл значительно увеличит параллельные издержки и может сделать его слишком дорогим для параллелизации.

...