当运行不使用调度并使用调度的算法时,性能差异是显着的 - 通过调度,算法在4秒内完成,在14秒内完成。我认为perf会提供一些关于为什么会发生这种情况的见解,但统计数据非常相似。
通过动态调度处理我是否可以安全地解决负载平衡问题?我希望在perf细节中找到一些东西。以下是有用的详细信息。也是用于使用调度的Pagerank的代码......
# pragma omp parallel for schedule(dynamic, 64)
for (int u = 0; u < vertex_count; u++) {
int* in_edge = G.getAdjList(u);
double sum = 0.0;
for (int j = 0; j < in_edge_counts[u]; j++) {
int v = in_edge[j];
sum += conntrib[v];
}
pr_temp[u] = sum * damp + adj;
}
使用调度
107470.977295 task-clock (msec) # 1.743 CPUs utilized
1,187 context-switches # 0.011 K/sec
44 cpu-migrations # 0.000 K/sec
2,279,522 page-faults # 0.021 M/sec
255,920,277,205 cycles # 2.381 GHz (20.00%)
17,116,048,117 stalled-cycles-frontend # 6.69% frontend cycles idle (20.02%)
153,944,352,418 stalled-cycles-backend # 60.15% backend cycles idle (20.02%)
148,412,677,859 instructions # 0.58 insn per cycle
# 1.04 stalled cycles per insn (30.01%)
27,479,936,585 branches # 255.696 M/sec (40.01%)
321,470,463 branch-misses # 1.17% of all branches (50.01%)
78,562,370,506 L1-dcache-loads # 731.010 M/sec (50.00%)
2,075,635,902 L1-dcache-load-misses # 2.64% of all L1-dcache hits (49.99%)
3,100,740,665 LLC-loads # 28.852 M/sec (50.00%)
964,981,918 LLC-load-misses # 31.12% of all LL-cache hits (50.00%)
不使用调度
106872.881349 task-clock (msec) # 1.421 CPUs utilized
1,237 context-switches # 0.012 K/sec
69 cpu-migrations # 0.001 K/sec
2,262,865 page-faults # 0.021 M/sec
254,236,425,448 cycles # 2.379 GHz (20.01%)
14,384,218,171 stalled-cycles-frontend # 5.66% frontend cycles idle (20.04%)
163,855,226,466 stalled-cycles-backend # 64.45% backend cycles idle (20.03%)
149,318,162,762 instructions # 0.59 insn per cycle
# 1.10 stalled cycles per insn (30.03%)
27,627,422,078 branches # 258.507 M/sec (40.03%)
213,805,935 branch-misses # 0.77% of all branches (50.03%)
78,495,942,802 L1-dcache-loads # 734.480 M/sec (50.00%)
2,089,837,393 L1-dcache-load-misses # 2.66% of all L1-dcache hits (49.99%)
3,166,900,999 LLC-loads # 29.632 M/sec (49.98%)
929,170,535 LLC-load-misses # 29.34% of all LL-cache hits (49.98%)
答案 0 :(得分:1)
perf stat
不是理解多线程程序中动态调度的性能影响的正确工具。为此,您需要一个工具,允许您在分析过程中区分线程和时间。
perf timechart
可能值得一试,但它并不特别了解OpenMP。通过使用专门为OpenMP制作的分析工具,您将获得最佳洞察力。此外,为了获得动态,我建议使用跟踪/时间线工具,而不是仅向您显示摘要的分析工具。此类工具的示例为Intel VTune或Score-P(跟踪)/ Vampir(可视化)。
答案 1 :(得分:1)
schedule(dynamic, 64)
告诉OpenMP不要假设每个内循环迭代需要相同的时间,IIRC。
因此,静态调度将工作分解为u值的范围,但是你的一个线程必须以比其余线程更多的总工作量结束(或者由于某种原因需要很长时间),从而延迟完成所有线程的总时间
它运行在具有四个AMD Opteron的计算服务器上。当时机器大部分处于闲置状态。两者之间的唯一区别是使用调度。我省去了时间,因为时间包括一个预处理步骤,它都会产生但仍然相差10秒。
在这种情况下,整体使用的1.4对1.7 CPU可以通过在您谈论的4/14秒内更多的利用率来解释。您可以通过使程序在并行部分之前退出并对其进行分析来近似有趣部分的结果。从总计数中减去这些计数,得到一个非常粗略的近似值。
IDK为何工作不平衡;这是你的代码+数据;G.getAdjList(u)
可能需要花费更多时间,或者某些线程的平均值in_edge_counts[u]
可能更大。
conntrib[in_edge[j]]
的位置差异可能会产生很大的不同,当不同的in_edge
元素接近以前的值时,会导致分散读取或缓存命中的缓存未命中。
如果您有不同的内核竞争内存+最后一级缓存带宽,那么为in_edge
缓存行的请求提供服务的延迟将比{{1}的一行的延迟响应更糟糕因为CPU在知道更多conntrib[]
数据所需的缓存行之前需要in_edge[]
数据。因此conntrib
缓存未命中会降低内存并行性,可能会使该线程获得较少的内存带宽份额。
IDK这些影响能够平衡多少。您的数据更有可能不均匀分布。
太糟糕AMD没有高效的AVX2收集,因为您的数据非常适合in_edge
。并不是说它有助于公平或任何事情,只是(在Skylake上)给予一个可能较小的加速与标量聚集。