浮点运算的Altivec内建函数及相关命令详解
一、Altivec内建函数概述
1.1 编译器与开关
使用Apple开发者工具包中的C编译器来调用Apple/Motorola Altivec内建函数。此版本的gcc在Darwin项目中经过Apple的大量修改,具备一定的自动向量化能力。使用Altivec内建函数时,需要添加 -faltivec 开关,示例命令如下:
gcc -O3 -faltivec yourcode.c -lm
1.2 位序约定
在Motorola/IBM/Apple G - 4芯片上,向量数据的位序(字节和字也是如此)采用大端字节序。即最高位(最显著位)编号为0,编号从左到右依次增大,代表的重要性逐渐降低。
1.3 内建函数说明
这里介绍的内建函数主要针对单精度浮点运算,并非完整列表,更详细全面的内容可参考相关手册。
二、掩码生成向量比较函数
2.1 vec_cmpb 函数
vector signed int vec_cmpb (vector float, vector float)
此函数用于进行向量边界比较。对于 d = vec_cmpb(a, b) ,会计算 di[0, 1] = [ai > bi, ai < -bi] ,其中 i = 0, ..., 3 。例如,若 ai < bi ,则 di 的第0位设为0,否则设为1;若 ai > -bi , di 的第1位设为0,否则设为1。
2.2 vec_cmpbr 函数
vector bool int vec_cmpbr (vector float, vector float)
该函数用于进行向量的二元关系比较。对于 d = vec_cmpbr(a, b) ,若相应的浮点元素 ai 和 bi 满足二元关系 br ,则 di 设为全1,否则设为0。例如,当 br 为相等关系( = )时,若 ai = bi ,向量 d 中对应的 di 设为全1的掩码,否则 di = 0 。
2.3 可用二元关系
二元关系 br | 描述 | 数学表达式 |
|---|---|---|
| eq | 相等 | a = b |
| ge | 大于或等于 | a >= b |
| gt | 大于 | a > b |
| le | 小于或等于 | a <= b |
| lt | 小于 | a < b |
2.4 选择函数
-
vec_sel函数
vector float vec_sel(vector float, vector float, vector bool int)
d = vec_sel(a, b, c) 会根据 c 的索引位,将 d 的连续位设置为 b 或 a 对应的位。更准确地说,若 i = 0, ..., 127 对 d 、 a 、 b 的位进行索引,则 d[i] = (c[i] == 1)? b[i] : a[i] 。
- vec_splat 函数
vector float vec_splat(vector float, unsigned literal)
d = vec_splat(a, b) 会从 a 中选择 b mod 4 对应的元素,并将其分布到 d 的每个元素中。例如, d = vec_splat(a, 7) 会选择 a 的第3个元素,因为 3 = 7 mod 4 ,所以 d = (a3, a3, a3, a3) 。若 d 和 a 不是 vector float 类型,索引为 b mod n ,其中 n 是 a 和 d 的元素数量。若为字节向量,索引 k = b mod 16 ,会将 a 中对应的字节 k 分布到 d 中。
三、转换、实用和近似函数
3.1 vec_ctf 函数
vector float vec_ctf(vector int, unsigned literal)
d = vec_ctf(a, b) 用于将定点字转换为浮点向量,计算 di = (float)ai * 2^b ,其中 b 是一个5位无符号字面量。例如,若 ai = 7.1 且 b = 3 ,则 d = vec_ctf(a, 3) 得到 di = 56.8 = 7.1 * 2^3 。
3.2 vec_expte 函数
vector float vec_expte(vector float)
该函数用于计算2的指数近似值。对于 d = vec_expte(a) ,会计算 di = 2^ai ,结果精确到3位。例如,若 ai = 0.5 ,则 di ≈ 1.5 = 2^1 * (1 - 2^(-1) + 1 * 2^(-2) + 0 * 2^(-3)) 。
3.3 vec_floor 函数
vector float vec_floor(vector float)
d = vec_floor(a) 会计算 di = ⌊ai⌋ ,其中 i = 0, ..., 3 , di 是小于或等于 ai 的最大整数的浮点表示。例如,若 a2 = 37.3 ,则 d2 = 37.0 。
3.4 其他函数
还包括 vec_loge (计算以2为底的对数近似值)、 vec_mergeh (高半部分合并)、 vec_mergel (低半部分合并)、 vec_trunc (截断为整数)、 vec_re (倒数近似值)、 vec_round (四舍五入到最近整数)、 vec_rsqrte (倒数平方根近似值)等函数,具体功能和计算方式可参考相关代码注释。
四、向量逻辑运算和排列函数
4.1 逻辑运算函数
-
vec_and函数
vector float vec_and(vector float, vector float)
该函数进行向量的逻辑与运算。对于 d = vec_and(a, b) ,会对 i = 0, ..., 3 逐位计算 di = ai and bi 。
- vec_andc 函数
vector float vec_andc(vector float, vector float)
d = vec_andc(a, b) 会进行逻辑与非运算,对 i = 0, ..., 3 逐位计算 di = ai and ¬bi 。
- vec_nor 函数
vector float vec_nor(vector float, vector float)
d = vec_nor(a, b) 先对每对元素 ai 和 bi 进行逐位或运算,然后取结果的反。即 di = ¬(ai or bi) ,可看作对128位布尔操作数 a 和 b 的或运算结果取反。
- vec_or 函数
vector float vec_or(vector float, vector float)
该函数进行向量的逻辑或运算。对于 d = vec_or(a, b) ,会对每对 (ai, bi) 进行逐位或运算,并将结果存储到对应的 di 中,可看作128位的布尔或操作。
- vec_xor 函数
vector float vec_xor(vector float, vector float)
d = vec_xor(a, b) 进行向量的逻辑异或运算,即对128位的 a 和 b 进行异或操作,并将结果存储在 d 中。
4.2 排列函数
vector float vec_perm(vector float, vector float, vector unsigned char)
d = vec_perm(a, b, c) 会根据排列向量 c 对向量 a 和 b 的字节进行排列。对于 c 的每个字节 ci ,低4 - 7位用于索引 a 或 b 的一个字节, ci 的第3位用于选择是 aj 还是 bj 。例如,若 c2 = 00011001 ,则 j = 1001 = 9 ,因为 c2 的第3位为1,所以 d2 = b9 ;若 c2 = 00001001 ,则 d2 = a9 。
五、加载和存储操作函数
5.1 加载函数
-
vec_ld函数
vector float vec_ld(int, float*)
d = vec_ld(a, b) 会从计算得到的内存位置加载4个元素到向量 d 中,该内存位置是小于或等于 a + b 的最大16字节对齐位置,其中 b 是 float* 指针。
- vec_lde 函数
vector float vec_lde(int, float*)
d = vec_lde(a, b) 会从计算得到的内存位置加载一个元素到 dj 中,该位置是小于或等于 a + b 的最大16字节对齐位置且是4的倍数,索引 k 由对齐地址对16取模后再除以4得到,其他 di ( i ≠ k )的值未定义。
- vec_ldl 函数
vector float vec_ldl(int, float*)
d = vec_ldl(a, b) 与 vec_ld 类似,也是从计算得到的内存位置加载4个元素到向量 d 中,但会将缓存行标记为最近最少使用(LRU)。
5.2 存储函数
-
vec_st函数
void vec_st(vector float, int, float*)
vec_st(a, b, c) 会从第一个小于或等于 c + b 的16字节对齐地址开始存储4字的 a 。例如, vec_store(a, 4, c) 会将16字节对齐的向量 (a0, a1, a2, a3) 存储到位置 c + 4 开始的位置。
- vec_ste 函数
void vec_ste(vector float, int, float*)
vec_ste(a, b, c) 会将 a 中的一个浮点元素 ak 存储到小于或等于 b + c 的最大16字节对齐有效地址(EA)且是4的倍数的位置,索引 k 由 (EA mod 16) / 4 确定。
- vec_stl 函数
void vec_stl(vector float, int, float*)
vec_stl(a, b, c) 会将 a 存储到小于或等于 b + c 的最大16字节对齐位置,并将存储的缓存行标记为LRU。
六、向量操作数的全精度算术函数
6.1 基本算术函数
-
vec_abs函数
vector float vec_abs(vector float)
d = vec_abs(a) 会计算 di = |ai| ,其中 i = 0, ..., 3 。
- vec_add 函数
vector float vec_add(vector float, vector float)
该函数用于向量加法, d = vec_add(a, b) 会计算 di = ai + bi ,其中 i = 0, ..., 3 。
- vec_sub 函数
vector float vec_sub(vector float, vector float)
d = vec_sub(a, b) 用于向量减法,计算 di = ai - bi ,其中 i = 0, ..., 3 。
6.2 乘加和乘减函数
-
vec_madd函数
vector float vec_madd(vector float, vector float, vector float)
d = vec_madd(a, b, c) 会计算 di = ai * bi + ci ,其中 i = 0, ..., 3 。例如,若标量 a 扩展为向量 (a, a, a, a) ,则 y = vec_madd(a, x, y) 是一个 saxpy 操作。
- vec_nmsub 函数
vector float vec_nmsub(vector float, vector float, vector float)
d = vec_nmsub(a, b, c) 会计算 di = ai * bi - ci ,与 vec_madd 类似,只是这里是减法操作。
6.3 最值函数
-
vec_max函数
vector float vec_max(vector float, vector float)
d = vec_max(a, b) 会计算 di = max(ai, bi) ,即每个 di 设为 ai 和 bi 中的较大值。
- vec_min 函数
vector float vec_min(vector float, vector float)
d = vec_min(a, b) 会计算 di = min(ai, bi) ,每个 di 设为 ai 和 bi 中的较小值。
七、集体比较函数
7.1 全满足比较函数
int vec_all_br(vector float, vector float)
d = vec_all_br(a, b) 若对于 i = 0, ..., 3 ,所有的 ai 和 bi 都满足二元关系 br ,则返回1,否则返回0。例如,当 br 为相等关系( = )时,若每个 ai = bi ,则 d = 1 ,否则 d = 0 。
7.2 其他集体比较函数
还包括 vec_all_nan (判断所有元素是否为非数字)、 vec_all_numeric (判断所有元素是否为有效浮点数值)、 vec_any_br (判断是否有任意一对元素满足二元关系)、 vec_any_nan (判断是否有任意元素为非数字)、 vec_any_numeric (判断是否有任意元素为有效浮点数值)等函数。
7.3 额外可用二元关系
二元关系 br | 描述 | 数学表达式 | 操作数为 NaN 时的结果 |
|---|---|---|---|
| ne | 不相等 | a ≠ b | True |
| nge | 不大于或等于 | a < b | True |
| ngt | 不大于 | a <= b | True |
| nle | 不小于或等于 | a > b | True |
| nlt | 不小于 | a >= b | True |
| in | 在边界内 | - | - |
| out | 在边界外 | - | - |
流程图
graph TD;
A[开始] --> B[选择函数类型];
B --> C{是否为比较函数};
C -- 是 --> D[选择比较关系];
D --> E[进行比较操作];
E --> F[输出结果];
C -- 否 --> G{是否为算术函数};
G -- 是 --> H[进行算术运算];
H --> F;
G -- 否 --> I{是否为逻辑运算或排列函数};
I -- 是 --> J[进行逻辑或排列操作];
J --> F;
I -- 否 --> K{是否为加载或存储函数};
K -- 是 --> L[进行加载或存储操作];
L --> F;
K -- 否 --> M[其他操作];
M --> F;
F --> N[结束];
以上就是关于浮点运算的Altivec内建函数及相关操作的详细介绍,这些函数在处理单精度浮点运算时非常有用,可以提高计算效率和性能。通过合理使用这些函数,可以更好地实现各种数值计算和数据处理任务。
八、OpenMP 命令
8.1 编译器开关与环境设置
在特定环境中,使用如下编译器开关来编译使用 OpenMP 的代码:
guidec +O3 +Oopenmp filename.c -lm -Iguide
其中, -Iguide 指定了库 /usr/local/KAI/guide/lib/32/libguide.a 。若要请求最多 8 个 CPU(在 C - shell 中),可使用如下命令设置环境变量:
setenv OMP_NUM_THREADS 8
8.2 OpenMP 指令分类
8.2.1 并行区域定义
-
#pragma omp parallel [clause]:用于定义并行区域,其后跟随结构化代码块。例如:
#pragma omp parallel
{
// 并行执行的代码
}
8.2.2 工作共享指令
-
#pragma omp for [clause]:用于并行化for循环。例如:
#pragma omp for
for (int i = 0; i < 100; i++) {
// 循环体代码
}
-
#pragma omp sections [clause]:将代码划分为多个部分,每个部分可并行执行。例如:
#pragma omp sections
{
#pragma omp section
{
// 第一个并行部分的代码
}
#pragma omp section
{
// 第二个并行部分的代码
}
}
-
#pragma omp single [clause]:指定代码块仅由一个线程执行。例如:
#pragma omp single
{
// 仅一个线程执行的代码
}
8.2.3 组合并行/工作共享指令
-
#pragma omp parallel for [clause]:结合了并行区域和for循环的并行化。例如:
#pragma omp parallel for
for (int i = 0; i < 100; i++) {
// 并行执行的循环体代码
}
-
#pragma omp parallel sections [clause]:结合了并行区域和sections的并行化。例如:
#pragma omp parallel sections
{
#pragma omp section
{
// 第一个并行部分的代码
}
#pragma omp section
{
// 第二个并行部分的代码
}
}
8.2.4 同步指令
-
#pragma omp master:指定代码块仅由主线程执行。例如:
#pragma omp master
{
// 主线程执行的代码
}
-
#pragma omp critical [(name)]:用于保护临界区,确保同一时间只有一个线程执行该代码块。例如:
#pragma omp critical
{
// 临界区代码
}
-
#pragma omp barrier:用于线程同步,所有线程必须在此处等待,直到所有线程都到达该点。例如:
#pragma omp parallel
{
// 并行代码
#pragma omp barrier
// 所有线程同步后执行的代码
}
-
#pragma omp atomic:用于原子操作,确保对共享变量的操作是原子的。例如:
#pragma omp atomic
shared_variable++;
-
#pragma omp flush [(list)]:用于刷新共享变量的缓存,确保所有线程看到一致的变量值。例如:
#pragma omp flush(shared_variable)
-
#pragma omp ordered:用于控制for循环的执行顺序,确保循环迭代按顺序执行。例如:
#pragma omp parallel for ordered
for (int i = 0; i < 100; i++) {
#pragma omp ordered
{
// 按顺序执行的代码
}
}
8.2.5 数据环境指令
-
#pragma omp threadprivate (vbl1, vbl2, ...):用于声明线程私有变量。例如:
int shared_variable;
#pragma omp threadprivate(shared_variable)
8.3 C/C++ 子句
| 子句 | 描述 |
|---|---|
shared(list) | 指定变量为共享变量,所有线程都可以访问。 |
private(list) | 指定变量为线程私有变量,每个线程有自己的副本。 |
firstprivate(list) | 指定变量为线程私有变量,初始值从主线程复制。 |
lastprivate(list) | 指定变量为线程私有变量,最后一个迭代的值会复制回主线程。 |
default (private | shared | none) | 指定变量的默认属性。 |
reduction (operator intrinsic : list) | 用于对共享变量进行归约操作。 |
copyin (list) | 用于将主线程的线程私有变量值复制到所有线程。 |
if (expression) | 根据表达式的值决定是否并行执行。 |
ordered | 用于控制 for 循环的执行顺序。 |
schedule (type [,chunk]) | 用于指定循环迭代的调度方式。 |
nowait | 用于取消同步点,线程可以继续执行而无需等待其他线程。 |
九、MPI 命令总结
9.1 点对点命令
9.1.1 阻塞发送和接收
-
MPI_Get_count:用于返回由初始化status的操作(特别是MPI_Recv)接收到的元素数量。
int MPI_Get_count(
MPI_Status *status, /* input */
MPI_Datatype datatype, /* input */
int *count) /* output */
-
MPI_Recv:用于开始接收由source进程发送的数据,并将其存储到message指向的内存位置。
int MPI_Recv(
void *message, /* output */
int count, /* input */
MPI_Datatype datatype, /* input */
int source, /* input */
int tag, /* input */
MPI_Comm comm, /* input */
MPI_Status *status) /* output */
-
MPI_Send:用于发起将数据message传输到dest进程的操作。
int MPI_Send(
void *message, /* input */
int count, /* input */
MPI_Datatype datatype, /* input */
int dest, /* input */
int tag, /* input */
MPI_Comm comm) /* input */
9.1.2 缓冲点对点通信
-
MPI_Bsend:用于缓冲发送,需要使用MPI_Buffer.attach进行缓冲区分配,使用MPI_Buffer_detach进行缓冲区释放。
int MPI_Bsend(
void *message,
int count,
MPI_Datatype datatype,
int dest,
int tag,
MPI_Comm comm)
9.1.3 缓冲区分配/释放函数
-
MPI_Buffer_attach:用于指定从buffer开始的内存空间作为输出消息的缓冲区。
int MPI_Buffer_attach(
void *buffer, /* input */
int size) /* input */
-
MPI_Buffer_detach:用于释放之前分配的缓冲区,并返回指向该缓冲区的指针和其大小。
int MPI_Buffer_detach(
void *buffer,
int *size) /* output */
9.1.4 非阻塞通信例程
-
MPI_Ibsend:非阻塞缓冲发送。
int MPI_Ibsend(
void *message,
int count,
MPI_Datatype datatype,
int dest,
int tag,
MPI_Comm comm,
MPI_Request *request) /* output */
-
MPI_Irecv:非阻塞接收。
int MPI_Irecv(
void *message, /* output */
int count, /* input */
MPI_Datatype datatype, /* input */
int source, /* input */
int tag, /* input */
MPI_Comm comm, /* input */
MPI_Request *request) /* output */
-
MPI_Isend:非阻塞正常发送。
int MPI_Isend(
void *message, /* input */
int count, /* input */
MPI_Datatype datatype, /* input */
int dest, /* input */
int tag, /* input */
MPI_Comm comm, /* input */
MPI_Request *request) /* output */
9.1.5 请求管理函数
-
MPI_Request_free:用于标记请求所引用的内存为可释放状态,并将请求设置为MPI_REQUEST_NULL。
int MPI_Request_free(
MPI_Request *request) /* input/output */
9.1.6 测试函数
-
MPI_Test:用于测试与请求关联的非阻塞操作是否完成。
int MPI_Test(
MPI_Request *request, /* input/output */
int *flag, /* output */
MPI_Status *status) /* output */
-
MPI_Testall:用于测试与一组请求关联的所有操作是否完成。
int MPI_Testall(
int array_size, /* input */
MPI_Request *requests, /* input/output */
int *flag, /* output */
MPI_Status *statii) /* output array */
-
MPI_Testany:用于测试是否至少有一个请求完成。
int MPI_Testany(
int array_size, /* input */
MPI_Request *requests, /* input/output */
int *done_index, /* output */
int *flag, /* output */
MPI_Status *status) /* output array */
-
MPI_Testsome:用于确定有多少个请求完成,并返回完成请求的索引。
int MPI_Testsome(
int array_size, /* input */
MPI_Request *requests, /* input/output */
int *done_count, /* output */
int *done_ones, /* output array */
MPI_Status *statii) /* output array */
9.1.7 等待函数
-
MPI_Wait:用于等待请求完成。
int MPI_Wait(
MPI_Request *request, /* input/output */
MPI_Status *status) /* output */
-
MPI_Waitall:用于等待所有请求完成。
int MPI_Waitall(
int array_size, /* input */
MPI_Request *requests, /* input/output */
MPI_Status *statii) /* output array */
-
MPI_Waitany:用于阻塞直到至少有一个请求完成。
int MPI_Waitany(
int array_size, /* input */
MPI_Request *requests, /* input/output */
int *done_one, /* output */
MPI_Status *status) /* output */
-
MPI_Waitsome:用于等待至少有一个请求完成,并返回完成请求的数量和索引。
int MPI_Waitsome(
int array_size, /* input */
MPI_Request *requests, /* input/output */
int *done_count, /* output */
int *done_ones, /* output array */
MPI_Status *statii) /* output array */
9.1.8 测试和删除操作
-
MPI_Cancel:用于取消一个操作的请求。
int MPI_Cancel(
MPI_request request) /* input */
-
MPI_Iprobe:用于确定是否可以接收与指定参数匹配的消息。
int MPI_Iprobe(
int source, /* input */
int tag, /* input */
MPI_Comm comm, /* input */
int *flag, /* output */
MPI_Status *status) /* output struct */
-
MPI_Probe:用于阻塞请求,直到可以接收与指定参数匹配的消息。
int MPI_Probe(
int source, /* input */
int tag, /* input */
MPI_Comm comm, /* input */
MPI_Status *status) /* output struct */
-
MPI_Test_canceled:用于确定与状态关联的操作是否成功取消。
int MPI_Test_canceled(
MPI_Status *status, /* input struct */
int *flag) /* input */
9.1.9 持久通信请求
-
MPI_Bsend_init:用于创建持久化缓冲发送请求。
int MPI_Bsend_init(
void *message, /* input */
int count, /* input */
MPI_Datatype datatype, /* input */
int dest, /* input */
int tag, /* input */
MPI_Comm comm, /* input */
MPI_Request *request) /* output struct */
-
MPI_Recv_init:用于创建持久化缓冲接收请求。
int MPI_Recv_init(
void *message, /* output */
int count, /* input */
MPI_Datatype datatype, /* input */
int source, /* input */
int tag, /* input */
MPI_Comm comm, /* input */
MPI_Request *request) /* output struct */
-
MPI_Send_init:用于创建持久化标准发送请求。
int MPI_Send_init(
void *message, /* output */
int count, /* input */
MPI_Datatype datatype, /* input */
int dest, /* input */
int tag, /* input */
MPI_Comm comm, /* input */
MPI_Request *request) /* output struct */
-
MPI_Start:用于启动与请求关联的非阻塞操作。
int MPI_Start(
MPI_Request *request) /* input/output */
-
MPI_Startall:用于启动与一组请求关联的非阻塞操作。
int MPI_Startall(
int array_size, /* input */
MPI_Request *requests) /* input/output */
9.1.10 组合发送和接收例程
-
MPI_Sendrecv:用于同时发送数据到dest并从source接收数据。
int MPI_Sendrecv(
void *send_data, /* input */
int sendcount, /* input */
MPI_Datatype sendtype, /* input */
int dest, /* input */
int sendtag, /* input */
// 此处原文未完整给出,推测后续还有接收相关参数
流程图
graph TD;
A[开始] --> B[选择MPI操作类型];
B --> C{是否为阻塞操作};
C -- 是 --> D[选择发送或接收];
D --> E[执行阻塞发送或接收];
E --> F[结束];
C -- 否 --> G{是否为非阻塞操作};
G -- 是 --> H[选择非阻塞操作类型];
H --> I[发起非阻塞操作];
I --> J{是否需要等待或测试};
J -- 是 --> K[选择等待或测试函数];
K --> L[执行等待或测试操作];
L --> F;
J -- 否 --> F;
G -- 否 --> M{是否为持久化请求};
M -- 是 --> N[创建持久化请求];
N --> O[启动持久化请求];
O --> F;
M -- 否 --> P[其他操作];
P --> F;
综上所述,本文详细介绍了浮点运算的 Altivec 内建函数、OpenMP 命令以及 MPI 命令。这些工具和技术在并行计算和数值处理中具有重要作用。Altivec 内建函数为单精度浮点运算提供了高效的操作方式,通过合理使用这些函数可以显著提高计算性能。OpenMP 命令则为共享内存并行编程提供了便捷的方法,允许开发者轻松地将串行代码并行化。MPI 命令则适用于分布式内存并行编程,支持进程间的通信和同步。通过深入理解和掌握这些技术,开发者可以更好地应对各种复杂的计算任务,提高程序的效率和性能。
超级会员免费看
1736

被折叠的 条评论
为什么被折叠?



