воскресенье, 19 июня 2011 г.

Великая битва OpenMP vs MPI (или Холодное vs Красное)

Считаем интеграл методом левых прямоугольников. Просто и безвкусно.


Вычисления будем проводить на одной машине, потому что у меня их одна.

Главное действующее лицо: Intel(R) Pentium(R) Dual CPU E2200 @ 2.20GHz
OS: Ubuntu 10.04 2.6.32-32-generic x86_64
MPICH2 Version: 1.2.1p1
GCC: 4.4.3-4ubuntu5

OpenMP вариант численного интегрирования:

/*
 *         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>

// Integral limits [a;b)
const double a = 0;
const double b = M_PI;
// Number of quantization steps in [a,b)
const int N = (500*1000*1000);

// Quantization step
const double step = (b - a) / N;

// Integral evaluation function
static inline double f(double x)
{
    return cos(x)*sin(x)*x*x + 2;
}

double evalIntegral()
{
    double result = 0;
#   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;
    }
    return result;
}

int main(int argc, char **argv)
{
    printf("=> OpenMP\n");
    printf("Number of quantization steps %iM\n", N / 1000000);
    fflush(stdout);
#   pragma omp parallel
    {
#       pragma omp critical
        {
            printf("Start working thread #%i of %i\n",
                   omp_get_thread_num(), omp_get_num_threads());
        }
    }
    // Looooooong call
    double result = evalIntegral();

    printf("Result with OpenMP: %lf\n", result);
    return 0;
}

MPI вариант численного интегрирования:

/*
 *         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 <mpi.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

// Integral limits [a;b)
const double a = 0;
const double b = M_PI;
// Number of quantization steps in [a,b)
const int N = (500*1000*1000);

// Quantization step
const double step = (b - a) / N;

// Integral evaluation function
static inline double f(double x)
{
    return cos(x)*sin(x)*x*x + 2;
}

double evalIntegral(int start, int end)
{
    double result = 0;
    for (int i = start; i < end; i++)
    {
        double x = a + i * step;
        result += f(x) * step;
    }
    return result;
}

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

    int rank;
    int sizeOfWorld;
    MPI_Status mpiStatus;

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &sizeOfWorld);

    static const int MasterRank = 0;

    if (rank == MasterRank)
    {
        // Master
        printf("=> MPI\n");
        printf("Number of quantization steps %iM\n", N / 1000000);
        fflush(stdout);
    }

    printf("Start working\n");
    fflush(stdout);

    int numOfWorkers = sizeOfWorld;
    int start = N / numOfWorkers * rank;
    int end = start + N / numOfWorkers;

    // Looooooong call
    double result = evalIntegral(start, end);

    printf("Job is done! [%i; %i) Result = %lf\n", start, end, result);
    fflush(stdout);

    if (rank != MasterRank)
    {
        // Workers send results to master
        MPI_Send(&result, 1, MPI_DOUBLE, MasterRank, 0, MPI_COMM_WORLD);
    }

    // Master collect results
    if (rank == MasterRank)
    {
        double resultFromWorker = 0;
        for (int id = 1; id < sizeOfWorld; id++)
        {
            MPI_Recv(&resultFromWorker, 1, MPI_DOUBLE, id,
                     MPI_ANY_TAG, MPI_COMM_WORLD, &mpiStatus);
            result += resultFromWorker;
        }
        printf("Result with MPI: %lf\n", result);
    }

    MPI_Finalize();
    return 0;
}

Барабанная дробь!


$ time ./openmp
=> OpenMP 
Number of quantization steps 500M 
Start working thread #0 of 2 
Start working thread #1 of 2 
Result with OpenMP: 3.815784
 
real 0m17.042s 
user 0m33.700s 
sys 0m0.020s

$ time mpirun -l -n 2 ./mpiapp 
0: => MPI 
1: Start working 
0: Number of quantization steps 500M 
0: Start working 
1: Job is done! [250000000; 500000000) Result = 0.307341 
0: Job is done! [0; 250000000) Result = 3.508443 
0: Result with MPI: 3.815784 

real 0m17.342s 
user 0m0.090s 
sys 0m0.030s


Представленные результаты являются моментальными, то есть никаких усреднений не проводилось. Делать какие-то выводы считаю бессмысленным в виду того, что сравнивать холодное с красным как минимум не логично. Использование MPI на одной машине с одним процессором и парой ядер практически бессмысленно. Единственная вещь, которая оправдывает затраченные усилия, это полученный опыт использования данных инструментов. Что собственно и являлось главной целью данного теста.

Пока писал примеры для сравнения производительности, у меня закралась шальная мысль, а что если их объединить? Смысла судя по тестам скорости не много, но ведь скорость работы это одно, а скорость разработки немного ортогональная вещь, которая очень сильно зависит от используемого инструмента. На OpenMP очень удобно постепенно распараллеливать уже написанный код, и ничуть не менее удобно писать новый. В тоже время MPI обеспечивает достаточно мощный интерфейс для обмена результатами вычислений между различными вычислительными блоками. Конечно на вскидку сложно придумать сферу применения данного подхода.


В общем будем считать, что победила дружба.

Комментариев нет:

Отправить комментарий

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