17、浮点运算的Altivec内建函数及相关命令详解

浮点运算的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 命令则适用于分布式内存并行编程,支持进程间的通信和同步。通过深入理解和掌握这些技术,开发者可以更好地应对各种复杂的计算任务,提高程序的效率和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值