gpt4 book ai didi

openmp - 如何统计测量程序中的 OpenMP 性能?

转载 作者:行者123 更新时间:2023-12-03 18:15:50 27 4
gpt4 key购买 nike

我想统计测量与 OpenMP 并行化的程序的性能。我选择在执行并行算法的测试应用程序中编写循环 MAX_EXPERIMENTS次并将时间测量报告到文件中。

问题解决方案似乎比提取外部循环上方的并行编译指示更复杂,因为我在内部并行循环之间有代码的串行部分。

编码:

#include <omp.h>
#include <vector>
#include <random>
#include <iostream>
#include <cmath>
#include <fstream>
#include <sstream>
#include <iomanip>

using namespace std;

int main()
{
const int MAX_NUMBERS = 1e07;
const int MAX_EXPERIMENTS = 1e02;

std::random_device rd;
std::mt19937 gen(rd());
std::bernoulli_distribution dis(0.1);

vector<double> numbers;
numbers.reserve(MAX_NUMBERS);

for(unsigned int i = 0; i < MAX_NUMBERS; ++i)
{
if (dis(gen))
numbers.emplace_back(100);
else
numbers.emplace_back(1);
}

stringstream ss;
ss << "time-measurements-nthread-" << setfill('0') << setw(2)
<< omp_get_max_threads() << ".csv";

ofstream exp(ss.str());
exp << "time\n";

for (unsigned int i = 0; i < MAX_EXPERIMENTS; ++i)
{
// BEGIN: Tested parallel program
double t0 = omp_get_wtime();

// Some serial work.

double x = 0;
//#pragma omp parallel for schedule(dynamic) reduction(+:x) // exp-01
#pragma omp parallel for schedule(static) reduction(+:x) // exp-02
for(unsigned int i = 0; i < numbers.size(); ++i)
{
if (numbers[i] > 1)
x = x + cos(numbers[i]); // Some work being done.
}
double t1 = omp_get_wtime();

// Some serial work

// Measure program execution
exp << t1 - t0 << "\n";

// END: Tested parallel program
}

};

程序首先串行初始化 1e07号码为 1100使得命中 100的概率是 10% ,这与我真实世界的输入数据相匹配。

主测试循环执行 100实验,并且循环体模型测试了并行算法。并行算法的某些部分必须串行执行。写作 pragma omp parallel for在循环中应该是一个坏主意,因为每次创建实验时它都会打开和关闭线程。

问题 1 :即使通常人们会避免在循环内打开并行区域,在这种情况下是否合理,其中每个实验循环步骤代表一个独立的并行程序执行,并且在运行时为实验准备输入数据的速度要快得多?

为了可视化写入的数据,我使用了 python(jupyter nootebook 代码):
%matplotlib inline

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rcParams
import os

rcParams["figure.figsize"] = [10,20]
rcParams["font.size"] = 24

def plot_experiment(expattern):
thread1df = pd.read_csv("time-measurements-nthread-01-%s.csv" % expattern)
thread2df = pd.read_csv("time-measurements-nthread-02-%s.csv" % expattern)
thread4df = pd.read_csv("time-measurements-nthread-04-%s.csv" % expattern)
thread8df = pd.read_csv("time-measurements-nthread-08-%s.csv" % expattern)


f, (ax1, ax2) = plt.subplots(2, 1, sharex=True)

ax1.plot(thread1df["time"], label="time 1", color='g')
ax1.plot(thread2df["time"], label="time 2", color='r')
ax1.plot(thread4df["time"], label="time 4", color='b')
ax1.plot(thread8df["time"], label="time 8", color='k')

ax2.plot(thread1df["time"] / thread8df["time"], label="speedup 8", color='k')
ax2.plot(thread1df["time"] / thread4df["time"], label="speedup 4", color='b')
ax2.plot(thread1df["time"] / thread2df["time"], label="speedup 2", color='r')

ax1.set_ylabel("Time in seconds")
ax1.legend()

ax2.set_xlabel("Test number")
ax2.legend()
ax2.set_ylabel("Speedup")


plot_experiment("exp-01")

并且应用程序是用gcc编译的,使用优化: g++ -std=c++1y -fopenmp -O3 main.cpp -o main
实验使用 for i in 1 2 4 8; do export OMP_NUM_THREADS=$i && ./main && sleep 5; done; 执行

然后使用 for file in *nthread-0[0-9].csv*; do mv $file ${file/.csv/-exp-02.csv}; done 为 Pandas 重新命名实验文件(在第一个实验中将 exp-02 替换为 exp-01)。

在第一个实验中,我尝试了动态调度,得到如下图:

enter image description here

这很奇怪,因为添加线程似乎会减慢程序的速度。使用 HPCToolkit 检查瓶颈为 exp-018线程,我注意到 OpenMP 花费了大量时间在 dynamic 中进行切换。调度模式:

enter image description here

所以我把调度模式切换到 static并重新运行实验,然后得到以下结果:

enter image description here

现在有一些扩展,至少对于 2线程,但现在 4线程振荡太多,使用 8效果不大线程。我使用 HPCToolkit 检查了它再次得到这个:

enter image description here

我认为这告诉我启动和停止线程正在消耗 85%我的运行时 8线程,但是 HPCToolkit 手册指出

Furthermore, if the filtered nodes are children of a “fake” procedures (such as program_root and thread_root), the exclusive metrics in callers view and flat view can be misleading.



问题 2 : 实验02在实验循环内打开和关闭并行区域是否有显着开销?如果是这样,考虑到算法的串行部分,如何解决这个问题?

软件:Arch Linux,g++ (GCC) 7.1.1 20170630,hpcrun:HPCToolkit 成员,版本 2017.11,CPU:Intel(R) Core(TM) i7-4710HQ CPU @ 2.50GHz

编辑

我尝试使用建议的环境变量更改线程持久性行为 in the answer to this question :
export OMP_WAIT_POLICY=active GOMP_SPINCOUNT=infinite

结果如下:

enter image description here

显然,由线程创建/销毁引起的振荡要低得多,但它们消失了吗?有没有办法改变程序,这样我就不必依赖自旋线程?调查此程序的瓶颈仍将显示自旋线程花费了大量 CPU 周期。

最佳答案

从评论中的讨论来看,您的主要问题似乎是您有一个复杂的现有应用程序,并希望在某个内部部分放置一个工作共享循环。但是仅创建所有线程在您的应用程序中有太多开销,libgomp 的线程池似乎不够。

如果你想在不重组的情况下做到这一点,使用 taskloop 可能会有所帮助。 ,其作用类似于 for ,但可以嵌套在 single 中部分。反过来,它可能不如`for 那样有效。基本上你的代码看起来像这样:

int a;
#pragma omp parallel
{
int b;
#pragma omp single
{
int c;
// lots of serial code
// somewhere inbetween
#pragma omp taskloop
for (...) {
int d;
}
// lots of serial code
}
}

请注意,任务生成结构的数据共享工作方式略有不同。默认情况下,在并行区域( a )之外声明的变量是 sharedparallel区域,并在执行内循环的任务之间共享。在并行区域内声明的变量,但在 taskloop 之外( b , c ), 是 private平行区域内和 firstprivate - 即每个线程都有自己的副本,该副本使用外部值进行初始化。最后 d只是每个循环迭代的局部。

编辑:不要设置任何障碍。由于隐式任务组,串行部分和任务在执行中是隔离的。

关于openmp - 如何统计测量程序中的 OpenMP 性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48303120/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com