用于矩阵向量乘积的 Rcpp Parallel 或 openmp

2024-01-03

我正在尝试对共轭梯度的朴素并行版本进行编程,所以我从简单的维基百科算法开始,我想改变dot-products and MatrixVector产品通过其适当的并行版本,Rcppparallel 文档具有以下代码dot-product使用并行化简;我想我会在我的代码中使用该版本,但我正在尝试制作MatrixVector乘法,但与 R 基础相比,我还没有取得好的结果(没有并行)

并行矩阵乘法的一些版本:使用 OpenMP、Rcppparallel、串行版本、使用 Armadillo 的串行版本以及基准测试

// [[Rcpp::depends(RcppParallel)]]
#include <Rcpp.h>
#include <RcppParallel.h>
#include <numeric>
// #include <cstddef>
// #include <cstdio>
#include <iostream>
using namespace RcppParallel;
using namespace Rcpp;

struct InnerProduct : public Worker
{   
   // source vectors
   const RVector<double> x;
   const RVector<double> y;

   // product that I have accumulated
   double product;

   // constructors
   InnerProduct(const NumericVector x, const NumericVector y) 
      : x(x), y(y), product(0) {}
   InnerProduct(const InnerProduct& innerProduct, Split) 
      : x(innerProduct.x), y(innerProduct.y), product(0) {}

   // process just the elements of the range I've been asked to
   void operator()(std::size_t begin, std::size_t end) {
      product += std::inner_product(x.begin() + begin, 
                                    x.begin() + end, 
                                    y.begin() + begin, 
                                    0.0);
   }

   // join my value with that of another InnerProduct
   void join(const InnerProduct& rhs) { 
     product += rhs.product; 
   }
};

struct MatrixMultiplication : public Worker
{
   // source matrix
   const RMatrix<double> A;

    //source vector
   const RVector<double> x;

   // destination matrix
   RMatrix<double> out;

   // initialize with source and destination
   MatrixMultiplication(const NumericMatrix A, const NumericVector x, NumericMatrix out) 
     : A(A), x(x), out(out) {}

   // take the square root of the range of elements requested
   void operator()(std::size_t begin, std::size_t end) {
      for (std::size_t i = begin; i < end; i++) {
            // rows we will operate on
            //RMatrix<double>::Row rowi = A.row(i);
            RMatrix<double>::Row rowi = A.row(i);

            //double res = std::inner_product(rowi.begin(), rowi.end(), x.begin(), 0.0);
            //Rcout << "res" << res << std::endl;
            out(i,1) = std::inner_product(rowi.begin(), rowi.end(), x.begin(), 0.0);
            //Rcout << "res" << out(i,1) << std::endl;
      }
    }  
};

// [[Rcpp::export]]
double parallelInnerProduct(NumericVector x, NumericVector y) {

   // declare the InnerProduct instance that takes a pointer to the vector data
   InnerProduct innerProduct(x, y);

   // call paralleReduce to start the work
   parallelReduce(0, x.length(), innerProduct);

   // return the computed product
   return innerProduct.product;
}
//librar(Rbenchmark)

// [[Rcpp::export]]
NumericVector matrixXvectorRcppParallel(NumericMatrix A, NumericVector x) {

   // // declare the InnerProduct instance that takes a pointer to the vector data
   // InnerProduct innerProduct(x, y);
   int nrows = A.nrow();
   NumericVector out(nrows);
   for(int i = 0; i< nrows;i++ )
   {
      out(i) = parallelInnerProduct(A(i,_),x);
   }
   // return the computed product
   return out;
}

// [[Rcpp::export]]
arma::rowvec matrixXvectorParallel(arma::mat A, arma::colvec x){
    arma::rowvec y = A.row(0)*0;
    int filas = A.n_rows;
    int columnas = A.n_cols;
    #pragma omp parallel for
    for(int j=0;j<columnas;j++)
    {
        //y(j) = A.row(j)*x(j))
        y(j) = dotproduct(A.row(j),x);
    }
    return y;
} 

arma::mat matrixXvector2(arma::mat A, arma::mat x){
  //arma::rowvec y = A.row(0)*0;
  //y=A*x;
  return A*x;
}

arma::rowvec matrixXvectorParallel2(arma::mat A, arma::colvec x){
    arma::rowvec y = A.row(0)*0;
    int filas = A.n_rows;
    int columnas = A.n_cols;

 #pragma omp parallel for
    for(int j = 0; j < columnas ; j++){
        double result = 0;
        for(int i = 0; i < filas; i++){
                result += x(i)*A(j,i);   
        }
        y(j) = result;
    }
    return y;
}

基准

                             test replications elapsed relative user.self sys.self user.child sys.child
1                         M %*% a           20   0.026    1.000     0.140    0.060          0         0
2 matrixXvector2(M, as.matrix(a))           20   0.040    1.538     0.101    0.217          0         0
4    matrixXvectorParallel2(M, a)           20   0.063    2.423     0.481    0.000          0         0
3     matrixXvectorParallel(M, a)           20   0.146    5.615     0.745    0.398          0         0
5 matrixXvectorRcppParallel(M, a)           20   0.335   12.885     2.305    0.079          0         0

我目前的最后一次尝试是将 parallefor 与 Rcppparallel 一起使用,但我遇到了内存错误,而且我不知道问题出在哪里

// [[Rcpp::export]]
NumericVector matrixXvectorRcppParallel2(NumericMatrix A, NumericVector x) {

   // // declare the InnerProduct instance that takes a pointer to the vector data
   int nrows = A.nrow();
   NumericMatrix out(nrows,1); //allocar mempria de vector de salida
   //crear worker
   MatrixMultiplication matrixMultiplication(A, x, out);


   parallelFor(0,A.nrow(),matrixMultiplication);

   // return the computed product
   return out;
}    

我注意到,当我使用 htop 检查终端时处理器的工作方式时,我在 htop 中看到当我使用 R-base 应用传统的矩阵向量乘法时,即使用所有处理器,所以矩阵乘法是否并行执行默认情况下?因为从理论上讲,如果是串行版本,则只有一个处理器应该工作。

如果有人知道 OpenMP 或 Rcppparallel 或其他方式哪个是更好的路径,那么这会给我带来比明显的 R-base 串行版本更好的性能。

目前共轭梯度系列代码

// [[Rcpp::export]]
arma::colvec ConjugateGradient(arma::mat A, arma::colvec xini, arma::colvec b, int num_iteraciones){
    //arma::colvec xnew = xini*0 //inicializar en 0's
    arma::colvec x= xini; //inicializar en 0's
    arma::colvec rkold = b - A*xini;
    arma::colvec rknew = b*0;
    arma::colvec pk = rkold;
    int k=0;
    double alpha_k=0;
    double betak=0;
    double normak = 0.0;

    for(k=0; k<num_iteraciones;k++){
         Rcout << "iteracion numero " << k << std::endl;
        alpha_k =  sum(rkold.t() * rkold) / sum(pk.t()*A*pk); //sum de un elemento para realizar casting
        (pk.t()*A*pk);
        x = x+ alpha_k * pk;
        rknew = rkold - alpha_k*A*pk;
        normak =  sum(rknew.t()*rknew);
        if( normak < 0.000001){
            break;
        }
        betak = sum(rknew.t()*rknew) / sum( rkold.t() * rkold );

        //actualizar valores para siguiente iteracion
        pk = rknew + betak*pk;
        rkold = rknew;

    }

    return x;

}

我不知道 R 中 BLAS 的使用,感谢 Hong Ooi 和 tim18,所以新的基准测试使用 option(matprod="internal") 和 option(matprod="blas")

options(matprod = "internal")
res<-benchmark(M%*%a,matrixXvector2(M,as.matrix(a)),matrixXvectorParallel(M,a),matrixXvectorParallel2(M,a),matrixXvectorRcppParallel(M,a),order="relative",replications = 20)
res

                             test replications elapsed relative user.self sys.self user.child sys.child
2 matrixXvector2(M, as.matrix(a))           20   0.043    1.000     0.107    0.228          0         0
4    matrixXvectorParallel2(M, a)           20   0.069    1.605     0.530    0.000          0         0
1                         M %*% a           20   0.072    1.674     0.071    0.000          0         0
3     matrixXvectorParallel(M, a)           20   0.140    3.256     0.746    0.346          0         0
5 matrixXvectorRcppParallel(M, a)           20   0.343    7.977     2.272    0.175          0         0

选项(matprod =“blas”)

options(matprod = "blas")

res<-benchmark(M%*%a,matrixXvector2(M,as.matrix(a)),matrixXvectorParallel(M,a),matrixXvectorParallel2(M,a),matrixXvectorRcppParallel(M,a),order="relative",replications = 20)
res
                             test replications elapsed relative user.self sys.self user.child sys.child
1                         M %*% a           20   0.021    1.000     0.093    0.054          0         0
2 matrixXvector2(M, as.matrix(a))           20   0.092    4.381     0.177    0.464          0         0
5 matrixXvectorRcppParallel(M, a)           20   0.328   15.619     2.143    0.109          0         0
4    matrixXvectorParallel2(M, a)           20   0.438   20.857     3.036    0.000          0         0
3     matrixXvectorParallel(M, a)           20   0.546   26.000     3.667    0.127          0         0

正如您已经发现的,如果使用多线程 BLAS 实现,则基本 R 矩阵乘法可以是多线程的。情况就是这样rocker/*docker 镜像,通常使用 OpenBLAS。

此外,(Rcpp)Armadillo 已经使用了 R 使用的 BLAS 库(在本例中为多线程 OpenBLAS)以及 OpenMP。所以你的“串行”版本实际上是多线程的。您可以在以下位置验证这一点:htop以足够大的矩阵作为输入。

顺便说一句,你想做的事情看起来像过早优化 http://wiki.c2.com/?PrematureOptimization to me.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用于矩阵向量乘积的 Rcpp Parallel 或 openmp 的相关文章

  • 在 openMP C++ 中并行化许多嵌套 for 循环

    你好 我是 C 新手 我编写了一个可以运行的代码 但是由于许多嵌套的 for 循环 它很慢 我想通过 openmp 来加速它 任何可以指导我的人 我尝试使用 pragma omp 并行 前ip循环并在这个循环中我使用了 pragma omp
  • 有没有好的 x86 双精度小矩阵 SIMD 库?

    我正在寻找一个专注于图形小型 4x4 矩阵运算的 SIMD 库 那里有很多单精度 但我需要支持单精度和双精度 我看过 Intel 的 IPP MX 库 但我更喜欢带有源代码的库 我对这些特定操作的 SSE3 实现非常感兴趣 垫4 垫4 Ma
  • 在 Mac OS 上使用 OpenMP 和 C++11

    我正在尝试在我的 C 11 代码中使用一些 OpenMP 多线程功能 例如 pragma omp parallel for 当我尝试使用以下命令进行编译时 clang std c 11 stdlib libc fopenmp main cp
  • OpenMP 因大型数组而崩溃

    我正在使用 Fortran 和 OpenMP 但当我尝试在存在大型数组时使用 OpenMP 并行化循环时 我不断遇到问题 例如 以下代码 PROGRAM main IMPLICIT NONE INTEGER PARAMETER NUMLOO
  • 在不平衡树上拆分 OpenMP 线程

    我正在尝试使用 OpenMP 并行进行树操作 例如对树中所有叶子中的数字进行求和 我遇到的问题是我工作的树不平衡 子节点的数量不同 分支的大小也不同 我目前在这些树上使用递归函数 我想要实现的是 1 在第一个可能的机会时分割线程 假设它是一
  • 使用 OpenMP 时无用的 printf 没有加速

    我刚刚编写了第一个 OpenMP 程序 它并行化了一个简单的 for 循环 我在双核机器上运行代码 发现从 1 个线程变为 2 个线程时速度有所提高 然而 我在学校 Linux 服务器上运行相同的代码并没有看到加速 在尝试了不同的事情之后
  • 在Python中计算矩阵乘以其转置(AA^T)的最快方法

    在Python中将矩阵与其转置 AA T 相乘的最快方法是什么 我认为 NumPy SciPy 没有考虑使用例如时涉及的对称性 np dot or np matmul 得到的矩阵总是对称的 所以我可以想象有一个更快的解决方案 None
  • OpenMP 线程映射到物理内核

    于是我在网上查了一段时间没有结果 我是 OpenMP 的新手 所以不确定这里的术语 但是有没有办法从 OMPThread 由 omp get thread num 给出 和线程将运行的物理核心找出特定机器的映射 我还对 OMP 分配线程的精
  • 使用 Rcpp 得出斐波那契数列的意外结果

    我刚刚开始使用Rcpp很抱歉 如果我错过了一个简单的步骤或类似的东西 我已经尝试过这个 sourceCpp library Rcpp sourceCpp code include
  • R、Rcpp 与 Armadillo 中矩阵 rowSums() 与 colSums() 的效率

    背景 来自 R 编程 我正在扩展到 C C 形式的编译代码Rcpp 作为循环交换 以及一般的 C C 效果的实践练习 我实现了 R 的等效项rowSums and colSums 矩阵的函数Rcpp 我知道它们以 Rcpp 糖的形式存在 并
  • C++ 是否可以在 MacOS 上与 OpenMP 和 boost 兼容?

    我现在已经尝试了很多事情并得出了一些结论 也许 我监督了一些事情 但似乎我无法完成我想要的事情 问题是 是否有可能使用 OpenMP 和 boost 在 MacOS High Sierra 上编译 C 一些发现 如果我错了请纠正我 Open
  • 如何使用 Rcpp 将 C 结构从 C 库公开到 R

    我正在尝试将 C 结构从 C 库公开到 R 中 例如 struct A int flag 库提供 API 来构造和销毁是很常见的A A initA void freeA A a 感谢RCPP MODULE 很容易暴露它而不考虑析构函数 in
  • OpenMP 共享与第一私有性能比较

    我有一个 pragma omp parallel for在类方法内循环 每个线程只读访问很少的方法局部变量 很少调用私有数据和方法的参数 所有这些都在一个声明中声明shared条款 我的问题 性能方面不应该有任何区别声明这些 变量share
  • 当我使用并行代码时,为什么我的计算机没有显示加速?

    所以我意识到这个问题听起来很愚蠢 是的 我使用的是双核 但我尝试了两个不同的库 Grand Central Dispatch 和 OpenMP 并且当使用 clock 来对带有和不带有使平行的话 速度是一样的 根据记录 他们都使用自己的并行
  • 更快地评估从右到左的矩阵乘法

    我注意到以二次形式评估矩阵运算右到左明显快于左到右在 R 中 取决于括号的放置方式 显然它们都执行相同的计算量 我想知道为什么会这样 这与内存分配有什么关系吗 A 5000 5000 B 5000 2 A matrix runif 5000
  • Mac OS Big Sur R 编译错误:ld:找不到 CoreFoundation 框架

    在我的 Xcode 自动更新到 12 4 后 我的 Rstudio 包编译中断并抛出以下错误 ld framework not found CoreFoundation collect2 error ld returned 1 exit s
  • 如何处理 OpenMP 中的数据争用?

    我正在尝试使用 OpenMP 将数字添加到数组中 以下是我的代码 int input int malloc sizeof int snum int sum 0 int i for i 0 i
  • 帮助解决 openmp 编译问题

    我试图在我的 C 代码中使用 omp 并遇到问题 在代码中我有 include 但是当我尝试编译时 g fopenmp g c 并行 c 我收到 cc1plus error unrecognized command line option
  • 如何使用 OpenMP 并行化数组移位?

    如何使用 OpenMP 并行化数组移位 我已经尝试了一些方法 但没有得到以下示例的任何准确结果 该示例旋转 Carteira 对象数组的元素 用于排列算法 void rotaciona int i Carteira aux this gt
  • 2 个数组/图像相乘的多线程性能 - 英特尔 IPP

    我正在使用英特尔 IPP 来进行 2 个图像 数组 的乘法 我使用的是 Intel Composer 2015 Update 6 附带的 Intel IPP 8 2 我创建了一个简单的函数来乘以太大的图像 整个项目已附后 见下文 我想看看使

随机推荐