8. C++语言扩展¶
8.1. 函数执行空间指示符 (Function Execution Space Specifiers)¶
函数执行空间指示符表示函数是在主机上执行还是在设备上执行,以及它是否可从主机或设备调用。
__global__¶
__global__
执行空间指示符声明一个函数为入口核函数。这样的函数可以:
在计算设备上执行
仅可从主机端程序调用
一个 __global__
函数必须具有 void 返回类型,并且不能是类的成员函数。
对 __global__
函数的任何调用都必须按照执行配置的描述进行指定。
对 __global__
函数的调用总是异步的,这意味着在设备完成执行之前它就会返回。
__device__¶
__device__
执行空间指示符声明一个函数:
在计算设备上执行
仅可从设备端调用,也是从其他
__global__
或__device__
核函数调用
__global__
和__device__
执行空间指示符不能用来同时修饰同一个函数。
__host__¶
__host__
执行空间指示符声明一个函数:
在主机端上执行
仅可从主机端程序调用
当一个函数不加任何执行空间指示符时,就等同于加了__host__
指示符,此时该函数只会被编译到主机端程序中。
__global__
和__host__
执行空间指示符不能用来同时修饰同一个函数。
__device__
和__host__
执行空间指示符可以用来同时修饰同一个函数,表示这个函数既可以在主机端被调用,也可以在设备端被调用。预定义宏__AGCU_ARCH__
可以用来区分不同的设备端和主机端代码。
__host__ __device__ void func(void) {
#if __AGCU_ARCH__ >= 300
// 兼容AGCU300的设备端代码
#elif __AGCU_ARCH__ >= 200
// 兼容AGCU200的设备端代码
#elif !defined(__AGCU_ARCH__)
// 主机端代码
#endif
}
__cooperative__¶
__cooperative__
指示符必须和__global__
一起使用,声明一个核函数:
所有线程块必须以协作方式同时启动
因而可以在整个线程网格内进行同步和通信
注意:被标记为__cooperative__
的核函数,启动时线程块的数量不能超过当前设备上的计算单元集群数量。
8.2. 变量内存空间指示符(Variable Memory Space Specifiers)¶
变量内存空间指示符表示设备上变量的内存位置。
TOPS C++内存模型中定义了以下内存空间:通用内存空间、全局内存空间、共享内存空间、本地内存空间和常量内存空间。变量内存空间指示符可用于C/C++内置类型变量的定义,也可以用于结构体和对象的定义,以指定所分配的内存空间。TOPS C++扩展了C/C++语法的类型指示符,将内存空间名称作为有效的类型指示符。
在设备端代码中,一个不加__device__
、__shared__
和__constant__
变量内存空间指示符的自动变量,对应到私有内存空间(Private Memory Space),且大概率会被编译器优化到寄存器中。当不使用变量内存空间指示符定义变量时:
将会分配在线程的私有内存空间中(容量限制参考《AGCU技术参数》中的每个线程的最大堆栈容量)
其生命期等同于所属线程的生命期
在不同的线程中对应不同的内存实例
只能被一个线程访问
每个线程中的私有变量可能会被分配到不同的内存地址上
定义指针时,如果没有显式声明内存空间指示符,则指针默认指向通用内存空间。内存空间A的指针只能赋值给指向和内存空间A相同内存空间的指针,或者父内存空间的指针。如果指针A、B的内存空间类型不相同,且B不是父内存空间类型,则将内存空间A的指针强制转换为内存空间B的指针是非法的。在TOPS C++中,除了常量内存空间,其它内存空间都是通用内存空间的子空间。
函数形参中包含指针时,不可以加内存空间指示符,默认指向通用内存空间。
__device__¶
变量内存空间指示符__device__
表示对应的变量内存在设备内存中。
__device__
可以和后续的指示符一起使用。当单独使用__device__
指示符定义变量时:
将会分配在全局内存空间中
其生命期等同于设备上下文的生命期
在不同的设备上对应不同的内存实例
既可以在一个计算网格中的所有线程中访问,也可以通过运行时库的API从主机端访问(
topsGetSymbolAddress
/topsGetSymbolSize
/topsMemcpyToSymbol
/topsMemcpyFromSymbol
)
注意:变量内存空间指示符__device__
和函数执行空间指示符__device__
是不同的。
定义设备端的全局变量:
__device__ float global_var = 1.0f;
__device__ void kernel_func(void) {
printf("%f\n", global_var);
}
声明设备端的全局变量:
extern __device__ float extern_global_var;
__device__ void kernel_func(void) {
printf("%f\n", extern_global_var);
}
定义设备端的静态变量:
static __device__ float static_var = 1.0f;
__device__ void kernel_func(void) {
static __device__ static_var2 = 1.0f;
printf("%f %f\n", static_var, static_var2);
}
在主机端设置__device__
变量:
__device__ float device_var;
void test(void) {
float host_var = 1.0f;
topsMemcpyToSymbol(&device_var, &host_var, sizeof(host_var));
}
__constant__¶
当使用变量内存空间指示符__constant__
定义变量时:
将会分配在设备端的常量内存空间中(容量限制参考《AGCU技术参数》中的每个核函数的最大常量数据段容量)
其生命期等同于设备上下文的生命期
在不同的设备上对应不同的内存实例
既可以在一个计算网格中的所有线程中访问,也可以通过运行时库的API从主机端访问(
topsGetSymbolAddress
/topsGetSymbolSize
/topsMemcpyToSymbol
/topsMemcpyFromSymbol
)
定义设备端的全局常量:
__constant__ float const_var = 1.0f;
__device__ void kernel_func(void) {
printf("%f\n", const_var);
}
指示符__constant__
可以和指示符__device__
一起使用。
__device__ __constant__ float device_const_var = 1.0f;
__device__ void kernel_func(void) {
printf("%f\n", device_const_var);
}
在主机端设置__constant__
变量:
__constant__ float const_var;
void host_func(void) {
float host_var = 1.0f;
topsMemcpyToSymbol(&const_var, &host_var, sizeof(host_var));
}
__local__¶
当使用变量内存空间指示符__local__
定义变量时:
将会分配在线程的本地内存空间中(容量限制参考《AGCU技术参数》中的每个线程的最大本地内存容量)
其生命期等同于所属线程的生命期
在不同的线程中对应不同的内存实例
只能被一个线程访问
每个线程中的本地变量可能会被分配到不同的内存地址上
本地内存主要是用来存放参与计算的向量和张量数据
定义一个核函数内部的本地变量:
__device__ void kernel_func(void) {
__local__ __vector float shared_var[128];
}
__restrict__¶
__restrict__
关键字可以用来定义受限指针(restrict pointer)。受限指针一般和const修饰符一起使用,用来标记函数的形参指针在函数体内部不会存在别名。比如以下代码中,指针a指向的值不能缓存在寄存器中,每次都必须从指针指向的内存中读入,否则计算结果是错误的。
__device__ void kernel_func(const float* a, const float* b, float* c) {
*a++;
*b++;
*c = *a + *b;
}
__device__ void kernel_func2(void) {
float a = 1.0f;
kernel_func(&a, &a, &a);
}
以下代码使用__restrict__
修饰了形参指针,告诉编译器指针a在函数kernel_func中不会存在别名:
__device__ void kernel_func(const float* __restrict__ a, const float* __restrict__ b, float* c) {
*a++;
*b++;
*c = *a + *b;
}
__device__ void kernel_func2(void) {
float a = 1.0f;
float b = 1.0f;
float c = 0.0f;
kernel_func(&a, &b, &c);
}
注意:在加上__restrict__
指示符后,编译器并不会检查调动时的实参是否指向了相同的内存空间,需要开发者自己保证这一点。
注意:开发者应该尽量将函数形参指针标记为const __restrict__
,这样可以给编译器和硬件更多的优化空间,降低访问内存的频率,并且可以降低寄存器分配压力。
转换规则¶
允许非通用地址空间的指针到通用地址空间指针的隐式转换
__local__ char* ptr0;
char* ptr1 = ptr0;
通用地址空间指针向非通用地址空间的指针转换必须要显式强制转换
char* ptr0;
__local__ char* ptr1 = (__local__ char*) ptr0;
非通用地址空间之间不能互相转换
__local__ char* ptr0;
// 以下代码编译器会报错
__shared__ char* ptr1 = (__shared__ char*) ptr0;
8.3. 内置向量类型和操作 (Built-in Vector Types & Operators)¶
内置向量类型 (Built-in Vector Types)¶
TOPS C++内置了向量类型,以方便开发者进行向量运算。在定义向量类型时,类型指示符和内存空间指示符位于定义中的关键字__vector
(或__vector2
__vector4
)之前。以下代码中提供了带__vector
(或__vector2
__vector4
)关键字 语法的大多数合法形式。为了清晰起见,图中省略了一些变体:类型指示符(如const
)和内存类指示符(如static
)可以在声明中以任何顺序出现,只要两者都不紧跟在关键字__vector
(或__vector2
__vector4
)之后即可。
__device__ void test(void) {
// 定义一个包含128个int的向量
__vector4 int v1;
// 定义一个包含128个bool的向量
__vector4 bool int v2;
// 定义一个包含256个short的向量
__vector4 short v3;
// 定义一个包含256个bool的向量
__vector4 bool short v4;
-}
关键字 |
GCU210 |
GCU300 |
---|---|---|
__vector |
128 B |
128 B |
__vector2 |
256 B |
256 B |
__vector4 |
512 B |
512 B |
注意:当前版本的TOPS C++还不支持在GCU210上使用向量类型以及相关运算,只可以在GCU300上使用。
向量类型 |
向量长度 |
---|---|
|
包含32个int |
|
包含32个unsigned int |
|
包含64个short |
|
包含64个unsigned short |
|
包含128个char |
|
包含128个unsigned char |
|
包含32个float |
|
包含64个__fp16 |
|
包含64个__bf16 |
|
包含64个int |
|
包含64个unsigned int |
|
包含128个short |
|
包含128个unsigned short |
|
包含256个char |
|
包含256个unsigned char |
|
包含64个float |
|
包含128个__fp16 |
|
包含128个__bf16 |
|
包含128个int |
|
包含128个unsigned int |
|
包含256个short |
|
包含256个unsigned short |
|
包含512个char |
|
包含512个unsigned char |
|
包含128个long |
|
包含128个unsigned long |
|
包含128个float |
|
包含256个__fp16 |
|
包含256个__bf16 |
|
包含32个bool |
|
包含32个bool |
|
包含64个bool |
|
包含64个bool |
|
包含128个bool |
|
包含128个bool |
|
包含128个bool |
|
包含64个bool |
|
包含128个bool |
|
包含128个bool |
|
包含256个bool |
|
包含256个bool |
|
包含128个bool |
|
包含128个bool |
|
包含256个bool |
|
包含256个bool |
|
包含512个bool |
|
包含512个bool |
|
包含128个bool |
|
包含128个bool |
向量运算符 (Built-in vector Operators)¶
双目运算符 (Binary Operators)¶
运算符 |
功能描述 |
支持的数据类型 |
---|---|---|
|
加法运算 |
支持所有相同类型的标量和向量操作 |
|
减法运算 |
支持所有相同类型的标量和向量操作 |
|
减法运算 |
支持所有相同类型的标量和向量操作 |
|
逻辑左移运算 |
支持所有无符号整型标量和向量操作 |
|
逻辑右移运算 |
支持所有无符号整型标量和向量操作 |
单目运算符 (Unary Operators)¶
运算符 |
功能描述 |
支持的数据类型 |
---|---|---|
|
获取向量中某个元素,或者修改向量中某个元素的值。(注:后续会添加越界检查) |
支持所有向量类型 |
|
负号运算 |
支持有符号数据类型向量 |
|
自增运算 |
支持所有向量类型 |
|
自减运算 |
支持所有向量类型 |
|
获取类型大小(单位:字节) |
支持所有向量类型 |
比较运算符 (Comparison Operators)¶
对于比较运算,输入为标量时返回布尔值,输入为向量时返回对应的掩码整型向量(maskbit vector)。
运算符 |
功能描述 |
支持的数据类型 |
---|---|---|
|
等于运算 |
支持所有标量和向量类型 |
|
不等于运算 |
支持所有标量和向量类型 |
|
小于运算 |
支持所有标量和向量类型 |
|
小于等于运算 |
支持所有标量和向量类型 |
|
大于运算 |
支持所有标量和向量类型 |
|
大于等于运算 |
支持所有标量和向量类型 |
位运算符 (Bitwise Operators)¶
运算符 |
功能描述 |
支持的数据类型 |
---|---|---|
|
求与运算 |
无符号整型向量、掩码整型向量 |
|
求或运算 |
无符号整型向量、掩码整型向量 |
|
异或运算 |
无符号整型向量、掩码整型向量 |
|
按位取反运算 |
无符号整型向量 |
|
将y求非后再与x求与 |
掩码整型向量 |
|
将y求非后再与x求或 |
掩码整型向量 |
|
与非运算 |
掩码整型向量 |
|
或非运算 |
掩码整型向量 |
|
异或然后再求反 |
掩码整型向量 |
备注1:上表中掩码整型向量包括如下数据类型:__vector bool int
, __vector bool short
, __vector bool char
, __vector2 bool int
, __vector2 bool short
, __vector2 bool char
, __vector4 bool int
, __vector4 bool short
, __vector4 bool char
。
备注2:上表中无符号整型向量包括如下数据类型:__vector unsigned char
, __vector unsigned short
, __vector unsigned int
, __vector2 unsigned char
, __vector2 unsigned short
, __vector2 unsigned int
, __vector4 unsigned char
, __vector4 unsigned short
, __vector4 unsigned int
。
8.4. 数学函数 (Mathmatical Functions)¶
函数 |
功能描述 |
支持的数据类型 |
---|---|---|
|
求绝对值运算 |
所有向量数据类型 |
|
求模运算 |
__vector4整型向量 |
|
除法运算 |
__vector4向量 |
|
获取符号信息 |
__vector4浮点向量 |
|
取整操作 |
__vector4浮点向量 |
|
对数函数 |
__vector4浮点向量、__vector浮点向量 |
|
返回log(x+1) |
__vector4浮点向量 |
|
求幂函数 |
__vector4浮点向量 |
|
平方根函数 |
__vector4浮点向量 |
|
求倒数函数 |
__vector浮点向量 |
|
平方根倒数函数 |
__vector4浮点向量、__vector浮点向量 |
|
指数函数 |
__vector4浮点向量、__vector浮点向量 |
|
返回exp(x)-1 |
__vector4浮点向量 |
|
判断有限值 |
__vector4浮点向量 |
|
向下取整函数 |
__vector4浮点向量 |
|
向上取整函数 |
__vector4浮点向量 |
|
正弦函数 |
__vector4浮点向量、__vector浮点向量 |
|
余弦函数 |
__vector4浮点向量 |
|
正切函数 |
__vector4浮点向量、__vector浮点向量 |
|
双曲正弦函数 |
__vector4浮点向量 |
|
双曲余弦函数 |
__vector4浮点向量 |
|
双曲正切函数 |
__vector4浮点向量、__vector浮点向量 |
|
反正弦函数 |
__vector4浮点向量 |
|
反余弦函数 |
__vector4浮点向量 |
|
反正切函数 |
__vector4浮点向量 |
|
返回以弧度表示的 x/y 的反正切 |
__vector4浮点向量 |
|
softplus激活函数 |
__vector4浮点向量、__vector浮点向量 |
|
sigmoid激活函数 |
__vector4浮点向量、__vector浮点向量 |
|
gelu激活函数 |
__vector4浮点向量、__vector浮点向量 |
备注:
上表中的__vector4整型向量包括如下数据类型:__vector4 int
, __vector4 short
, __vector4 char
, __vector4 unsigned int
, __vector4 unsigned short
, __vector4 unsigned char
。
上表中的__vector4浮点向量包括如下数据类型:__vector4 __bf16
, __vector4 __fp16
, __vector4 float
。
上表中的__vector4向量包括如下数据类型:__vector4 int
, __vector4 short
, __vector4 char
, __vector4 unsigned int
, __vector4 unsigned short
, __vector4 unsigned char
, __vector4 __bf16
, __vector4 __fp16
, __vector4 float
。
上表中的__vector浮点向量包括如下数据类型:__vector __bf16
, __vector __fp16
, __vector float
。
8.5. 内置变量 (Built-in Variables)¶
内建变量指定了线程网格和线程块的维度以及线程块和线程的索引。它们仅在在设备上执行的函数中有效。
gridDim¶
该变量的类型为dim3
,包含了线程网格的维度。
blockIdx¶
该变量的类型为uint3
,包含了线程块在线程网格中的索引。
blockDim¶
该变量的类型为dim3
,包含了线程块的维度。
threadIdx¶
该变量的类型为uint3
,包含了线程在线程块中的索引。
8.6. 内置宏定义 (Built-in Macro Definitions)¶
GCU_ARCH¶
GCU_ARCH是编译器内置宏,表示当前编译过程所针对的硬件架构版本,开发者可以直接在代码中使用。
宏定义 |
硬件架构 |
---|---|
GCU_ARCH = 210 |
GCU210 |
GCU_ARCH = 300 |
GCU300 |
AGCU_ARCH¶
AGCU_ARCH是编译器内置宏,表示当前编译过程所针对的抽象硬件架构版本,开发者可以直接在代码中使用。
宏定义 |
抽象硬件架构 |
实际硬件架构 |
---|---|---|
AGCU_ARCH = 200 |
AGCU200 |
GCU210和GCU300 |
AGCU_ARCH = 300 |
AGCU300 |
GCU300和下一代 |
8.7. 数据变换引擎 (Data Transformation Engine)¶
数据变换引擎简称DTE,是用于数据传输和转换(切片、反切片、转置等)的硬件引擎,提供满足计算引擎要求的数据。
DTE的特点包括:
独立于SIP进行工作
数据流向不需要寄存器的分配,数据流转的效率更高
节约了SIP的算力,数据传输和转换不需要SIP计算地址和初始化值
DTE上下文 (DTE Context)¶
DTE上下文用于维护DTE资源,对DTE上下文的基本操作包括:定义、初始化、释放。TOPS C++目前支持以下两种DTE上下文:
// 定义一个线程块共享的DTE
__shared__ tops_dte_ctx_t ctx;
// 定义一个线程私有的DTE
tops_dte_ctx_t ctx;
修饰符类型 |
可见性 |
占用资源 |
数据传输 |
---|---|---|---|
|
表示一个线程块内的所有线程共享的DTE上下文 |
使用线程块的DTE硬件资源 |
主要用作全局内存和共享内存之间的数据传输 |
无(不加任何修饰符) |
表示线程私有的DTE上下文 |
使用线程的DTE硬件资源 |
主要用作全局内存和本地内存、共享内存和本地内存之间的数据传输 |
// 初始化DTE上下文
tops_init_dte(&ctx);
// 释放DTE上下文
tops_destroy_dte(&ctx);
DTE的数据参数 (DTE Parameters)¶
DTE 操作相关接口都使用mdspan
类型作为配置数据参数。通过mdspan
数据结构来给设备地址附加额外的信息(如维度、形状、所属内存空间、总大小等),其构造函数的参数包括:内存空间(可选)、内存地址、形状维度。
声明mdspan
的两种方式:
// 方式 1:使用平铺展开形状维度的方式初始化形状
tops::mdspan src2((char*)(address), N, H, W, C);
// 方式 2:使用数组变量的方式初始化形状
int shape[] = {N, H, W, C};
tops::mdspan src4((char*)(address), shape);
DTE的操作 (DTE Operations)¶
DTE数据搬运操作接口一般是配置和启动合并的方式,通常至少包含两个参数:目标对象、源对象,目标对象和源对象类型统一使用mdspan数据类型。
简要起见,本小节介绍中,将目标对象简称为dst,源对象简称为src。
DTE支持两种启动方式:同步启动和异步启动。
同步启动: 使用同步DTE数据搬运接口(如,
memcpy()
)启动并等待
tops_dte_ctx_t ctx;
ctx.init();
tops::memcpy(ctx, dst, src);
ctx.destroy();
异步启动: 使用异步DTE数据搬运接口(如,
memcpy_async()
)启动,会返回一个tops::event
,可以使用tops::wait
来同步等待tops::event
tops_dte_ctx_t ctx;
tops_init_dte(ctx);
tops::event ev = tops::memcpy_async(ctx, dst, src);
tops::wait(ev);
tops_destroy_dte(ctx);
DTE数据搬运接口包括下列函数,同时也为每个函数提供了 xxx_async()
后缀的异步版本。
下表中是GCU210、GCU300都支持的数据搬运接口:
接口 |
描述 |
---|---|
|
以src总大小拷贝src到dst,开发者需确保dst 的大小足够 |
|
将dst所指定的内存内容设置为const_value |
|
按照dst指定的形状和offset指定的偏置从src中拷贝数据到dst所指定的位置,开发者需确保dst的大小和偏置不超过src的相应值,否则会发生自动填充 |
|
把src所指定的数据拷贝并覆盖dst中由offset所指定的位置 |
|
按照layout对src的数据进行拷贝并转置到dst所指定的位置 |
|
slice和transpose的组合,先拷贝src中dst所指定的切片接着按照layout对切片进行转置并把结果保存在dst所指定的位置 |
|
transpose和deslice的组合,先把src所指定的数据按照layout进行转置,接着将数据拷贝并覆盖到dst中offset所指定的位置 |
|
pad操作,把src指定的数据,按照dst所指定的形状和大小,用pad_value的值设置到src的首部(pad_low有效),尾部(pad_high有效),或者中间(pad_mid有效),并把结果移动到dst所指的位置 |
|
按照第一维(X轴,shape的最后一个元素)将src指定的数据翻转,并把结果移动到dst所指的位置 |
|
按照第二维(Y轴,shape的倒数第二个元素)将src指定的数据翻转,并把结果移动到dst所指的位置 |
|
按照src到dst维度的变化,将src的数据做相应的扩张,并把结果移动到dst所指的位置 |
下表中是GCU300以及之后新增的数据搬运接口(GCU210不支持):
接口 |
描述 |
---|---|
|
按照slice_shape指定的形状和src_offsets指定的偏置从src中拷贝数据到dst所指定的位置,dst的大小和偏置超过 src的相应值时,发生自动填充,用pad_value的值设置到src的首部(pad_low有效),尾部(pad_high有效),或者中间(pad_mid)有效,并把结果移动到dst所指的位置 |
|
按照slice_shape指定的形状和src_offsets指定的偏置从src中拷贝数据,并覆盖dst中由dst_offsets所指定的位置 |
|
按照slice_shape指定的形状和src_offsets指定的偏置从src中拷贝数据,并将数据做dst维度相应的扩张,并把结果移动到dst所指定的位置 |
|
按照slice_shape指定的形状和src_offsets指定的偏置从src中拷贝数据,并把结果移动到dst按照ratio和phase选取数据的指定位置(举例,如果dst bpe = 2 * src bpe,那么ratio必须设置为0,phase可以取值 0或1,取值0意味着指定dst每个数据的低一半,作为拷贝目的,高一半补0) |
|
把src所指定的数据按照ratio和phase选取src数据(举例,如果src bpe = 2 * dst bpe,那么ratio必须设置为0,phase可以取值 0或1,取值0意味着选取src每个数据的低一半),拷贝并覆盖dst中由dst_offsets所指定的位置 |
|
把src所指定的数据初始化为value值后,拷贝并覆盖dst中由offset所指定的位置 |
|
按照第一维(X轴,shape的最后一个元素)将src指定的数据翻转,并把结果移动到dst所指的位置,dst的大小和偏置超过 src的相应值时,发生自动填充,用value的值设置到数据的首部(pad_low有效),尾部(pad_high有效),或者中间(pad_mid)有效,并把结果移动到dst所指的位置 |
|
按照第二维(Y轴,shape的倒数第二个元素)将src指定的数据翻转,并把结果移动到dst所指的位置,dst的大小和偏置超过 src的相应值时,发生自动填充,用value的值设置到数据的首部(pad_low有效),尾部(pad_high有效),或者中间(pad_mid)有效,并把结果移动到dst所指的位置 |
|
按照第一维(X轴,shape的最后一个元素)将src指定的数据翻转,并把结果拷贝并覆盖dst中由dst_offsets所指定的位置 |
|
按照第二维(Y轴,shape的倒数第二个元素)将src指定的数据翻转,并把结果拷贝并覆盖dst中由dst_offsets所指定的位置 |
|
采样操作,把src的dim_stride维度做下采样,并把结果拷贝到dst中 |
8.8. 同步机制 (Synchronization Mechanism)¶
topscc支持两种同步机制:线程组的同步和计数同步。
线程组同步¶
阻塞指定范围内所有线程,直到所有线程都到达后,解除阻塞。
接口 |
描述 |
---|---|
|
线程块内所有线程做一次同步。 |
|
线程网格内所有线程做一次同步。 |
注意:使用__syncblocks()
的核函数必须声明为__cooperative__
计数同步¶
该机制通过屏障(Barrier)对象实现,可以阻塞一组已知数量的线程,直到要求数量的线程都到达屏障。屏障对象的生命周期由一个或多个(count)阶段组成,每个阶段定义一个等待线程阻塞的阶段同步点。线程可以到达(arrive)屏障,并等待(wait)以阻塞在相位同步点上。
屏障阶段由以下步骤组成:
每次调用到达
arrive()
或arrive_and_wait()
时,预期计数都会减少。当预期计数达到零时,在阶段同步点上阻塞的所有线程都将解除阻塞。
接口 |
描述 |
---|---|
|
屏障构造函数,初始化屏障的阶段计数 enum BarrierScope { SIP = 0, CLUSTER = 1 }; |
|
到达屏障并减少计数 |
|
阻塞在同步点直到阶段计数为零 |
|
到达屏障并减少计数,阻塞在同步点直到阶段计数为零 |