并行计算——OpenMP加速矩阵相乘

高性能计算实验——矩阵乘法基于OpenMP的实现及优化 高性能计算实验——矩阵乘法基于OpenMP的实现及优化1.实验目的1.1.通过OpenMP实现通用矩阵乘法1.2.基于OpenMP的通用矩阵乘法优化1.3.构造基于Pthreads的并行for循环分解、分配和执行机制2.实验过程和核心代码2.1.通过OpenMP实现通用矩阵乘法2.2.通用矩阵乘法优化2.3.大规模矩阵计算优化3.实验结果3.1.基于OpenMP实现通用矩阵乘法3.2.基于OpenMP的通用矩阵优化3.3.构造基于 Pthreads 的并行 for 循环分解、分配和执行机制。4.实验感想 1 阅读详情

        OpenMP是一套基于共享内存方式的多线程并发编程库。第一次接触它大概在半年前,也就是研究cuda编程的那段时间。OpenMP产生的线程运行于CPU上,这和cuda不同。由于GPU的cuda核心非常多,可以进行大量的并行计算,所以我们更多的谈论的是GPU并行计算(参见拙文《浅析GPU计算——CPU和GPU的选择》和《浅析GPU计算——cuda编程》)。本文我们将尝试使用OpenMP将CPU资源榨干,以加速计算。(转载请指明出于breaksoftware的csdn博客)

        并行计算的一个比较麻烦的问题就是数据同步,我们使用经典的矩阵相乘来绕开这些不是本文关心的问题。

环境和结果

        我的测试环境是:

  • CPU:Intel Core i7 4790。主频3.6G,4核8线程,8MB三级缓存,集成HD4600核显。
  • 内存:16G
  • 操作系统:Windows7 64bit

        测试的程序是:

  • 32位Release版
  • 4096*2048和2048*4096两个矩阵相乘
  • 非并行版本直接计算
  • 并行版本使用OpenMP库开启8个线程

基础环境

        CPU基本处在1%左右,抖动频率很低。

非并行计算

       由于是4核8线程,所以CPU最高处在12%(100% 除以8),而且有抖动。

并行计算

        CPU资源被占满,长期处在100%状态。

时间对比

  • 非并行计算:243,109ms
  • 并行计算:68,800ms

        可见,在我这个环境下,并行计算将速度提升了4倍。

测试代码

构建初始矩阵

    auto left = std::make_unique <Matrix<int>>(4096, 2048);
    auto right = std::make_unique <Matrix<int>>(2048, 4096);

    left->fill([](size_t x, size_t y) -> decltype(x * y) {return (x + 1) * (y + 1); });
    right->fill([](size_t x, size_t y) -> decltype(x * y) {return (x + 1) * (y + 1); });

        Matrix是我定义的一个矩阵类,之后会列出代码。

非并行计算

    std::vector<int> result;
    result.resize(left->get_height() * right->get_width());

    {
        Perform p;
        for (size_t i = 0; i < left->get_height(); i++) {
            RowMatrix<int> row(*left, i);
            for (size_t j = 0; j < right->get_width(); j++) {
                ColumnMatrix<int> column(*right, j);
                auto x = std::inner_product(row.begin(), row.end(), column.begin(), 0);
                result[i * right->get_width() + j] = x;
            }
        }
    }

        result用于保存矩阵相乘的计算结果。

        RowMatrix和ColumnMatrix是我将矩阵分拆出来的行矩阵和列矩阵。这么设计是为了方便设计出两者的迭代器,使用std::inner_product方法进行计算。

        Perform是我统计代码段耗时的工具类。其实现可以参见《C++拾取——使用stl标准库实现排序算法及评测》。

并行计算

    std::vector<int> result_parallel;
    result_parallel.resize(left->get_height() * right->get_width());

    {
        Perform p;
        omp_set_dynamic(0);
        #pragma omp parallel default(shared) num_threads(8)
        {
            int iam = omp_get_thread_num();
            int nt = omp_get_num_threads();

            for (size_t i = 0; i < left->get_height(); i++) {
                if (i % nt != iam) {
                    continue;
                }
                RowMatrix<int> row(*left, i);
                for (size_t j = 0; j < right->get_width(); j++) {
                    ColumnMatrix<int> column(*right, j);
                    auto x = std::inner_product(row.begin(), row.end(), column.begin(), 0);
                    result_parallel[i * right->get_width() + j] = x;
                }
            }
        }
    }

        只增加了7行代码。

        第6行,使用omp_set_dynamic关闭OpenMP动态调整线程数。

        第7行,告诉OpenMP启动8个线程执行下面区块中的逻辑。

        第9行,通过omp_get_thread_num()当前线程在OpenMP中的ID。该ID从0开始递增。

        第10行,通过omp_get_num_threads()获取并行执行的线程数。由于第6行和第7行的设置,本例中其值将为8。

        第13~15行,分拆任务。这样可以保证每个线程可以不交叉的运算各自的区域。

        仅仅7行代码,将程序的计算能力提升了4倍!还是很值得的。

矩阵代码

        用于测试的代码比较短小,但是为了支持这个短小的测试代码,我还设计了5个类。

矩阵   

template<class T>
class Matrix {
public:
    Matrix(size_t x, size_t y) : _width(x), _heigth(y) {
        assert(x != 0 && y != 0);
        size_t size = x * y;
        _vec.resize(size);
    }

    Matrix() = delete;
public:
    void fill(std::function<T(size_t, size_t)> fn) {
        for (size_t i = 0; i < _heigth; i++) {
            for (size_t j = 0; j < _width; j++) {
                _vec.at(i * _width + j) = fn(i, j);
            }
        }
    }
public:
    const T* get_row_start(size_t row_num) const {
        assert(row_num < _heigth);
        return &_vec.at(row_num * _width);
    }

    const T* get_row_end(size_t row_num) const {
        assert(row_num < _heigth);
        return &_vec.at((row_num + 1) * _width);
    }

    const T* get_row_data(size_t row_num, size_t index) const {
        return &_vec.at(row_num * _width + index);
    }

    const T* get_column_start(size_t column_num) const {
        assert(column_num < _width);
        return &_vec.at(column_num);
    }

    const T* get_column_end(size_t column_num) const {
        assert(column_num < _width);
        return &_vec.at(_heigth  * _width + column_num);
    }

    const T* get_column_data(size_t column_num, size_t index) const {
        return &_vec.at(index * _width + column_num);
    }

public:
    const size_t get_width() const {
        return _width;
    }

    const size_t get_height() const{
        return _heigth;
    }
private:
    std::vector<T> _vec;
    size_t _width;
    size_t _heigth;
};

 行矩阵和其迭代器

template<class T> class RowMatrixIterator;

template<class T>
class RowMatrix {
    friend class RowMatrixIterator<T>;
public:
    RowMatrix(const Matrix<T>& matrix, size_t row_num) {
        _p = &matrix;
        _row_num = row_num;
    }

    RowMatrix() = delete;
public:
    RowMatrixIterator<T> begin() {
        RowMatrixIterator<T> begin_it(*this);
        begin_it._index = 0;
        return begin_it;
    }

    RowMatrixIterator<T> end() {
        RowMatrixIterator<T> end_it(*this);
        end_it._index = _p->get_width();
        return end_it;
    }
private:
    const T* row_offset(size_t index) const {
        return _p->get_row_data(_row_num, index);
    }

protected:
    const Matrix<T>* _p;
    size_t _row_num = 0;
};

template<class T>
class RowMatrixIterator : public std::iterator<std::forward_iterator_tag, T> {
    friend class RowMatrix<T>;
public:
    explicit RowMatrixIterator(RowMatrix<T>& row) : _row(row), _index(0) {
    }

    RowMatrixIterator& operator=(const RowMatrixIterator& src) {
        _row = src._row;
        _index = src._index;
    }

    const T& operator*() {
        return *_row.row_offset(_index);
    }

    RowMatrixIterator& operator++() {
        ++_index;
        return *this;
    }

    RowMatrixIterator& operator++(int) {
        auto temp = RowMatrixIterator(*this);
        _index++;
        return std::move(std::ref(temp));
    }

    bool operator<(const RowMatrixIterator& iter) const { 
        return _index < iter._index; 
    }

    bool operator==(const RowMatrixIterator& iter) const {
        return _index == iter._index;
    }

    bool operator!=(const RowMatrixIterator& iter) const {
        return _index != iter._index; 
    }

    bool operator>(const RowMatrixIterator& iter) const {
        return _index > iter._index; 
    }

    bool operator<=(const RowMatrixIterator& iter) const {
        *this < iter || *this == iter;
    }

    bool operator>=(const RowMatrixIterator& iter) const {
        *this > iter || *this == iter; 
    }
protected:
    RowMatrix<T>& _row;
    size_t _index;
};

   列矩阵和其迭代器

template<class T> class ColumnMatrixIterator;

template<class T>
class ColumnMatrix {
    friend class ColumnMatrixIterator<T>;
public:
    ColumnMatrix(const Matrix<T>& matrix, size_t column_num) {
        _p = &matrix;
        _column_num = column_num;
    }

    ColumnMatrix() = delete;
public:
    ColumnMatrixIterator<T> begin() {
        ColumnMatrixIterator<T> begin_it(*this);
        begin_it._index = 0;
        return begin_it;
    }

    ColumnMatrixIterator<T> end() {
        ColumnMatrixIterator<T> end_it(*this);
        end_it._index = _p->get_height();
        return end_it;
    }
private:
    const T* column_offset(size_t index) const {
        return _p->get_column_data(_column_num, index);
    }
protected:
    const Matrix<T>* _p;
    size_t _column_num = 0;
};

template<class T>
class ColumnMatrixIterator : public std::iterator<std::forward_iterator_tag, T> {
    friend class ColumnMatrix<T>;
public:
    explicit ColumnMatrixIterator(ColumnMatrix<T>& Column) : _p(Column), _index(0) {
    }

    ColumnMatrixIterator& operator=(const ColumnMatrixIterator& src) {
        _p = src._p;
        _index = src._index;
    }

    const T& operator*() {
        return *_p.column_offset(_index);
    }

    ColumnMatrixIterator& operator++() {
        ++_index;
        return *this;
    }

    ColumnMatrixIterator& operator++(int) {
        auto temp = ColumnMatrixIterator(*this);
        _index++;
        return std::move(std::ref(temp));
    }

    bool operator<(const ColumnMatrixIterator& iter) const {
        return _index < iter._index;
    }

    bool operator==(const ColumnMatrixIterator& iter) const {
        return _index == iter._index;
    }

    bool operator!=(const ColumnMatrixIterator& iter) const {
        return _index != iter._index;
    }

    bool operator>(const ColumnMatrixIterator& iter) const {
        return _index > iter._index;
    }

    bool operator<=(const ColumnMatrixIterator& iter) const {
        *this < iter || *this == iter;
    }

    bool operator>=(const ColumnMatrixIterator& iter) const {
        *this > iter || *this == iter;
    }
protected:
    ColumnMatrix<T>& _p;
    size_t _index;
}; 

openMP的加速原理 自动并行化循环,每个线程处理循环中的一部分迭代。OpenMP 将线程的工作划分为独立的任务,每个任务分配给一个线程,最终实现并行加速。使用 OpenMP 很简单,只需在代码中加入相关的 Pragma 指令,编译器会自动处理线程创建、任务分配和同步。这样,通过简单地添加指令,你就可以利用多线程的计算能力,加速循环等耗时操作。 阅读详情

相关推荐

利用openmp实现矩阵相乘_线性代数之——复数矩阵

为了完整地展示线性代数,我们必须包含复数。即使矩阵是实的,特征值和特征向量也经常会是复数。1. 虚数回顾虚数由实部和虚部组成,虚数相加时实部和实部相加,虚部和虚部相加,虚数相乘时则利用 。在虚平面,虚数 是位于坐标 的一个点。复数 的共轭为 。在极坐标下,复数则可以写作模长和极角的形式。两个复数相乘是模长相乘,极角相加。2. 厄米特(Hermitian)矩阵和酉(Unitary)矩...

weixin_39989862的博客 536

使用OpenMP给程序加速(三)负载平衡与并行性能--转载

摘要实现出色并行性能的关键是选择适合应用的粒度。粒度是指并行任务的实际工作量。如果粒度太细,则并行性能会因通信开销增加而受到影响。如果粒度太粗,则并行性能会因负载不均衡而受到影响。为确保实现最佳并行性能,开发人员应确定适合并行任务的粒度(通常粒度越大越好),同时还应避免负载不均衡和通信开销增加的情况发生。背景多线程应用的并行任务工作量大小(粒度)会对其并行性能产生很大影响。在分解一项应用使之适用于多线程处理时,开发人员通常采用的方法是从逻辑上将问题分割成尽量多的并行任务,或者在并行任务内根据共享数据与执行顺

2464

使用OpenMP进行多GPU的线程处理实例

接下来,我们创建一个基于OpenMP的C++程序,其中包括了CUDA的头文件和库文件。我们定义了一个用于并行处理的函数,在这个函数中,我们使用CUDA的API初始化多个GPU设备,并为每个设备开辟相应的内存空间,以便在后续的计算中使用。然后,我们通过OpenMP的指令让每个线程对应不同的GPU设备,并将数据分配给每个线程所对应的设备进行计算。最后,我们需要注意的是,在实际项目中,我们还需要加入异常情况的处理,比如CUDA API调用失败等情况。在这个例子中,我们使用OpenMP的。指令来并行化处理数据,

pytorchCode的博客 664

openMP和Cuda对加密程序进行并行化加速

CUDA(Compute Unified Device Architecture)是由NVIDIA公司开发的一个并行计算平台和应用程序接口(API)模型。它允许软件开发者和软件工程师使用NVIDIA GPU(图形处理单元)进行通用处理(即非图形处理),这种技术被称为GPGPU(General-Purpose computing on Graphics Processing Units)。执行线程是可由调度程序独立管理的最小程序指令序列。

Takina的博客 1845

C++中的并发编程:使用OpenMP进行高效处理

本文以 OpenMP 为例,简要介绍 OpenMP 的历史、作用、应用范围及优缺点。文章会给读者提供一些示例代码,展示如何使用 OpenMP 来实现并行计算。

AI天才研究院 3204

Android13系统设置第三方app为设备所有者

DevicePolicyManagerService.java文件所在目录是frameworks/base,记得将包类名换成自己app的。2. 将拷贝到系统分区下的两个xml文件继续通过init.rc服务拷贝到data/system/目录下并修改权限。1. 拷贝设备所有者和设备管理器两个对应的xml文件到系统分区。

wzh048503的博客 2502

cpu代码加速: 多线程:openmp并行\nOpenMP支持的编程语言包括

cpu代码加速: 多线程:openmp并行\nOpenMP支持的编程语言包括C语言、C++和Fortran;而支持OpenMp的编译器包括Sun Compiler,GNU Compiler和Intel Compiler等。OpenMp提供了对并行算法的高层的抽象描述。\n\n程序员通过在源代码中加入专用的#pragma来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。\n当选择忽略这些#pragma,或者编译器不支持OpenMP时,程序又可退化为通常的程序(一般为串行)

y632481222的博客 278

细说VLAN之untag和tag

一、拓扑图 先给出上面拓扑图的结论,后面再详细讲解: 结论:只有PC1与PC4之间能相互通信,除此之外,所有PC之间均不能通信,比如PC1与PC3不能建立通信。 针对Ping命令的一个知识: 要想ping通某台主机,必须满足两个条件: 1,源主机的ICMP请求包必须能达到目的主机。 2,目的主机的ICMP应答包必须能回到源主机。 二、理论基础 端口的出...

NoobMaster的博客 3万+

openmp矩阵乘法

目录 1. 实验内容与方法 2. 实验过程 2.1 运行时间 2.2 加速比 2.3 图表 3. 实验分析 4. 源代码 1. 实验内容与方法 初始化数组。初始化三个double矩阵matrix_a,matrix_b和result,矩阵的行和列可以根据数据量大小自行调整。数组中的值使用c++11中的random类随机生成0到1之间的double值。 程序计时。使用c++11中的...

dadan的博客 1万+

U8 API接口开发踩过的坑

U8 API接口开发踩过的坑

haiyang5233233的专栏 1655

Apollo学习笔记(4)坐标系

百度Apollo项目用到了多种坐标系,其中帮助文档提及的坐标系包括: 全球地理坐标系(The Global Geographic coordinate system ) 局部坐标系—东-北-天坐标(The Local Frame – East-North-Up,ENU) 车身坐标系—右-前-天坐标(The Vehicle Frame —Right-Forward-Up,RFU) 车身坐标系—前-左-天坐标(The Vehicle Frame —Front-Left-Up,FLU) 还有一种Frenet坐标

qq_24649627的博客 5798

OpenMP+AVX加速矩阵运算

根据高维数组的存储方式不难知道,在这样的情况下,矩阵 A 是的访问在内存上是连续的,而 B 的访问在内存上是不连续的。在开 16 线程的条件下,对比 2000*2000 的矩阵乘法,使用 avx256 的速度大概接近常规的方式的 140 倍,使用 avx512 的速度大概接近常规的方式的 220 倍。对比 2000*2000 的矩阵乘法,使用 avx256 的速度大概是常规的方式的 8 倍多,使用 avx512 的速度大概接近常规的方式的 13 倍。的寄存器的每个单元都要写入相同的。方法进行复制型的加载。

哦豁灬 1万+

SAP FI实战入门:从借贷法则到F-02记账,新手必懂的会计凭证操作

本文详细介绍了SAP FI模块中借贷记账法的核心逻辑及F-02凭证录入的全流程操作。通过实例解析借贷方向规则、科目编码设置及金额镜像原则,帮助新手快速掌握会计凭证操作技巧。同时提供了FS10N余额查询和常见错误排查方法,助力用户高效完成SAP财务记账任务。

weixin_29306571的博客 260

CUDA算法:矩阵相乘(含C语言代码和OPENMP)

CUDA是基于nvidia的显卡SDK一门并行编程技术。做矩阵相乘自然是“得心应手”。 设A为的矩阵,B为的矩阵,那么称的矩阵C为矩阵A与B的乘积,记作。 C/CPP串行实现代码: int Matrix::MatMulti(Mat &MatA, Mat &MatB, Mat &MatC){ if(MatA.col!=MatB.row) ...

DevinTT的专栏 1495

【报错】ImportError: libpython3.10.so.1.0: cannot open shared object file: No such file or directory

ImportError: libpython3.10.so.1.0: cannot open shared object file: No such file or directory

静谧、淡雅 859

矩阵乘法的OpenMP并行程序

同时,为了避免程序的多次间断的运行,导致程序运行时CPU的状态差,导致实验获取的结果误差较大,这里考虑,创建数组提前存储“1、2、4、8、16、32、64”,然后通过for循环将数值传入函数“double juzheng(int tp)”中,获取运行时间,并计算出对应5次实验的平均值。所以在高级语言的层面,除非通过编译器提供的方法或者本来的规定的语言的规则,否则,我们是无法轻易判断一个操作是原子操作的。根据对上述,表1和图2的分析可知,随着线程数的增加,矩阵乘法的运行时间逐渐下降,加速比逐渐上升。

weixin_60705585的博客 1916

C#上位机性能优化全攻略:彻底解决界面卡顿+多线程死锁+PLC数据延迟,产线实测稳定运行2年

一开始我们用的是网上找的零散优化代码,根本满足不了要求,后来花了2个月的时间,把之前27条产线的代码整理、重构、优化,做了一套这套方案上线后,连续稳定运行2年零3个月:界面响应时间稳定在50ms以内,多线程死锁率持续为0,PLC数据延迟稳定在150ms以内,系统可用率≥99.99%,帮客户一年省下了超过480万的停线损失、罚款损失和人工成本,客户第二年就把剩下的22条产线也全部交给我们做了,还介绍了3家同行。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 293

OpenMP矩阵乘法实现

前面已经介绍了MPI和Pthreads矩阵乘法的实现,下面将继续介绍OpenMP矩阵乘法的实现方式。其实OpenMP矩阵乘法的实现与前面的Pthreads的实现方式有共同之处,都是基于线程的并行矩阵乘法的实现,因此如果Pthreads那章的代码看明白的话,本章就会变得非常简单,代码实现也和上一章差不了多少。 串行化思路 首先通过I/O操作将两个矩阵读入数组中去,再对第二个矩阵进行转置操作,之...

Francis Li 2345

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

矩阵乘法(暴力,并行加速,stressen,并行加速stressen)

openMP 矩阵乘法 stressen矩阵乘法 openMP加速stressen矩阵乘法 openMP加速普通矩阵乘法

weixin_56273009的博客 3062

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

深圳大学并行计算实验一简单矩阵乘法的OpenMP并行程序

1. 学会编写简单的OpenMP程序;2. 掌握for编译制导语句;3. 对并行程序进行简单的性能分析;

jannleo的博客 1569

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

利用AVX、OpenMP进行矩阵乘加速

在存储访问结构中,行优先存储访问速度是优于列优先的,但B矩阵的访问是以列优先读取的,所以可以考虑将B矩阵存储改为行优先存储。速度得到了很大提升,但是可以看到,我们数据结果出现了误差,这是由于数据量比较大,我们只用的是float类型,造成了。运算方法:A矩阵中每一行中的数字乘以B矩阵中对应的的每一列的数字,把结果相加起来。这样的代码最后计算结果是正确的,但是数据量较大的时候,运算速度就会非常缓慢。此时速度还是不够理想,现在就使用OpenMP进行优化了,这样计算就是A的每一行与其B的对应行相乘并相加。

Vicczyq 1230

基于OpenMP和PThread的多线程矩阵乘法及分析

本文主要记录了基于OpenMP和PThread实现不同线程、不同矩阵大小的矩阵乘法实例及其分析,代码注释比较详细,以供参考。

张小殊的博客 3218

利用openmp实现矩阵相乘算法加速。(矩阵自拟)_AI | 如何实现高速卷积?深度学习库使用了这些「黑魔法」...

文章申明文章作者:Manas Sahni责任编辑:薰风初入弦微信编辑:葡萄文章转自 微信公众号 机器之心(ID:almosthuman2014),原文链接:如何实现高速卷积?深度学习库使用了这些「黑魔法」编者按:使用深度学习库可以大幅加速CNN模型运行,那么这些库中的哪些具体的做法实现了这种高速度和高性能呢?佐治亚理工学院计算机科学硕士研究生Manas Sahni在自己的电脑上试验了多种方法的策略...

weixin_33256233的博客 751

并行计算加速矩阵乘法:算法优化实战案例

通过并行计算技术显著提升矩阵乘法的运算效率,结合算法优化策略,在多核环境下实现性能加速,是高性能计算中的典型应用案例。

weixin_36178216的博客 955
上一篇: C++拾趣——使用stl标准库实现排序算法及评测
下一篇: Colly源码解析——框架
breaksoftware
breaksoftware 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄20年 6901粉丝 724原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

breaksoftware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值