• 超越openmp通用核心的硬件


    目录

    NUMA:非同一内存访问

    控制线程亲和力:

    嵌套并行构造:

    检查线程亲和力:

    SIMD:

    主机-设备模式:


    NUMA:非同一内存访问

    数据局部性:

    图12-6:有和没有首次接触

    1. //Step 1.a Initialization by initial thread only
    2. for (j = 0; j < VectorSize; j++) {
    3. a[j] = 1.0; b[j] = 2.0; c[j] = 0.0;}
    4. //Step 1.b Initialization by all threads (first touch)
    5. omp_set_dynamic(0);
    6. #pragma omp parallel for schedule(static)
    7. for (j = 0; j < VectorSize; j++) {
    8. a[j] = 1.0; b[j] = 2.0; c[j] = 0.0;}
    9. //Step 2 Compute
    10. #pragma omp parallel for schedule(static)
    11. for (j = 0; j < VectorSize; j++) {
    12. a[j] = b[j] + d * c[j];}

    控制线程亲和力:

    place:线程对应的硬件资源

    定义place : export OMP_PLACES="{0,1,2,3},{4,5,6,7}"

    export OMP_PLACES="{0:4},{4:4}"

    export OMP_PLACES=threads(以硬件线程的粒度绑定openmp线程),cores,sockets

    控制处理器绑定:OMP_PROC_BIND true,false,master,close(组中的线程被放置在靠近主线程的place,线程以循环的方式从主线程的右边位置开始被分配到连续的place),spread(将线程尽可能均匀的分布在各个place上)

    嵌套并行构造:

    通过嵌套并行构造来影响NUMA系统中的线程分布:

    export OMP_NESTED=true(openmp5.0弃用)

    export OMP_MAX_ACTIVATE_LEVELS=3

    1. #include
    2. #include
    3. void report_num_threads(int level)
    4. {
    5. #pragma omp single
    6. {
    7. printf("Level %d: number of threads in the team: %d\n", \
    8. level, omp_get_num_threads());
    9. }
    10. }
    11. int main()
    12. {
    13. omp_set_dynamic(0);
    14. #pragma omp parallel num_threads(2)
    15. {
    16. report_num_threads(1);
    17. #pragma omp parallel num_threads(2)
    18. {
    19. report_num_threads(2);
    20. #pragma omp parallel num_threads(2)
    21. {
    22. report_num_threads(3);
    23. }
    24. }
    25. }
    26. return(0);
    27. }

    OMP_NUM_THREADS,OMP_PLACES,OMP_PROC_BIND环境变量被扩展为支持嵌套:

    export OMP_PLACES=sockets,threads

    export OMP_NUM_THREADS=2,4

    export OMP_PROC_BIND=spread,close

    程序开始时,有一个初始线程运行在核心0的第一个硬件线程上,我们遇到第一个并行区域,并使用OMP_NUM_THREADS和OMP_PROC_BIND的第一个值(2,spread)。我们创建两个线程,每个插槽上一个。注意,线程可以运行在任何核心和定义place的任何硬件线程上,本例中是socket,意味着它们可以运行在各自插槽的任何核心上。在创建一个并行区域后,内部控制变量会进入列表的下个值,4代表线程数,close代表处理器绑定。当每个线程遇到嵌套的并行区域时,他们会在同一核心上创建4个线程。

    检查线程亲和力:

    OMP_DISPLAY_AFFINITY

    OMP_AFFINITY_FORMAT

    图12-12:

    1. $ icc -qopenmp -DNTIMES=20 -DSTREAM_ARRAY_SIZE=64000000 -c stream.c
    2. $ icc -qopenmp -o stream stream.o
    3. $ export OMP_DISPLAY_AFFINITY=true
    4. $ export OMP_AFFINITY_FORMAT="Thrd Lev=%3L, thrd_num=%5n, thrd_aff=%15A"
    5. $ export OMP_PLACES=threads
    6. $ export OMP_NUM_THREADS=8
    7. $ export OMP_PROC_BIND=spread
    8. $ ./stream | sort -k3
    9. Thrd Lev=1 , thrd_num=0 , thrd_aff=0
    10. Thrd Lev=1 , thrd_num=1 , thrd_aff=8
    11. Thrd Lev=1 , thrd_num=2 , thrd_aff=16
    12. Thrd Lev=1 , thrd_num=3 , thrd_aff=24
    13. Thrd Lev=1 , thrd_num=4 , thrd_aff=1
    14. Thrd Lev=1 , thrd_num=5 , thrd_aff=9
    15. Thrd Lev=1 , thrd_num=6 , thrd_aff=17
    16. Thrd Lev=1 , thrd_num=7 , thrd_aff=25
    17. $ export OMP_PROC_BIND=close
    18. $ ./stream |sort -k3
    19. Thrd Lev=1 , thrd_num=0 , thread_aff=0
    20. Thrd Lev=1 , thrd_num=1 , thread_aff=32
    21. Thrd Lev=1 , thrd_num=2 , thread_aff=2
    22. Thrd Lev=1 , thrd_num=3 , thread_aff=34
    23. Thrd Lev=1 , thrd_num=4 , thread_aff=4
    24. Thrd Lev=1 , thrd_num=5 , thread_aff=36
    25. Thrd Lev=1 , thrd_num=6 , thread_aff=6
    26. Thrd Lev=1 , thrd_num=7 , thread_af=38

    线程亲和力和数据局部性:

    一般建议是每个NUMA域至少有一个进程(列入单个MPI rank或OS进程)。让Openmp线程对应一个NUMA域内的并行,将所需数据保持在同一个NUMA域内。这就减少了跨NUMA域边界时,正确初始化首次接触的任何错误的影响。另一个建议是将线程相隔很远(spread)以利用聚集的内存带宽,然后将最里面close的工作在嵌套的并行区域内分叉,以最大化缓存局部性。

    SIMD

    图12-17:

    openmp对PI程序进行向量化:

    1. #include
    2. #include
    3. static long num_steps = 100000;
    4. float step;
    5. int main ()
    6. {
    7. int i;
    8. float x, pi, sum = 0.0;
    9. step = 1.0f / (double) num_steps;
    10. #pragma omp simd private(x) reduction(+:sum)
    11. for (i = 0; i < num_steps; i++) {
    12. x = (i + 0.5f) * step;
    13. sum += 4.0f / (1.0f + x * x);
    14. }
    15. pi = step * sum;
    16. printf("pi=%lf \n", pi);
    17. }

    图12-18:

    openmp对PI程序进行多线程和向量化:

    1. #include
    2. #include
    3. static long num_steps = 100000000;
    4. double step;
    5. int main ()
    6. {
    7. int i;
    8. double x, pi, sum = 0.0;
    9. step = 1.0f / (double) num_steps;
    10. #pragma omp parallel for simd private(x) reduction(+:sum)
    11. for (i = 0; i < num_steps; i++) {
    12. x = (i + 0.5f) * step;
    13. sum += 4.0f / (1.0f + x * x);
    14. }
    15. pi = step * sum;
    16. printf("pi=%f\n",pi);
    17. }

    主机-设备模式:

    target指令及其相关的结构化块定义了卸载到设备上执行的目标区域。target指令还导致数据移动到设备上,当目标区域完成执行时这些数据将从设备副指挥主机

    图12-19:

    1. #include
    2. #include
    3. #define N 1024
    4. int main()
    5. {
    6. float a[N], b[N], c[N];
    7. int i;
    8. // initialize a, b, and c (code not shown)
    9. #pragma omp target
    10. #pragma omp teams distribute parallel for simd
    11. for (i = 0;i < N; i++)
    12. c[i] += a[i] * b[i];
    13. }

    对于GPU来说target指令后面加上以下指令及其相关循环:

    #pragma omp teams distribute parallel for simd

    map子句:

    图12-21:

    1. #include
    2. #include
    3. #include
    4. #define N 1024
    5. int main()
    6. {
    7. float *a, *b, *c, *d;
    8. int i;
    9. a = (float*) malloc(N * sizeof(float));
    10. b = (float*) malloc(N * sizeof(float));
    11. c = (float*) malloc(N * sizeof(float));
    12. d = (float*) malloc(N * sizeof(float));
    13. // initialize a, b, c, and d (code not shown)
    14. #pragma omp target map(to:a[0:N],b[0:N]) map(tofrom:c[0:N])
    15. #pragma omp teams distribute parallel for simd
    16. for (i = 0; i < N;i++)
    17. c[i] += a[i] * b[i];
    18. #pragma omp target map(to:a[0:N],c[0:N]) map(tofrom:d[0:N])
    19. #pragma omp teams distribute parallel for simd
    20. for (i = 0; i < N; i++)
    21. d[i] += a[i] + c[i];
    22. }

    图12-22:

    1. #include
    2. #include
    3. #include
    4. #define N 1024
    5. int main()
    6. {
    7. float *a, *b, *c, *d;
    8. int i;
    9. a = (float*)malloc(N*sizeof(float));
    10. b = (float*)malloc(N*sizeof(float));
    11. c = (float*)malloc(N*sizeof(float));
    12. d = (float*)malloc(N*sizeof(float));
    13. // initialize a, b, c, and d (code not shown)
    14. #pragma omp target data map(to:a[0:N],b[0:N],c[0:N]) map(tofrom:d[0:N])
    15. {
    16. #pragma omp target
    17. #pragma omp teams distribute parallel for simd
    18. for (i = 0; i < N; i++)
    19. c[i] += a[i] * b[i];
    20. #pragma omp target
    21. #pragma omp teams distribute parallel for simd
    22. for (i = 0; i < N; i++)
    23. d[i] += a[i] + c[i];
    24. }
    25. // continue in the program but only using d (not c)
    26. }

  • 相关阅读:
    美国访问学者申请|签证需要哪些材料?
    SpringBoot 源码分析(四) 内置Tomcat分析
    HTTPS 如何保证数据传输安全
    Sentry Relay 二次开发调试简介
    期货开户风险因素都考虑清楚
    手动编译GDB
    yarn的介绍、安装及使用
    Unity入门教程||创建项目(上)
    H3C防火墙安全授权导入
    golang 工程组件:grpc-gateway 环境安装+默认网关测试
  • 原文地址:https://blog.csdn.net/qq_52758467/article/details/133819208