OpenMP с ODEINT в функции ODE - PullRequest
       25

OpenMP с ODEINT в функции ODE

0 голосов
/ 20 февраля 2020

Я пытаюсь внутренне распараллелить функцию ODE, интегрированную ODEINT.

Я сделал следующий небольшой пример

#include <iostream>
#include <chrono>
#include <Eigen/Dense>
#include <omp.h>
#include <boost/numeric/odeint.hpp>
#include <boost/numeric/odeint/external/openmp/openmp.hpp>
#include <boost/numeric/odeint/external/eigen/eigen.hpp>

using namespace boost::numeric::odeint;

class System {
private:
    Eigen::VectorXd _input_data;
public:
    System( Eigen::VectorXd &input_data ) { _input_data = input_data; };
    void operator() ( const Eigen::VectorXd &x , Eigen::VectorXd &dxdt , const double t ) {
        double _sum = 0.;
        #pragma omp parallel for reduction(+:_sum)
        for(int k = 0; k < _input_data.size(); ++k) {
            _sum += _input_data(k);
        };
        dxdt(0) = _sum;
    };
};

int main() {
    omp_set_num_threads(1);
    Eigen::VectorXd input_data = Eigen::VectorXd::Zero(100);
    System ode(input_data);
    runge_kutta_dopri5<Eigen::VectorXd> rk5_stepper;
    Eigen::VectorXd x = Eigen::VectorXd::Zero(1);
    auto start = std::chrono::high_resolution_clock::now();
    size_t steps = integrate_const(rk5_stepper, ode, x, 0., 1., 0.01);
    auto stop = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
    std::cout << "Execution time: " << duration.count() / 1000000. << " sec" << std::endl;
    return 0;
}

с файлом CMakeLists.txt

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_COMPILER /usr/local/bin/gcc-9)
set(CMAKE_CXX_COMPILER /usr/local/bin/g++-9)

project(ODEINT_OPENMP_TEST)

set(CMAKE_CXX_STANDARD 14)

include_directories("/usr/local/include")

find_package(OpenMP REQUIRED)

add_executable(ODEINT_OPENMP_TEST main.cpp)

target_link_libraries(ODEINT_OPENMP_TEST PRIVATE OpenMP::OpenMP_CXX)

когда я пытаюсь использовать больше потоков через omp_set_num_threads(N), программа постоянно замедляется по сравнению с использованием только одного потока omp_set_num_threads(1). Программа становится примерно в 3 раза медленнее (на моей машине), выбирая N = 2.

Интуитивно понятно, функция правой части должна работать быстрее параллельно? Я что-то не так делаю?

1 Ответ

0 голосов
/ 26 февраля 2020

Прежде всего, ваш l oop слишком мал, чтобы извлечь выгоду из использования нескольких потоков в этом примере c (примерно 3,5 мс на моей машине последовательно, 1,8 мс при использовании 6 потоков на 6 ядрах).

Кроме того, ваш тест слишком короткий, и вы, вероятно, измеряете непредвиденные эффекты (кеширование, сбои страниц, проблемы с масштабированием частоты процессора и т. Д. c.). Подумайте об этом в al oop, чтобы смягчить большинство эффектов (если это имеет смысл в реальных условиях).

Более того, некоторые среды выполнения OpenMP создают потоки при выполнении параллельной секции. Эта операция довольно медленная. Поскольку директива #pragma omp parallel включена в синхронизацию, вы также можете измерить создание потока.

Вот результаты на моем 6-ядерном компьютере размером в 1000 раз больше:

1 thread:  2.330 sec
2 threads: 1.212 sec
3 threads: 0.813 sec
4 threads: 0.649 sec
5 threads: 0.532 sec
6 threads: 0.459 sec

Ускорение составляет 5,1 с 6 потоками, что хорошо.

Обратите внимание, что масштабирование может ухудшиться, поскольку ваш l oop кажется ограниченным памятью (пропускная способность памяти не масштабируется с количеством используемые ядра).

...