具有OpenMP关键指令的Rcpp明显慢于编译的C ++代码

时间:2018-02-23 13:38:41

标签: c++ r openmp rcpp

正如标题所说,与Rcpp一起使用R包中的#pragma omp critical指令与编译&相比,显着减慢了执行速度。因为没有使用所有CPU电源,所以运行R包中使用的C ++代码。

考虑一个简单的C ++程序(使用cmake):

test.h as:

#ifndef RCPP_TEST_TEST_H
#define RCPP_TEST_TEST_H

#include <limits>
#include <cstdio>
#include <chrono>
#include <iostream>
#include <omp.h>

namespace rcpptest {
    class Test {
    public:
        static unsigned int test();
    };
}

#endif //RCPP_TEST_TEST_H

在test.cpp中实现test.h:

#include "test.h"

namespace rcpptest {
    unsigned int Test::test() {
        omp_set_num_threads(8);
        unsigned int x = 0;

        std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();

#pragma omp parallel for
        for (unsigned int i = 0; i < 100000000; ++i) {

#pragma omp critical
            ++x;
        }
        std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
        std::cout << "finished (ms): " << std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() <<std::endl;

        return x;
    }
}

并且主要为:

#include "src/test.h"

int main() {
    unsigned int x = rcpptest::Test::test();
    return 0;
}

如果我在IDE(CLion)中构建并运行此程序,一切都可以正常工作。

然后我使用Rcpp创建了一个R包:

library(Rcpp)
Rcpp.package.skeleton('rcppTestLib')

并使用包的所有SAME C ++源代码+&#34; Rcpp&#34;文件导出我的测试函数可以从R(rcppTestLib.cpp)导出:

#include <Rcpp.h>
#include "test.h"

// [[Rcpp::export]]
void rcppTest() {
    rcpptest::Test::test();
}

如果我然后使用包

从R运行测试
library(rcppTestLib)
rcppTest()

执行速度要慢得多。

我使用编译的c ++和Rcpp包进行了一些测试,结果是:

   program   | execution time
-----------------------------
compiled c++ | ~7 200ms
Rcpp package | ~551 000 ms

不同之处在于使用Rcpp软件包,产生了8个线程,但每个线程只使用了约1%的CPU,而使用编译的C ++时,8个线程组合使用了所有的CPU功率。

我尝试将#pragma omp critical#pragma omp atomic切换为结果:

   program   | execution time
-----------------------------
compiled c++ | ~2 900ms
Rcpp package | ~3 300 ms

使用#pragma omp atomic Rcpp程序包生成8个线程并使用所有CPU权限。但是,执行时间仍然存在差异,但并不显着。

所以我的问题是:为什么#pragma omp critical R / Rcpp包使用#pragma omp atomic时不会使用所有CPU功率,它甚至可以在CLion中构建和运行相同的代码使用所有CPU功能两种情况?

我在这里缺少什么?

2 个答案:

答案 0 :(得分:3)

这里有两种可能的选择:

  1. 在包形式中,OpenMP(unix)或src/Makevars(窗口)中尚未设置src/Makevars.win标记选项
  2. num_threads(x)推出critical
  3. 例如,放在src/Makevarssrc/Makevars.win文件中:

    PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) $(SHLIB_OPENMP_CFLAGS)
    PKG_CFLAGS = $(SHLIB_OPENMP_CFLAGS)
    PKG_CXXFLAGS = $(SHLIB_OPENMP_CXXFLAGS)
    

    有关详细信息,请参阅:https://cran.r-project.org/doc/manuals/r-release/R-exts.html#OpenMP-support

    关于失踪的num_threads(x) ......我已经能够加快解决问题...

    更改:

    #pragma omp parallel for
    

    #pragma omp parallel for num_threads(4)
    

    收率:

    <强>之前

    finished (ms): 30822
    [1] 1e+08
    

    VS。

    <强>后

    finished (ms): 17979
    [1] 1e+08
    

    或大约1.7加速。我的想法在cmake的某个地方正在设置一个全局线程选项。

    omp_set_num_threads(x)
    

    set OMP_NUM_THREADS=x
    

    https://gcc.gnu.org/onlinedocs/libgomp/omp_005fset_005fnum_005fthreads.html

    https://software.intel.com/en-us/mkl-linux-developer-guide-setting-the-number-of-threads-using-an-openmp-environment-variable

答案 1 :(得分:2)

@coatless再次完全正确。我们创建的默认 send_file没有OpenMP。您可以在当前足够的编译器上看到这一点:

src/Makevars*

根据需要添加ccache g++ -I/usr/share/R/include -DNDEBUG -I"/usr/local/lib/R/site-library/Rcpp/include" -fpic -g -O3 -Wall -pipe -march=native -c test.cpp -o test.o test.cpp:10:0: warning: ignoring #pragma omp parallel [-Wunknown-pragmas] #pragma omp parallel for test.cpp:13:0: warning: ignoring #pragma omp critical [-Wunknown-pragmas] #pragma omp critical ,一切都很好。 src/Makevars表示我选择挂起的CPU数量。

但是你的例子仍然很糟糕,因为循环太少了。开销变得占主导地位。我在这里有多个核心,但没有理由它应该运行得更快htop应该比OMP_NUM_THREADS=2OMP_NUM_THREADS=3运行得更快 - 除了事实 我们似乎只有开销。