咨询:矩阵/张量运算示例能否用OpenMP归约改写
关于张量运算与OpenMP归约的适配分析
OpenMP归约的核心是将多线程的局部计算结果聚合为单一全局结果(如标量求和、求积、取最值),而你的两个示例均为逐元素独立计算,每个位置的输出仅依赖对应位置的输入,无跨线程聚合需求,因此不适用归约指令,但可以直接并行化循环提升效率。以下是具体分析和改写示例:
示例A:三个张量逐元素求和
你的代码是对每个(k,i,j)位置的三个张量元素做独立求和,无全局聚合操作,直接并行化循环即可,无需归约。
并行化改写示例
#include <Eigen/Dense> #include <Eigen/SparseCore> #include <Eigen/Sparse> #include <omp.h> static const int nx = 128; static const int ny = 128; static const int nz = 128; Eigen::Tensor<std::complex<double>, 3> Tensor1(nx,ny,nz); Eigen::Tensor<std::complex<double>, 3> Tensor2(nx,ny,nz); Eigen::Tensor<std::complex<double>, 3> Tensor3(nx,ny,nz); Eigen::Tensor<std::complex<double>, 3> Sum(nx,ny,nz); // 补充Sum的定义 // 用collapse合并三层循环,让OpenMP更高效分配线程任务 #pragma omp parallel for collapse(3) for(int i = 0; i< nx; i++){ for(int j = 0; j< ny; j++){ for(int k = 0; k< nz; k++){ Sum(k,i,j) = Tensor1(k,i,j) + Tensor2(k,i,j) + Tensor3(k,i,j); } } }
若编译器不支持collapse,仅并行化最外层i循环也可达到并行效果。
示例B:叉乘逐元素计算
同理,每个Termx/Termy/Termz的元素仅依赖对应位置的Ax/Ay/Az与常量B,所有计算完全独立,直接并行化循环即可。
并行化改写示例
#include <Eigen/Dense> #include <Eigen/SparseCore> #include <Eigen/Sparse> #include <omp.h> static const int nx = 128; static const int ny = 128; static const int nz = 128; double B[3] = {Bx,By,Bz}; Eigen::Tensor<std::complex<double>, 3> Ax(nx,ny,nz); Eigen::Tensor<std::complex<double>, 3> Ay(nx,ny,nz); Eigen::Tensor<std::complex<double>, 3> Az(nx,ny,nz); Eigen::Tensor<std::complex<double>, 3> Termx(nx,ny,nz); Eigen::Tensor<std::complex<double>, 3> Termy(nx,ny,nz); Eigen::Tensor<std::complex<double>, 3> Termz(nx,ny,nz); #pragma omp parallel for collapse(3) for(int i = 0; i< nx; i++){ for(int j = 0; j< ny; j++){ for(int k = 0; k< nz; k++){ Termx(k,i,j) = Az(k,i,j) * B[1] - Ay(k,i,j) * B[2]; Termy(k,i,j) = Ax(k,i,j) * B[2] - Az(k,i,j) * B[0]; Termz(k,i,j) = Ay(k,i,j) * B[0] - Ax(k,i,j) * B[1]; } } }
线程间无数据竞争,因为每个线程仅操作Termx/Termy/Termz的不同位置。
归约的典型适用场景
只有当需求是全局聚合计算时,才需要用OpenMP归约。比如计算张量所有元素的总和(得到一个标量):
std::complex<double> total_sum = 0; #pragma omp parallel for collapse(3) reduction(+:total_sum) for(int i = 0; i< nx; i++){ for(int j = 0; j< ny; j++){ for(int k = 0; k< nz; k++){ total_sum += Tensor1(k,i,j); } } }
此时每个线程维护局部total_sum副本,循环结束后OpenMP自动合并所有副本得到全局总和,这才是归约的标准用法。
内容的提问来源于stack exchange,提问作者Jamie
相关产品推荐
相关产品推荐

