并行计算——OpenMP加速矩阵相乘
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;
};
相关文章:

JavaScript继承详解(四)
文章截图 - 更好的排版 在本章中,我们将分析Douglas Crockford关于JavaScript继承的一个实现 - Classical Inheritance in JavaScript。 Crockford是JavaScript开发社区最知名的权威,是JSON、JSLint、JSMin和ADSafe之父,是《JavaScript: The …

C语言:在屏幕上输出信息
#include<stdio.h>int main(){printf ("This is a C program.\n");printf("welcome to bit\n");return 0;}结果:This is a C program.welcome to bitPress any key to continue转载于:https://blog.51cto.com/yaoyaolx/1715542

Colly源码解析——框架
Colly是一个使用golang实现的数据抓取框架,我们可以使用它快速搭建类似网络爬虫这样的应用。本文我们将剖析其源码,以探析其中奥秘。(转载请指明出于breaksoftware的csdn博客) Collector是Colly的核心结构体,其中包含了…

未经任何测试的源代码开放
未经任何测试的源代码开放 http://files.cnblogs.com/TextEditor/TextBoxEx.rar 这个代码只是一个Demo. 请将一个Vb.net的代码放在C盘下面,并且改名为Test.txt,然后使用菜单的Open来打开文件。 有任何问题,请在这里留言。 C#的上色还没有完成…

助力企业抗疫,360金融推出免费AI语音机器人
复工潮来临之际,为帮助各大企业进行高效的内部防疫宣传、员工行程信息收集以及快速生成公司内部防疫排班表,360金融针对复工企业的需求痛点推出了AI语音机器人,以助力企业更高效的防疫、抗疫。 针对复工企业的需求痛点,360金融人…

实现strncat
函数原型char *strncat(char *front,char *back,size_t count)参数说明back为源字符串,front为目的字符串,count为指定的back中的前count个字符。 所在库名#include <string.h>函数功能把back所指字符串的前count个字符添加到front结尾处&a…

Colly源码解析——结合例子分析底层实现
通过《Colly源码解析——框架》分析,我们可以知道Colly执行的主要流程。本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现。(转载请指明出于breaksoftware的csdn博客) 递归深度 以下例子截取于Basic c : colly.NewCollecto…

无限路由 DI-624+A 详细介绍
无线路由器硬件安装设置图解1、确认宽带线路正常:无线宽带路由器可以让您将家中的计算机共享高速宽带网络连结至互联网;但在此之前,您必须先具备一部基于以太网络的Cable/DSL Modem(使用RJ-45 接头),并确定您的宽带网络在只有连接…
教你如何编写第一个爬虫
2019年不管是编程语言排行榜还是在互联网行业,Python一直备受争议,到底是Java热门还是Python热门也是一直让人争吵的话题。随着信息时代的迭代更新,人工智能的兴起,Python编程语言也随之被人们广泛学习,Python数据分析…

【BZOJ】3542: DZY Loves March
题意 \(m * m\)的网格,有\(n\)个点。\(t\)个询问:操作一:第\(x\)个点向四个方向移动了\(d\)个单位。操作二:询问同行同列其他点到这个点的曼哈顿距离和。强制在线。(\(n \le 10^5,m \le 10^{18}\)ÿ…

Gin源码解析和例子——路由
Gin是一个基于golang的net包实现的网络框架。从github上,我们可以看到它相对于其他框架而言,具有优越的性能。本系列将从应用的角度来解析其源码。(转载请指明出于breaksoftware的csdn博客) 本文我们将分析其路由的原理。先看个例…
一文讲透推荐系统提供web服务的2种方式
作者丨gongyouliu编辑丨zandy来源 | 大数据与人工智能(ID: ai-big-data)推荐系统是一种信息过滤技术,通过从用户行为中挖掘用户兴趣偏好,为用户提供个性化的信息,减少用户的找寻时间,降低用户的决策成本&am…

jQuery遍历json数组怎么整。。。
{"options":"[{\"text\":\"王家湾\",\"value\":\"9\"},{\"text\":\"李家湾\",\"value\":\"10\"},{\"text\":\"邵家湾\",\"value\":\"13\…

述说C#中的值类型和引用类型的千丝万缕
关于值类型和引用类型方面的博客和文章可以说是汗牛充栋了,今天无意中又复读了一下这方面的知识,感觉还是有许多新感悟的,就此时间分享一下: CLR支持两种类型:值类型和引用类型,看起来FCL的大多数类型是引用…

Gin源码解析和例子——中间件(middleware)
在《Gin源码解析和例子——路由》一文中,我们已经初识中间件。本文将继续探讨这个技术。(转载请指明出于breaksoftware的csdn博客) Gin的中间件,本质是一个匿名回调函数。这和绑定到一个路径下的处理函数本质是一样的。 再以Engin…

DNS简单配置
DNS的原理就不说了,这里只是做个简单的配置,也是方便自己记忆,在这里还要十分感谢redking老大的教程!要安装的bind* 、caching-nameserver 包1、/var/named/chroot/etc/named.conf这个文件需要自己创建options { listen-on…
关系抽取论文整理,核方法、远程监督的重点都在这里
来源 | CSDN 博客作者 | Matt_sh,编辑 | Carol来源 | CSDN云计算(ID:CSDNcloud)本文是个人阅读文章的笔记整理,没有涉及到深度学习在关系抽取中的应用。笔记中一部分来自个人解读,一部分来自原文࿰…

freemarker内建函数介绍
Sequence的内置函数1.sequence?first 返回sequence的第一个值。2.sequence?last 返回sequence的最后一个值。3.sequence?reverse 将sequence的现有顺序反转,即倒序排序4.sequence?size 返回sequence的大小5.sequence?sort 将sequence中的对象转化为字符串后顺序…

PowerBuilder 11.x 的重要进步和不足
PowerBuilder 11(以下简称PB)出来有一段时间了,但很多用户对PB11的到底有哪些进步还不是很清楚,由于对PB11缺乏了解和信心,目前用PB11做出像样应用的用户不多,这确实非常遗憾,这里我讲一下我对P…
超赞的PyTorch资源大列表,GitHub标星9k+,中文版也上线了
点击阅读原文,快速报名!作者 | 红色石头来源 | AI有道(ID: redstonewill)自 2017 年 1 月 PyTorch 推出以来,其热度持续上升。PyTorch 能在短时间内被众多研究人员和工程师接受并推崇是因为其有着诸多优点,…

C++拾取——Linux下实测布隆过滤器(Bloom filter)和unordered_multiset查询效率
布隆过滤器是一种判定元素是否存在于集合中的方法。其基本原理是使用哈希方法将数据映射到一个很长的向量上。在维基百科上,它被称为“空间效率和查询时间都远远超过一般的算法”的方法。由于它只保存散列的数据,所以对于很长的数据有着良好的压缩特性&a…

递归思想解决输出目录下的全部文件
刚刚了解了下递归思想 递归就是在方法内调用本方法 下面说一个实际的应用 输出目录下的全部文件,当目录中还有目录时,则进入目录输出里面的文件 import java.io.*; class ShowFile{public static void showfile(File files){if(files.isDirectory()){Fi…

实战之网马解密之shellcode篇
今天上卡卡社区发现里面发了个网马解密的链接,呵呵 顺便试试看能解出来不.呵呵. 相信各位已经对网马有点了解了吧.一般网马都是加密了的.关于什么是网马以及怎么防止网马也不是本文的重点.本文是实战shellcode网马解密.以后的博文会放出常见的网马及其解密.以及常见的解密工具的…
机器学习中的线性回归,你理解多少?
作者丨algorithmia编译 | 武明利,责编丨Carol来源 | 大数据与人工智能(ID: ai-big-data)机器学习中的线性回归是一种来源于经典统计学的有监督学习技术。然而,随着机器学习和深度学习的迅速兴起,因为线性(多…

Golang反射机制的实现分析——reflect.Type类型名称
现在越来越多的java、php或者python程序员转向了Golang。其中一个比较重要的原因是,它和C/C一样,可以编译成机器码运行,这保证了执行的效率。在上述解释型语言中,它们都支持了“反射”机制,让程序员可以很方便的构建一…

设计模式----组合模式UML和实现代码
2019独角兽企业重金招聘Python工程师标准>>> 一、什么是组合模式? 组合模式(Composite)定义:将对象组合成树形结构以表示‘部分---整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性. 类型:结构型模式 顺口…

Golang反射机制的实现分析——reflect.Type方法查找和调用
在《Golang反射机制的实现分析——reflect.Type类型名称》一文中,我们分析了Golang获取类型基本信息的流程。本文将基于上述知识和经验,分析方法的查找和调用。(转载请指明出于breaksoftware的csdn博客) 方法 package mainimpor…
太狠!33岁年薪50万:“复工第一天,谢谢裁掉我!” 网友:有底气!
最近脉脉一则帖子炸锅了:某HR发帖称公司以按时下班为由裁员。这种情况下很多人都慌了,大家纷纷把“副业救国”奉为神律。可是你有没有认真的想过,为什么现在大家都需要副业:意外裁员后,房贷能够按时还上不至于“回收”…

SEO内部链接优化的技巧
内部链接是搜索引擎优化中的重要因素之一。思亿欧做的SEO调查发现,国内大部分网站都没有怎么做内部链接优化。这可能是网站管理员并不知晓SEO或者是对内部链接优化不够重视。 内部链接的设计不能是单纯的为了SEO的目的而作内部链接,同时要注意规划一个良…

Ubuntu 15.10安装ns2.35+nam
2019独角兽企业重金招聘Python工程师标准>>> Step1: 更新系统sudo apt-get update #更新源列表sudo apt-get upgrade #更新已经安装的包sudo apt-get dist-upgrade #更新软件,升级系统Step2:安装ns2需要的几个包sudo apt-get install build-essentialsu…