2013-06-19 5 views
0

Я пытаюсь научиться использовать openmp для многопоточности. Вот мой код:Openmp не может создавать потоки автоматически

#include <iostream> 
#include <math.h> 
#include <omp.h> 
//#include <time.h> 
//#include <cstdlib> 

using namespace std; 

bool isprime(long long num); 

int main() 
{ 
     cout << "There are " << omp_get_num_procs() << " cores." << endl; 
     cout << 2 << endl; 
     //clock_t start = clock(); 
     //clock_t current = start; 
     #pragma omp parallel num_threads(6) 
     { 
     #pragma omp for schedule(dynamic, 1000) 
     for(long long i = 3LL; i <= 1000000000000; i = i + 2LL) 
     { 
       /*if((current - start)/CLOCKS_PER_SEC > 60) 
       { 
         exit(0); 
       }*/ 
       if(isprime(i)) 
       { 
         cout << i << " Thread: " << omp_get_thread_num() << endl; 
       } 
     } 
     } 
} 

bool isprime(long long num) 
{ 
     if(num == 1) 
     { 
       return 0; 
     } 
     for(long long i = 2LL; i <= sqrt(num); i++) 
     { 
       if (num % i == 0) 
       { 
         return 0; 
       } 
     } 
     return 1; 
} 

Проблема заключается в том, что я хочу OpenMP автоматически создавать несколько потоков на основе того, сколько ядер доступны. Если я выберу num_threads (6), то он просто использует 1 поток, но omp_get_num_procs() корректно выводит 64.

Как я могу заставить это работать?

ответ

0

Я не уверен, правильно ли я понял ваш вопрос, но кажется, что вы почти там. Вы имеете в виду что-то вроде:

#include <omp.h> 
#include <iostream> 

int main(){ 

    const int num_procs = omp_get_num_procs(); 
    std::cout<<num_procs; 

#pragma omp parallel for num_threads(num_procs) default(none) 
    for(int i=0; i<(int)1E20; ++i){ 
    } 

    return 0; 

} 
0

Если я не ошибаюсь, а плохо, OpenMP обычно упорядочивает ввода/вывода (по крайней мере, в одном потоке), так что, вероятно, по крайней мере, часть, где ваша проблема возникновения. Удаление, что из петли, и втирание немного остальных (не имеет особого смысла работать на распараллеливание, пока вы не достаточно эффективный серийный код), я в конечном итоге с чем-то вроде этого:

#include <iostream> 
#include <math.h> 
#include <omp.h> 

using namespace std; 

bool isprime(long long num); 

int main() 
{ 
    unsigned long long total = 0; 

    cout << "There are " << omp_get_num_procs() << " cores.\n"; 

    #pragma omp parallel for reduction(+:total) 
    for(long long i = 3LL; i < 100000000; i += 2LL) 
     if(isprime(i)) 
      total += i; 

    cout << "Total: " << total << "\n"; 
} 

bool isprime(long long num) { 
    if (num == 2) 
     return 1; 
    if(num == 1 || num % 2 == 0) 
     return 0; 
    unsigned long long limit = sqrt(num); 

    for(long long i = 3LL; i <= limit; i+=2) 
     if (num % i == 0) 
      return 0; 
    return 1; 
} 

Это не печатает из числа потоков, но времени это я получаю что-то вроде этого:

Real 78.0686 
User 489.781 
Sys  0.125 

Обратите внимание на то, что время «Пользователь» более чем в 6 раз больше, «Реал» время, указывая, что нагрузка распределяется по всем ядрам 8, доступным на этой машине с КПД около 80%. С немного большей работой вы могли бы улучшить это дальше, но даже с этой простой версией мы видим значительно более одного используемого ядра (на вашей 64-ядерной машине мы должны увидеть как минимум улучшение 50: 1 над однопоточным кодом и, вероятно, немного лучше этого).

0

Единственная проблема, которую я вижу с вашим кодом, заключается в том, что когда вы делаете вывод, вам нужно поместить его в раздел critcal, иначе несколько потоков могут записываться в одну и ту же строку одновременно.
См. Исправления моего кода.

Что касается одного потока, я думаю, что вы могли бы видеть из-за использования dynamic. Поток, выполняющийся по небольшим числам, намного быстрее, чем один, работающий на больших количествах. Когда поток с небольшими номерами заканчивается и получает другой список небольших чисел для запуска, он снова заканчивается быстрее, пока поток с большими числами все еще работает. Это не значит, что вы используете только один поток. В моем выпуске я вижу длинные потоки одного и того же потока, набирая простые числа, но в конце концов другие сообщают также. Вы также установили размер патрона на 1000, поэтому, если вы, например, только набрали более 1000 номеров, в цикле будет использоваться только один поток.

Похоже, вы пытаетесь найти список простых чисел или сумму числа простых чисел. Для этого вы используете пробное подразделение. Это намного менее эффективно, чем использование «Сита Эратосфена».

Приведен пример сита Эратосфена, который обнаруживает простые числа в первых миллиардах чисел менее чем за одну секунду в моей 4-х ясной системе с помощью OpenMP. http://create.stephan-brumme.com/eratosthenes/

Я немного очистил ваш код, но не пытался ничего оптимизировать, так как алгоритм неэффективен в любом случае.

int main() { 
    //long long int n = 1000000000000; 
    long long int n = 1000000; 
    cout << "There are " << omp_get_num_procs() << " cores." << endl; 
    double dtime = omp_get_wtime(); 
    #pragma omp parallel 
    { 
     #pragma omp for schedule(dynamic) 
     for(long long i = 3LL; i <= n; i = i + 2LL) { 
      if(isprime(i)) { 
       #pragma omp critical 
       { 
        cout << i << "\tThread: " << omp_get_thread_num() << endl; 
       } 
      } 
     } 
    } 
    dtime = omp_get_wtime() - dtime; 
    cout << "time " << dtime << endl; 
} 
1

Вы пренебрегали упоминанием того, какой компилятор и реализация OpenMP вы используете. Я собираюсь угадать, что вы используете один из них, например PGI, который автоматически не принимает число потоков для создания в параллельном регионе по умолчанию, если этого не попросят сделать это. Поскольку вы не указали компилятор, я не могу быть уверенным, что эти параметры вам действительно помогут, но для компиляторов PGI необходимая опция - -mp=allcores при компиляции и связывании исполняемого файла. При добавлении он заставит систему создавать один поток на ядро ​​для параллельных областей, которые не указывают количество потоков или имеют соответствующую переменную среды.

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

Смежные вопросы