使用 openMP 并行化矢量 C++ 对象上的循环
Using openMP to parallelize a loop over a vector c++ objects
我正在尝试使用 openMP 提高 C++ 代码的性能,但我没有看到很好的缩放比例。在深入研究我的代码的细节之前,我有一个非常笼统的问题,如果我能得到明确的答案,我认为可以节省很多时间。
代码的基本结构是一个对象向量(假设大小 num_objs = 5000),其中每个对象包含一个相对较小的双精度向量(让我们说尺寸 num_elems = 500)。我想遍历这个对象向量,并且对于每个对象,在成员向量上执行一个子循环来修改每个元素。我只是试图并行化外部循环(在对象上),因为这是 openMP 的标准方法,而且这个循环比嵌套循环大得多。
现在回答我的问题。我是否通过遍历对象数组然后遍历每个较小的成员向量来严重影响性能?如果我改为制作一个大小为 num_objects * num_elems 的大向量然后执行并行循环那个大向量的“块”,它对应于我上面描述的每个对象中存储的成员向量?这样外循环和内循环都将从一个大向量中访问数据,而不是必须从单独的对象中获取数据?
实际代码比上面描述的要复杂得多,所以为了尝试这种替代方法需要花费大量时间进行修改。因此,我只是想感受一下,如果我花时间重构整个代码,我能获得多大的加速。我对计算机体系结构、内存访问、缓存等了解不多,如果这很明显,我深表歉意。
编辑:
我在想这可能有一个简单的答案;但是,我发现事实并非如此。请考虑以下(简化示例)。
#include <cmath>
#include <ctime>
#include <iostream>
#include <omp.h>
#include <string>
#include <vector>
class Block {
public:
static double a;
std::vector<double> x;
std::vector<double> y;
Block(int N);
};
double Block::a = 5;
int main(int argc, char const *argv[]) {
int num_blocks = 80000;
int num_elems = 1000;
int num_iter = 100;
int nthreads = 1;
bool parallel_on = true;
omp_set_num_threads(nthreads);
std::vector<Block> block_vec;
for (int i = 0; i < num_blocks; i++) {
block_vec.push_back(Block(num_elems));
}
double start;
double end;
start = omp_get_wtime();
int iter = 0;
while (iter < num_iter) {
#pragma omp parallel for if (parallel_on)
for (int bl = 0; bl < num_blocks; bl++) {
for (int i = 0; i < num_elems; i++) {
block_vec[bl].x[i] = Block::a * block_vec[bl].y[i] + block_vec[bl].x[i];
}
}
iter++;
std::cout << "ITER: " << iter << std::endl;
}
end = omp_get_wtime();
double time_taken = end - start;
std::cout << "TIME: " << time_taken << std::endl;
return 0;
}
Block::Block(int N) {
x.assign(N, 2.0);
y.assign(N, 3.0);
}
我用以下代码编译这个程序:
g++ -fopenmp -O3 saxpy.cpp
我 运行 它在 i7-6700 CPU @ 3.40GHz(四个物理内核和八个逻辑内核)上。这是不同线程数的计算时间:
1 个线程:8.65 秒
2 个线程:7.37 秒
3 线程:7.41s
4 线程:7.65s
我确实尝试了上面描述的此代码的一个版本,它使用一个大向量而不是嵌套循环;然而,它的结果大致相同,实际上有点慢。
你的程序运行速度主要取决于内存的速度read/write(包括缓存利用率等)。根据硬件,您可能会或可能不会观察到速度增加。有关详细信息,请阅读例如this.
在我的笔记本电脑上(i7-8550U,g++ -fopenmp -O3 -mavx2 saxpy.cpp)我得到了类似的结果,但在 Xeon 服务器上我得到了显着的速度提升:
nthreads=1
TIME: 13.0372
real 0m14.303s
user 0m13.206s
sys 0m1.096s
nthreads=4
TIME: 5.1537
real 0m5.921s
user 0m18.473s
sys 0m0.615s
nthreads=8
TIME: 3.43479
real 0m4.237s
user 0m27.337s
sys 0m0.608s
我正在尝试使用 openMP 提高 C++ 代码的性能,但我没有看到很好的缩放比例。在深入研究我的代码的细节之前,我有一个非常笼统的问题,如果我能得到明确的答案,我认为可以节省很多时间。
代码的基本结构是一个对象向量(假设大小 num_objs = 5000),其中每个对象包含一个相对较小的双精度向量(让我们说尺寸 num_elems = 500)。我想遍历这个对象向量,并且对于每个对象,在成员向量上执行一个子循环来修改每个元素。我只是试图并行化外部循环(在对象上),因为这是 openMP 的标准方法,而且这个循环比嵌套循环大得多。
现在回答我的问题。我是否通过遍历对象数组然后遍历每个较小的成员向量来严重影响性能?如果我改为制作一个大小为 num_objects * num_elems 的大向量然后执行并行循环那个大向量的“块”,它对应于我上面描述的每个对象中存储的成员向量?这样外循环和内循环都将从一个大向量中访问数据,而不是必须从单独的对象中获取数据?
实际代码比上面描述的要复杂得多,所以为了尝试这种替代方法需要花费大量时间进行修改。因此,我只是想感受一下,如果我花时间重构整个代码,我能获得多大的加速。我对计算机体系结构、内存访问、缓存等了解不多,如果这很明显,我深表歉意。
编辑: 我在想这可能有一个简单的答案;但是,我发现事实并非如此。请考虑以下(简化示例)。
#include <cmath>
#include <ctime>
#include <iostream>
#include <omp.h>
#include <string>
#include <vector>
class Block {
public:
static double a;
std::vector<double> x;
std::vector<double> y;
Block(int N);
};
double Block::a = 5;
int main(int argc, char const *argv[]) {
int num_blocks = 80000;
int num_elems = 1000;
int num_iter = 100;
int nthreads = 1;
bool parallel_on = true;
omp_set_num_threads(nthreads);
std::vector<Block> block_vec;
for (int i = 0; i < num_blocks; i++) {
block_vec.push_back(Block(num_elems));
}
double start;
double end;
start = omp_get_wtime();
int iter = 0;
while (iter < num_iter) {
#pragma omp parallel for if (parallel_on)
for (int bl = 0; bl < num_blocks; bl++) {
for (int i = 0; i < num_elems; i++) {
block_vec[bl].x[i] = Block::a * block_vec[bl].y[i] + block_vec[bl].x[i];
}
}
iter++;
std::cout << "ITER: " << iter << std::endl;
}
end = omp_get_wtime();
double time_taken = end - start;
std::cout << "TIME: " << time_taken << std::endl;
return 0;
}
Block::Block(int N) {
x.assign(N, 2.0);
y.assign(N, 3.0);
}
我用以下代码编译这个程序:
g++ -fopenmp -O3 saxpy.cpp
我 运行 它在 i7-6700 CPU @ 3.40GHz(四个物理内核和八个逻辑内核)上。这是不同线程数的计算时间:
1 个线程:8.65 秒
2 个线程:7.37 秒
3 线程:7.41s
4 线程:7.65s
我确实尝试了上面描述的此代码的一个版本,它使用一个大向量而不是嵌套循环;然而,它的结果大致相同,实际上有点慢。
你的程序运行速度主要取决于内存的速度read/write(包括缓存利用率等)。根据硬件,您可能会或可能不会观察到速度增加。有关详细信息,请阅读例如this.
在我的笔记本电脑上(i7-8550U,g++ -fopenmp -O3 -mavx2 saxpy.cpp)我得到了类似的结果,但在 Xeon 服务器上我得到了显着的速度提升:
nthreads=1
TIME: 13.0372
real 0m14.303s
user 0m13.206s
sys 0m1.096s
nthreads=4
TIME: 5.1537
real 0m5.921s
user 0m18.473s
sys 0m0.615s
nthreads=8
TIME: 3.43479
real 0m4.237s
user 0m27.337s
sys 0m0.608s