18.2.4. intro_shmem

intro_shmem - OpenSHMEM编程模型简介

18.2.4.1. 描述

SHMEM编程模型由一系列库例程组成,旨在为高度并行化的可扩展程序提供低延迟、高带宽的通信功能。OpenSHMEM应用程序编程接口(API)中的例程为协作并行进程之间的数据交换提供了一种编程模型。由此产生的程序在风格上类似于消息传递接口(MPI)程序。SHMEM API既可以单独使用,也可以与同一并行程序中的MPI例程结合使用。

OpenSHMEM程序采用SPMD(单程序多数据)风格。 称为处理单元或PE的SHMEM进程同时启动并运行相同的程序。通常,PE在问题的较大子域上执行计算,并定期与其他PE通信以交换下一计算阶段所依赖的信息。

OpenSHMEM例程旨在最小化与数据传输请求相关的开销,最大化带宽并降低数据延迟。数据延迟是指从某个处理单元(PE)发起数据传输开始,到目标PE能够使用该数据为止的时间段。OpenSHMEM例程通过以下方式支持远程数据传输:put操作(将数据传输至其他PE)、get操作(从其他PE获取数据)以及远程指针(允许直接引用其他PE拥有的数据对象)。此外还支持集体广播与归约操作、屏障同步以及原子内存操作。原子内存操作是指对远程或本地数据对象执行的原子读写更新操作,例如获取并递增(fetch-and-increment)操作。

18.2.4.2. OPENSHMEM 例程

本节列出了重要的OpenSHMEM消息传递例程。

PE查询

  • 仅限C/C++:

    • _num_pes(3)

    • _my_pe(3)

  • 仅限Fortran:

    • NUM_PES(3)

    • MY_PE(3)

元素数据放置例程

块数据放置例程

元素数据获取例程

块数据获取例程

跨步放置例程

跨步获取例程

点对点同步例程

屏障同步例程

原子内存获取并操作(fetch-op)例程

归约例程

广播例程

缓存管理例程

字节粒度块放置例程

收集例程

原子内存获取并操作(fetch-op)例程

原子内存操作例程

  • 仅限Fortran:

    • shmem_int4_add(3)

    • shmem_int4_inc(3)

远程内存指针函数

归约例程

可访问性查询例程

对称数据对象

与OpenSHMEM编程模型的SPMD特性一致的是对称数据对象的概念。这些数组或变量在所有PE上具有相同的大小、类型和相对地址。对称数据对象的另一个术语是"可远程访问的数据对象"。在OpenSHMEM数据传输例程的接口定义中,通常要求一个或多个参数是对称或可远程访问的。

以下类型的数据对象是对称的:

  • Fortran数据对象位于公共块或具有SAVE属性的情况。这些数据对象不得在动态共享对象(DSO)中定义。

  • 非堆栈的C和C++变量。这些数据对象不得在DSO中定义。

  • 使用shpalloc(3)分配的Fortran数组

  • shmalloc(3)分配的C和C++数据

Collective Routines

部分SHMEM例程,例如shmem_broadcast(3)和 shmem_float_sum_to_all(3),被归类为集合例程, 因为它们在一组PE之间分配工作。这些例程必须由 PE_start、logPE_stride、PE_size参数三元组定义的活动集合中的所有PE 同时调用。以下手册页描述了OpenSHMEM集合例程:

  • shmem_and(3)

  • shmem_barrier(3)

  • shmem_broadcast(3)

  • shmem_collect(3)

  • shmem_max(3)

  • shmem_min(3)

  • shmem_or(3)

  • shmem_prod(3)

  • shmem_sum(3)

  • shmem_xor(3)

18.2.4.3. 使用对称工作数组 PSYNC

如果某个PE在未经过屏障同步的情况下连续两次调用OpenSHMEM集合例程,通常需要多个pSync数组。若调用2的活动集中某些PE在调用1的活动集所有PE完成处理前就到达调用2,则会出现问题。您可以使用shmem_barrier(3)或shmem_barrier_all(3)在连续调用OpenSHMEM集合例程之间执行屏障同步。

有两种特殊情况:

  • shmem_barrier(3) 例程允许在连续调用中使用相同的 pSync 数组,只要活动的 PE 集合保持不变。

  • 如果使用相同的活动集多次调用同一个集体例程,这些调用可能会在两个pSync数组之间交替进行。SHMEM例程确保在所有PE上开始处理第三次调用之前,第一次调用已由所有PE完全完成。

由于SHMEM例程会将pSync恢复为其原始内容,因此使用相同pSync数组的多次调用不需要在第一次调用后重新初始化pSync。

18.2.4.4. SHMEM环境变量

本节列出了重要的SHMEM环境变量。

  • SMA_VERSION 在启动时打印库版本信息。

  • SMA_INFO 打印关于所有这些环境变量的帮助文本。

  • SMA_SYMMETRIC_SIZE 为对称堆分配的字节数。

  • SMA_DEBUG 启用调试消息。

首次调用SHMEM必须使用start_pes(3)。这个例程用于初始化SHMEM运行时环境。

在此之前调用任何其他SHMEM例程会导致未定义行为。 不允许多次调用此例程。

18.2.4.5. 编译和运行OPENSHMEM程序

OpenSHMEM规范对于如何编译、链接和运行OpenSHMEM程序没有明确规定。本节展示了一些示例,说明如何使用包装器程序来编译和启动应用程序。这些命令的样式借鉴了许多MPI实现中的包装器程序。

以下示例命令行展示了如何使用包装脚本(本例中为oshrun)运行OpenSHMEM程序:

  • C/C++:

oshcc c_program.c
  • FORTRAN:

oshfort fortran_program.f

以下示例命令行展示了如何运行一个OpenSHMEM程序,假设该库为此目的提供了一个包装脚本(本例中命名为oshrun):

oshrun -n 32 ./a.out

18.2.4.6. 示例

示例1: 以下Fortran OpenSHMEM程序指示所有PE同时对所有PE中VALUES变量的数值进行求和:

PROGRAM REDUCTION
  REAL VALUES, SUM
  COMMON /C/ VALUES
  REAL WORK

  CALL START_PES(0)
  VALUES = MY_PE()
  CALL SHMEM_BARRIER_ALL ! Synchronize all PEs
  SUM = 0.0
  DO I = 0, NUM_PES()-1
    CALL SHMEM_REAL_GET(WORK, VALUES, 1, I) ! Get next value
    SUM = SUM + WORK                ! Sum it
  ENDDO
  PRINT *, 'PE ', MY_PE(), ' COMPUTED SUM=', SUM
  CALL SHMEM_BARRIER_ALL
END

示例2:以下C语言OpenSHMEM程序将10个长整型数组从PE 0传输到PE 1:

#include <mpp/shmem.h>

main() {
  long source[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  static long target[10];

  shmem_init();
  if (shmem_my_pe() == 0) {
    /* put 10 elements into target on PE 1 */
    shmem_long_put(target, source, 10, 1);
  }
  shmem_barrier_all(); /* sync sender and receiver */
  if (shmem_my_pe() == 1)
    printf("target[0] on PE %d is %d\n", shmem_my_pe(), target[0]);
}

另请参阅

以下手册页也包含有关OpenSHMEM例程的信息。有关具体实现信息,请参阅相应手册页。

shmem_add(3) shmem_and(3) shmem_barrier(3) shmem_barrier_all(3) shmem_broadcast(3) shmem_cache(3) shmem_collect(3) shmem_cswap(3) shmem_fadd(3) shmem_fence(3) shmem_finc(3) shmem_get(3) shmem_iget(3) shmem_inc(3) shmem_iput(3) shmem_lock(3) shmem_max(3) shmem_min(3) shmem_my_pe(3) shmem_or(3) shmem_prod(3) shmem_put(3) shmem_quiet(3) shmem_short_g(3) shmem_short_p(3) shmem_sum(3) shmem_swap(3) shmem_wait(3) shmem_xor(3) shmem_pe_accessible(3) shmem_addr_accessible(3) shmem_init(3) shmem_malloc(3) shmem_my_pe(3) shmem_n_pes(3)