OpenMP 基准并行计算

OpenMP benchmarking parallel computations

我正在尝试对 f(x) 的计算进行基准测试,同时在每次迭代中改变线程数。

f(x) = c * ln(x) * cos(x)

n=10000000


for (int pp = 2; pp<17; pp++)
{
    p = pp;
    int chunk = n/p; //acts like floor
    omp_set_num_threads(p);
    double start_parallel = omp_get_wtime();
    //start parallel
    #pragma omp parallel shared(tt,chunk) private (i)
    {
        //printf("thread number %d\n",omp_get_thread_num());
        #pragma omp for schedule(dynamic,chunk) nowait
        for(i=0; i<n; i++)
        {
            //tt[i] = f(tt[i]);
            tt[i] = f1(tt[i]); //the speed up is much higher with f1 since log and cos 
                               //computations are polynomial; see function.
        }
    } //end parallel
    double end_parallel = omp_get_wtime();
    double cpu_time_used_parallel = (double) (end_parallel - start_parallel);
    printf("parallel: for n=%d, p=%d, time taken=%f, speedup=%f\n",
            n,p,cpu_time_used_parallel,
            cpu_time_used_seq/cpu_time_used_parallel);
}

结果:

Started varying threads:

parallel: for n=10000000, p=2, time taken=0.153774, speedup=3.503831

parallel: for n=10000000, p=3, time taken=0.064447, speedup=8.360370

parallel: for n=10000000, p=4, time taken=0.044694, speedup=12.055239

parallel: for n=10000000, p=5, time taken=0.048700, speedup=11.063550

parallel: for n=10000000, p=6, time taken=0.039009, speedup=13.811989

parallel: for n=10000000, p=7, time taken=0.041735, speedup=12.910017

parallel: for n=10000000, p=8, time taken=0.041268, speedup=13.055919

parallel: for n=10000000, p=9, time taken=0.039032, speedup=13.804157

parallel: for n=10000000, p=10, time taken=0.038970, speedup=13.825767

parallel: for n=10000000, p=11, time taken=0.039843, speedup=13.522884

parallel: for n=10000000, p=12, time taken=0.041356, speedup=13.028237

parallel: for n=10000000, p=13, time taken=0.041039, speedup=13.128763

parallel: for n=10000000, p=14, time taken=0.047433, speedup=11.359218

parallel: for n=10000000, p=15, time taken=0.048430, speedup=11.125202

parallel: for n=10000000, p=16, time taken=0.051950, speedup=10.371477


注意:这里的加速是根据顺序算法计算的(线程数=1)

加速似乎并没有真正受到 p(线程数)变化的影响。

我这样做对吗,或者原因来自于线程数的非有效增加(即理论上改变p不会严重影响O(myprogram) ) ?

由于 omp pragma 中的 nowait 子句,您的结果不正确。通常,一个 parallel 的结束代表一个同步点,但是没有等待,第一个完成的线程不会等待其他线程,所以你将执行 double end_parallel = omp_get_wtime(); 而其他线程仍在做一些事情计算。如果你去掉nowait,你应该观察到完全不同的结果。

Q : Am I doing this right...?
A : No, sorry, you do not happen to do this right. Let's analyse the reasons and let's sketch some hints for HPC-grade performance benchmarking together :

好吧,您已经知道最初的基准设计不是精心设计的。附加成本的影响,在当代 criticism of the original, overhead-naive Amdahl-law has more details on this 和时序细节中得到了很好的证明,因为实例化成本越来越小,相对于其他 类 处理和 I/O-related 成本,因为如下所示。

原因?

与 "computation" 部分相比,该代码具有巨大的附加开销成本。这是对其他合法语法构造器(这里是 OpenMP,其他地方是 map-reduce,其他情况下是列表理解,其他地方是其他一些语法加糖技巧)的技术能力使用有缺陷的最有力标志)

最终性能是一种平衡的艺术(正确 - 平衡成本和收益 - 任何一种不平衡都意味着失去性能优势)。


成本?这里?查看结果的情况::

第二个罪过是忽略 "behind-scene" [TIME] 域对 [SPACE] 域缩放的惩罚。 n 越大,分配的内存就越大,(可怕的)高速缓存行效率低下带来的惩罚就越多,所有这些都具有零保护,不会掉入地狱般的内存交换:

n=1E3        1E4        1E5        1E6        1E7        1E8        1E9       :
______________________________________________________________________________:_____
1.000      1.000      1.000      1.000      1.000      1.000      1.000       : p= 1
0.930      1.403      0.902      1.536      1.492      1.517      0.356       : p= 2
1.075      2.319      2.207      1.937      2.001      1.991      1.489++     : p= 3
1.497+++++ 2.636++++  1.563      1.657      2.571      2.144      0.687       : p= 4
1.226++    2.548+++   0.957      2.025      2.357      1.731      1.569++++   : p= 5
1.255+++   1.805      2.704      2.020      2.348      1.502      0.989       : p= 6
0.957      0.581      3.104++    2.124      2.486      2.002      0.838       : p= 7
1.151      1.376      2.449      2.154      2.573      1.536      0.776       : p= 8
1.135      1.685      2.388      2.506+++   2.852++++  2.311      1.676+++++  : p= 9
1.285++++  2.492++    2.497      2.568++++  2.647+     2.467      1.413+      : p=10
1.177      2.314+     2.709+     2.174      2.688+++   2.634++++  0.606       : p=11
1.216+     2.293      2.442      2.287      2.550      2.551++    1.256       : p=12
1.034      2.148      1.802      2.361++    2.635      2.554+++   1.181       : p=13
0.999      0.440      3.672+++++ 2.774+++++ 2.927+++++ 2.839+++++ 1.496+++    : p=14
1.091      1.217      3.285++++  2.284      2.525      2.356      1.005       : p=15
0.937      2.850+++++ 3.185+++   2.334+     2.655++    2.508+     0.889       : p=16

提高性能的技巧?

  • 如果基准计算,基准计算和避免MEM-I/O

  • 如果进行基准测试,请始终检查整个景观,感觉(更好学习如何avoid/evict 任何此类偏离测量结果)缓存中 副作用

  • 始终避免任何形式的共享(可以避免 - 将处理映射到 tt[],但覆盖 "whole" tt[],缓存行一致块,以避免错误共享和 "at least" 重新使用已获取数据块中的任何数据您已经支付了 MEM-I/O 获取的费用(如果基于矢量的 LOAD/STORE 确实是必须的 - 见上文)

  • 允许并积极利用 FMA4/SIMD/AVX512 可用的任何 HPC 矢量化技巧