【发布时间】:2017-11-02 21:23:21
【问题描述】:
我正在研究 OpenMP,部分原因是我的程序需要添加非常大的向量(数百万个元素)。但是,如果我使用 std::vector 或原始数组,我会看到很大的差异。我无法解释。 我坚持认为区别仅在于循环,当然不是初始化。
我所指的时间差异,只是时间加法,特别是没有考虑向量、数组等之间的任何初始化差异。我真的只在谈论求和部分。向量的大小在编译时是未知的。
我在 Ubuntu 16.04 上使用 g++ 5.x。
编辑:我测试了@Shadow 所说的内容,这让我开始思考,优化是否有问题?如果我使用-O2 编译,那么,使用初始化的原始数组,我会返回线程数进行循环缩放。但是使用-O3 或-funroll-loops,就好像编译器提前启动并在看到编译指示之前进行优化。
我想出了以下简单的测试:
#define SIZE 10000000
#define TRIES 200
int main(){
std::vector<double> a,b,c;
a.resize(SIZE);
b.resize(SIZE);
c.resize(SIZE);
double start = omp_get_wtime();
unsigned long int i,t;
#pragma omp parallel shared(a,b,c) private(i,t)
{
for( t = 0; t< TRIES; t++){
#pragma omp for
for( i = 0; i< SIZE; i++){
c[i] = a[i] + b[i];
}
}
}
std::cout << "finished in " << omp_get_wtime() - start << std::endl;
return 0;
}
我用
编译 g++ -O3 -fopenmp -std=c++11 main.cpp
并获得一个线程
>time ./a.out
finished in 2.5638
./a.out 2.58s user 0.04s system 99% cpu 2.619 total.
对于两个线程,循环需要 1.2s,总共 1.23。
现在如果我使用原始数组:
int main(){
double *a, *b, *c;
a = new double[SIZE];
b = new double[SIZE];
c = new double[SIZE];
double start = omp_get_wtime();
unsigned long int i,t;
#pragma omp parallel shared(a,b,c) private(i,t)
{
for( t = 0; t< TRIES; t++)
{
#pragma omp for
for( i = 0; i< SIZE; i++)
{
c[i] = a[i] + b[i];
}
}
}
std::cout << "finished in " << omp_get_wtime() - start << std::endl;
delete[] a;
delete[] b;
delete[] c;
return 0;
}
我得到(1 个线程):
>time ./a.out
finished in 1.92901
./a.out 1.92s user 0.01s system 99% cpu 1.939 total
std::vector 慢了 33%!
对于两个线程:
>time ./a.out
finished in 1.20061
./a.out 2.39s user 0.02s system 198% cpu 1.208 total
作为比较,使用 Eigen 或 Armadillo 进行完全相同的操作(使用向量对象的 c = a+b 重载),我得到的总实时时间约为 2.8 秒。它们不是用于向量添加的多线程。
现在,我认为std::vector 几乎没有开销?这里发生了什么?我想使用漂亮的标准库对象。
在这样的简单示例中,我在任何地方都找不到任何参考。
【问题讨论】:
-
如果您在编译时知道确切的大小,只需使用
std::array它可能比在向量上调用 resize 来获得这么大的容量要快一些。 -
您使用什么编译器版本?什么处理器/内存/操作系统?可能需要使用非预定为零的值初始化数据以获得可比较的结果。
-
你好。我所指的计时器,至于 33%,只是计时加法,尤其是不考虑任何初始化。我真的只谈论总和。大小在编译时是未知的。我在 Ubuntu 16.04 上使用 g++ 5.something。至于处理器,我不明白这有什么关系。我认为这是在 i7 6500U 上。 4M L3 缓存,当然。
-
顺便说一句:我不认为这是一个 OpenMP 问题,因为您可以省略编译指示并且行为仍然相同。而且您甚至没有将使用
std::vector运行的两个线程与使用原始数组运行的两个线程进行比较。 -
我做了,只是没有发布。 1个线程2.6s,2个1.2..
标签: c++ openmp linear-algebra compiler-optimization