суббота, 18 июня 2011 г.

Архитектура вычислительных систем. OpenMP

Суперкомпьютер - это устройство, сводящее проблемы вычислений к проблемам ввода-вывода.
Ken Batcher, 2001

Постоянные читатели (вас нет? ну и ладно) заметили, что последнее время нередко публикуются статьи по теме. Не будем отходить от этой замечательной традиции. 

OpenMP

Open Multi-Processing -- удобный и достаточно простой в применении стандарт распараллеливания программ для языков C, C++, Fortran.

Команды OpenMP описываются в C/С++ с использованием префиксов #pragma omp, которые являются простыми директивами препроцессора, и обрабатываются перед непосредственной компиляцией. Препроцессор, поддерживающий OpenMP, обрабатывает их вставляя зависимые от ОС системные вызовы для создания потоков и работы с ними. OpenMP безумно удобен, например если компилятор не поддерживает OpenMP, то он просто проигнорирует все #pragma и код будет выполнен в одном потоке, в результате код получается в достаточной степени переносимым.

В Visual Studio поддержка OpenMP включается соответствующим тумблером в настройках C/C++ / Language, которая передает компилятору параметр /openmp.
В GCC включается опцией -fopenmp, библиотека -lgomp (libgomp).

Например, при использовании qmake (или QtCreator), в *.pro файле достаточно добавить пару строк:
QMAKE_LIBS += -lgomp
QMAKE_CXXFLAGS += -fopenmp

Пример работы с OpenMP

Вычисление интеграла sin(x) на интервале [0; pi] простейшим методом левых прямоугольников:


/*
 *         DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 *                    Version 2, December 2004
 *
 * Copyright (C) 2011 Brunql
 *
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as long
 * as the name is changed.
 *
 *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 *
 *  0. You just DO WHAT THE FUCK YOU WANT TO.
 */

#include <omp.h>
#include <stdio.h>
#include <cmath>
#include <string>

const double a = 0, b = M_PI; // integral limits
const int N = 50*1000*1000; // number of quantization steps in [a,b)
const double step = (b - a) / N; // quantization step

// OpenMP Time Evaluations
double g_startTime, g_endTime;

static inline void tStart()
{
    g_startTime = omp_get_wtime();
}

static inline void tElapsed(std::string str)
{
    g_endTime = omp_get_wtime();

    printf("Time elapsed (%s): %0.4lfs\n", str.c_str(), 
                g_endTime - g_startTime);
}

// Integral evaluation function
double f(double x)
{
    return sin(x);
}

double evalIntegralWithoutOpenMP()
{
    double result = 0;
    tStart();
    for (int i = 0; i < N; i++)
    {
        double x = a + i * step;
        result += f(x) * step;
    }
    tElapsed("Without OpenMP");
    return result;
}

double evalIntegralOpenMP()
{
    double result = 0;
    tStart();
#   pragma omp parallel for reduction(+:result) schedule(dynamic,(N/1000))
    for(int i = 0; i < N; i++)
    {
        double x = a + i * step;
        result += f(x) * step;
    }
    tElapsed("With OpenMP");
    return result;
}

int main(int argc, char **argv)
{
#   pragma omp parallel
    {
#       pragma omp critical
        {
            printf("Thread #%i of %i\n", 
                        omp_get_thread_num(), 
                        omp_get_num_threads());
        }
    }

    double resultWithoutOpenMP = evalIntegralWithoutOpenMP();
    double resultOpenMP = evalIntegralOpenMP();

    printf("Result without OpenMP: %lf\n", resultWithoutOpenMP);
    printf("Result with OpenMP: %lf\n", resultOpenMP);

    return 0;
}

При запуске программы получим:

Thread #1 of 2
Thread #0 of 2
Time elapsed (Without OpenMP): 2.3659s
Time elapsed (With OpenMP): 1.1919s
Result without OpenMP: 2.000000
Result with OpenMP: 2.000000

Судя по выхлопу программы можно понять, что OpenMP определил оптимальное количество потоков равное количеству физических ядер в моей машине, т.е. создал 2 потока. Так же по времени вычисления видно, что скорость параллельного вычисления как раз в два раза выше.

Код с использованием OpenMP отличается от последовательного кода всего лишь одной строчкой. Супер.

Бесполезные ссылки:

1) 32 подводных камня OpenMP при программировании на Си++ (http://www.dtf.ru/articles/read.php?id=55038&page=1)
2) Куча статей по распараллеливанию программ (http://www.viva64.com/ru/r/tag/parallel-programming/)
3) Да ну их, все равно никто по ним не ходит.

1 комментарий:

  1. Ну вроде, что-то где-то даже получается.

    Осталось только за завтрашний день MPI осилить и будет очень супер.

    ОтветитьУдалить

А у тебя есть клавиатура? Здесь можно попробовать воспользоваться ей по прямому назначению.