Параллельное программирование - OpenMP. Эксперименты: часть 1

Всегда думал, что параллельное программирование это очень сложно…



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

#pragma omp parallel shared(a, b, c) private(i)

  {

#pragma omp for

    for (i = 0; i < N; i++)

      c[i] = a[i] + b[i];

  }



Сразу бросается в глаза то, что это обычный код на C, дополненный директивами OpenMP. А значит, чтобы сделать свои старые программы много поточными, нужно грамотно расставить директивы и, возможно, переписать некоторые небольшие фрагменты. Надеюсь, что это действительно так. Хочется поскорее протестировать OpenMP и его возможности.
Работать буду в Ubuntu 12(вчера скачал, понравилось описание, решил попробовать. К слову могу добавить, что в Unity работать не смог, сразу вернулся к Gnome)
Процессор Intel i3, 4GB DDR3 памяти
Компиляцию буду проводить в g++. Для тестирования времени работы буду использовать функцию time.
Для начала написал несколько строк кода:

#include <iostream>

#include <cstdlib>

using namespace std;

#define N 1000

int main(int argc, char* argv[]){

    int a[N];

    long sum;

    int i,j,k;

    for (i = 0; i < N; i++){

	sum = 0;

        for (j=0; j < N; j++){

	    for (k=0; k < N; k++){

	        sum += (i+j);    

	    }

	}

	sum /= 42;

        a[i] = sum;

    }

    sum = 0;

    for (i=0; i < N; i++){

	sum += a[i];

    }

    cout << sum;

    return 0;

}


Программа представляет из себя 3 цикла, которые заполняют массив, затем выводится сумма элементов массива. Протестируем:

Выполнение программы без OpenMP



Как и планировалось, программа выполняется некоторое время, около 3.512 секунды. Ну что же, попробую уменьшить данное время, использую OpenMP.
Подключу библиотеку omp.h и несколько директив для распараллеливания, получился следующий код:

#include <iostream>

#include <cstdlib>

#include <omp.h>

using namespace std;

#define N 1000

int main(int argc, char* argv[]){

    int a[N];

    long sum;

    int i,j,k;

omp_set_dynamic(0);      // Не менять число потоков во время исполнения

  omp_set_num_threads(4); // число потоков

#pragma omp parallel shared(a) private(i,j,k,sum)

  {

#pragma omp for

    for (i = 0; i < N; i++){

	sum = 0;

        for (j = 0; j < N; j++){

	    for (k = 0; k < N; k++){

	        sum += (i+j);    

	    }

	}

	sum /= 42;

        a[i] = sum;

    }

  }

    sum = 0;

    for (i = 0; i < N; i++){

	sum += a[i];

    }

    cout << sum;

    return 0;

}



Компилирую с флагом -fopenmp. Что получилось:

Выполнение программы использующей OpenMP


ДА! Это правда, несколько дополнительных строк кода ускорили программу почти в 3 раза. Сумма элементов не изменилась и по прежнему составляет 23785713809, значит потоки работали правильно.
OpenMP отлично себя показал. Такого прироста мощности я по правде говоря и не ожидал, и это очень радует.
OpenMP стоит того, чтобы его изучали и использовали.

For more complete information about compiler optimizations, see our Optimization Notice.