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

Я пытаюсь внутренне распараллелить функцию 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.

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


person Niclas    schedule 20.02.2020    source источник


Ответы (1)


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

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

Более того, некоторые среды выполнения 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 потоками, что хорошо.

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

person Jérôme Richard    schedule 25.02.2020