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)
元素数据放置例程
仅限C/C++:
shmem_int_p(3)
shmem_long_p(3)
shmem_short_p.*(3)
块数据放置例程
C/C++ 和 Fortran:
shmem_put32(3)
shmem_put64(3)
shmem_put128(3)
仅限C/C++:
shmem_short_put.*(3)
仅限Fortran:
shmem_complex_put(3)
shmem_integer_put(3)
shmem_logical_put(3)
shmem_real_put(3)
元素数据获取例程
仅限C/C++:
shmem_int_g(3)
shmem_long_g(3)
块数据获取例程
C/C++ 和 Fortran:
shmem_get32(3)
shmem_get64(3)
shmem_get128(3)
仅限C/C++:
仅限Fortran:
shmem_complex_get(3)
shmem_integer_get(3)
shmem_logical_get(3)
shmem_real_get(3)
跨步放置例程
C/C++ 和 Fortran:
shmem_iput32(3)
shmem_iput64(3)
仅限C/C++:
仅限Fortran:
shmem_complex_iput(3)
shmem_integer_iput(3)
shmem_logical_iput(3)
shmem_real_iput(3)
跨步获取例程
C/C++ 和 Fortran:
shmem_iget32(3)
shmem_iget64(3)
仅限C/C++:
仅限Fortran:
shmem_complex_iget(3)
shmem_integer_iget(3)
shmem_logical_iget(3)
shmem_real_iget(3)
点对点同步例程
仅限C/C++:
Fortran语言:
shmem_int4_wait(3)
shmem_int4_wait_until(3)
shmem_int8_wait(3)
shmem_int8_wait_until(3)
屏障同步例程
C/C++ 和 Fortran:
原子内存获取并操作(fetch-op)例程
C/C++ 和 Fortran:
归约例程
仅限C/C++:
仅限Fortran:
shmem_int4_and_to_all(3)
shmem_int8_and_to_all(3)
shmem_real4_max_to_all(3)
shmem_real8_max_to_all(3)
shmem_int4_max_to_all(3)
shmem_int8_max_to_all(3)
shmem_real4_min_to_all(3)
shmem_real8_min_to_all(3)
shmem_int4_min_to_all(3)
shmem_int8_min_to_all(3)
shmem_real4_sum_to_all(3)
shmem_real8_sum_to_all(3)
shmem_int4_sum_to_all(3)
shmem_int8_sum_to_all(3)
shmem_real4_prod_to_all(3)
shmem_real8_prod_to_all(3)
shmem_int4_prod_to_all(3)
shmem_int8_prod_to_all(3)
shmem_int4_or_to_all(3)
shmem_int8_or_to_all(3)
shmem_int4_xor_to_all(3)
shmem_int8_xor_to_all(3)
广播例程
C/C++ 和 Fortran:
缓存管理例程
C/C++ 和 Fortran:
字节粒度块放置例程
C/C++ 和 Fortran
shmem_putmem(3)
shmem_getmem(3)
仅限Fortran:
shmem_character_put(3)
shmem_character_get(3)
收集例程
C/C++ 和 Fortran:
原子内存获取并操作(fetch-op)例程
仅限C/C++:
仅限Fortran:
shmem_int4_cswap(3)
shmem_int4_fadd(3)
shmem_int4_finc(3)
shmem_int4_swap(3)
shmem_int8_swap(3)
shmem_real4_swap(3)
shmem_real8_swap(3)
shmem_int8_cswap(3)
原子内存操作例程
仅限Fortran:
shmem_int4_add(3)
shmem_int4_inc(3)
远程内存指针函数
C/C++ 和 Fortran:
shmem_ptr(3)
归约例程
仅限C/C++:
仅限Fortran:
shmem_real16_max_to_all(3)
shmem_real16_min_to_all(3)
shmem_real16_prod_to_all(3)
shmem_real16_sum_to_all(3)
可访问性查询例程
C/C++ 和 Fortran:
对称数据对象
与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_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)