0%

太初元碁SDAA C 编程模型

Teco异构并行计算平台

1757593943368
Teco异构并行模型采用主从异构的物理架构,即Host-Device运行模式。
1757593952590

SPMD于SDAA C

SPMD(Single Program Multiple Data,单程序多数据),是一种用于任务并行的编程范式。其本质是将一个问题分解成若干个子问题,然后对其并行求解。

SDAA C编程模型遵循SPMD编程范式,同一计算核心阵列SPA内所有计算核心SPE运行同一份应用程序,每个SPE可以独立完成对子问题的并行求解。其核心在于对复杂计算任务的切分,并合理地将计算任务分配到不同的SPE内进行计算。

1757593963537

SDAA C编程模型提供了threadIdxthreadDim为每个SPE定制计算任务。其中:

  • threadIdx:当前计算核心SPE的ID号,可以通过该关键字为目标SPE分配计算任务。

  • threadDim:获取同一计算核心阵列SPA内计算核心SPE的总数。

编程示例

以数组计算为例,将计算任务分配到同一SPA的所有SPE内并行执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using namespace sdaa;
#define SIZE 67

__device__ int g_data[SIZE];
__global__ void func()
{
// 初始化原始数据
for (int i = 0; i < SIZE; i++) {
g_data[i] = i;
}

// SPE同步,确保所有的SPE都完成了数据的初始化
sync_threads();

// 可用的计算资源超过计算量时,每个SPE承担一次计算
if (threadDim >= SIZE) {
if (threadIdx < SIZE) {
g_data[threadIdx]++;
}
} else {
// 可用计算资源少于计算量时,每个SPE需承担多次计算
// 例如当前计算量为67,假如共有32个SPE参与计算。则每个SPE需要至少承担两次计算任务
int cal_time = SIZE / threadDim;
for (int i = 0; i < cal_time; i++) {
g_data[i * threadDim + threadIdx]++;
}

// 对于剩余的计算量使用部分SPE承担
// 例如当前的计算量为67,共有32个SPE参与计算,每个SPE计算两次后,还剩余3次。则使用0, 1, 2三个SPE完成剩余的计算任务
int remain_time = SIZE % threadDim;
if ((threadIdx + 1) <= remain_time) {
g_data[cal_time * threadDim + threadIdx]++;
}
}

// SPE同步,确保所有的SPE都完成了子问题的求解
sync_threads();

// 打印输出计算结果
for (int i = 0; i < SIZE; i++) {
printf("SPE ID = %lu, g_data[%d] = %d\n", threadIdx, i, g_data[i]);
}
}

上述应用程序中就通过SPMD编程范式完成对复杂任务的求解。

  • SP(Single Program,单程序):所有参与计算的SPE都执行同一份代码,在本例中为func函数。

  • MD(Multiple Data,多数据):通过threadIdx将数组进行切分,每个SPE仅需完成少量运算。

    在本例中:SIZE为67,假如可用计算资源为32个SPE,计算资源少于计算量,每个SPE需要承担多次计算,如:SPE0需要计算数据g_data[0]、g_data[32]和g_data[64]、SPE1需要计算数据g_data[1]、g_data[33]和g_data[65]等。

1757593977134

图2 使用SPMD编程范式完成数组计算任务

具体计算任务的代码片段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 可用计算资源少于计算量时,每个SPE需承担多次计算
// 例如当前计算量为67,共有32个SPE参与计算。则每个SPE需要至少承担两次计算任务
int cal_time = SIZE / threadDim;
for (int i = 0; i < cal_time; i++) {
g_data[i * threadDim + threadIdx]++;
}

// 对于剩余的计算量使用部分SPE承担
// 例如当前的计算量为67,共有32个SPE参与计算,每个SPE计算两次后,还剩余3次。则使用0, 1, 2三个SPE完成剩余的计算任务
int remain_time = SIZE % threadDim;
if ((threadIdx + 1) <= remain_time) {
g_data[cal_time * threadDim + threadIdx]++;
}

代码中涉及部分关键字含义如下:

  • __device__:运行空间标识符,修饰Device端的函数或变量,在Device端调用、运行,可参考device

  • __global__:函数标识符,修饰Kernel函数,可参考global

  • sync_threads:对同一计算核心阵列SPA内的所有或部分计算核心SPE进行同步操作,可参考sync_threads

  • printf:打印接口,可参考printf