为什么使用openMP的VC++矩阵时间矢量比异步更快
Why is VC++ matrix times vector faster with openMP than async?
我用两种方式编码了向量与矩阵的乘积,一种是用openMP,另一种是std::async。我原以为演出会完全一样。OpenMP在第一次调用时速度较慢,可能是因为它推迟了线程池的创建。在那之后,异步版本始终慢40%。(我有英特尔酷睿i5,它是4核。)
怎么回事?VC++不使用异步线程池吗?我在做傻事吗?(很可能。)我认为对输出向量的访问间隔足够大,可以避免错误共享。
#include "stdafx.h"
# include <iostream>
# include <vector>
# include <omp.h>
# include <ctime>
#include <numeric>
#include <thread>
#include <chrono>
#include <future>
// Matrix multiplication of vector using omp
template<class Mat, class Vec>
void mult_mat_vec_omp
(const Mat &mat, const Vec &inp, Vec &out) {
const int steps = static_cast<int>(std::size(mat));
using std::begin; using std::end;
auto N = std::thread::hardware_concurrency();
omp_set_num_threads(N);
#pragma omp parallel for
for (int i=0; i < steps; ++i) {
out[i] = std::inner_product(begin(mat[i]), end(mat[i]), begin(inp), 0.0);
}
}
// Matrix multiplication of vector using async
template<class Mat, class Vec>
void mult_mat_vec_async
(const Mat &mat, const Vec &inp, Vec &out) {
using std::begin; using std::end;
auto N = std::thread::hardware_concurrency();
typedef decltype(N) usigned;
const unsigned steps = static_cast<unsigned>(std::size(mat));
auto f = [&](unsigned id) {
for (unsigned i=id; i < steps; i+= N) {
out[i] = std::inner_product(begin(mat[i]), end(mat[i]), begin(inp), 0.0);
}
};
std::vector<std::future<void>> threads;
for (unsigned i = 1; i<N; ++i) {
threads.push_back(std::async(std::launch::async, f, i));
}
f(0);
for (auto &x: threads) {
x.get();
}
}
double test() {
using std::vector;
using clock=std::chrono::high_resolution_clock;
vector<double> a;
vector<double> b;
vector<double> c;
vector<vector<double>> mat;
vector<double> v;
int rows = 350;
int cols = 350;
for (int i = 0; i< cols; ++i) {
a.push_back(i/10.0);
b.push_back(-999);
c.push_back(8888);
}
for (int i=0; i<rows; ++i) {
v.clear();
for (int j=0; j<cols; ++j) {
v.push_back (((i+.5)*j)/100.0);
}
mat.push_back(v);
}
clock::time_point start = clock::now();
int N = 10000;
for (int i=0; i< N/10; ++i) {
mult_mat_vec_omp(mat, a, b) ;
mult_mat_vec_omp(mat, a, b);
mult_mat_vec_omp(mat, a, b);
mult_mat_vec_omp(mat, a, b);
mult_mat_vec_omp(mat, a, b);
mult_mat_vec_omp(mat, a, b);
mult_mat_vec_omp(mat, a, b);
mult_mat_vec_omp(mat, a, b);
mult_mat_vec_omp(mat, a, b);
mult_mat_vec_omp(mat, a, b);
};
long long duration =
std::chrono::duration_cast<std::chrono::milliseconds>(clock::now()-start).count();
start = clock::now();
size_t cutoff = 0; // 2*rows*cols;
for (int i=0; i< N/10; ++i) {
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
mult_mat_vec_async(mat, a, c);
};
long long duration2 = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now()-start).count();
//cout << mat[0][5] << " " << b[0] << " " << c[0] << endl;
bool good = (b==c);
std::cout << duration*(1.0/N) << ' ' << duration2*(1.0/N) << " " << good << std::endl;
return 0;
}
int main ()
{
for(int i=0; i<15; ++i) test();
return 0;
}
在英特尔酷睿i7-2600上,禁用HT,使用gcc 7.2/Linux,数字有所不同,异步版本慢了大约10%。
现在,关于缓存效率和错误共享的讨论已经步入正轨。您应该尝试由同一个线程访问连续的元素,至少访问到缓存行的大小(例如64字节)。对于读取,您只需通过使用缓存/数据局部性来节省内存访问——对于写入,效率更高,因为错误共享会在内核之间的缓存线上反弹。然而,重要的是要认识到,这与实际的数据访问无关——这发生在std::inner_product
中,并且对于两个版本都是相同的。如果实际的数据访问是在这样的线程交错模式中,性能将比40%的折扣差得多
现在它很容易避免和测试,如果它有帮助:
const unsigned steps = static_cast<unsigned>(std::size(mat));
auto f = [&](unsigned id) {
const auto chunk_size = 1 + ((steps - 1) / N);
const auto max = std::min(chunk_size * (id + 1), steps);
for (unsigned i = chunk_size * id; i < max; i++)
{
out[i] = std::inner_product(begin(mat[i]), end(mat[i]), begin(inp), 0.0);
}
};
在我的配置中,消除了版本之间的所有性能差异。
如果您仍然看到系统的性能存在差异,我建议您使用合适的性能分析工具。我不熟悉你的生态系统,所以不能做任何推荐——但重要的是不要猜测性能。
注意,对于高性能数据访问/矩阵乘法,std::vector<std::vector<>>
不是好的数据结构。使用矩阵的连续内存,您将无法接近高度优化的库的性能。
相关文章:
- C++为构建时间获取QDateTime的可靠方法
- 从持续时间构造std::chrono::system_clock::time_point
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- while循环中while循环的时间复杂度是多少
- 使用简单类型列表实现的指数编译时间.为什么
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在已经使用Git的情况下减少编译时间
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何将包含epoch时间的十六进制字符串转换为time_t
- 从文本文件中读取时钟时间和事件时间并进行处理
- 具有未知值时的时间复杂性
- 如何减少花费的时间
- C++在变量给定的指定时间内关闭电脑
- 为什么for循环中的异步不能提高执行时间
- 异步控制线程执行时间
- 异步执行比顺序执行需要更长的时间
- 为什么使用openMP的VC++矩阵时间矢量比异步更快
- 在我的CUDA运行时间计划中,CPU和GPU可以异步计算,但不能合作地计算
- C++:如何运行一个系统命令N次(异步)并返回N次执行时间