x86平台SIMD编程入门(3):浮点指令

打印 上一主题 下一主题

主题 913|帖子 913|积分 2739

1、算术指令

算术类型函数示例备注加_mm_add_sd、_mm256_add_ps减_mm_sub_sd、_mm256_sub_ps乘_mm_mul_sd、_mm256_mul_ps除_mm_div_sd、_mm256_div_ps平方根_mm_sqrt_sd、_mm256_sqrt_ps倒数_mm_rcp_ss、_mm_rcp_ps、_mm256_rcp_ps快速计算32位浮点数的近似倒数(1/x),最大相对误差小于\(1.5\times 2^{-12}\)。倒数平方根_mm_rsqrt_ss、_mm_rsqrt_ps、_mm256_rsqrt_ps快速计算32位浮点数的近似倒数平方根(1/sqrt(x)),最大相对误差小于\(1.5\times 2^{-12}\)。水平加_mm_hadd_ps、_mm256_hadd_pd输入两个寄存器[a, b, c, d]和[e, f, g, h],返回[a+b, c+d, e+f, g+h]。水平减_mm_hsub_ps、_mm256_hsub_pd输入两个寄存器[a, b, c, d]和[e, f, g, h],返回[a-b, c-d, e-f, g-h]。交替加减_mm_addsub_ps、_mm256_addsub_pd输入两个寄存器[a, b, c, d]和[e, f, g, h],返回[a-e, b+f, c-g, d+h]。对于复数乘法比较有用。点乘_mm_dp_ps、_mm_dp_pd、_mm256_dp_ps输入两个寄存器和一个8位常量,常量高4位表示需要点乘的通道,低4位表示需要广播结果的通道。四舍五入_mm_round_ps、_mm_floor_ss、_mm256_ceil_pd最大/最小值_mm_min_ss、_mm256_max_pdx86 SIMD指令中没有一元减号或绝对值指令,但可以通过位操作技巧来实现对应的功能,例如_mm_xor_ps(x, _mm_set1_ps(-0.0f))可实现一元减号运算,_mm_andnot_ps(_mm_set1_ps(-0.0f), x)可实现取绝对值。(因为-0.0f浮点数值只把符号位设置为1,其余位均为0,所以_mm_xor_ps会翻转符号,_mm_andnot_ps会清除符号位。)
2、比较指令

SSE实现了各种浮点数比较运算,如下表所示:
运算符函数示例等于_mm_cmpeq_ss、_mm_cmpeq_ps、_mm_cmpeq_sd、_mm_cmpeq_pd小于_mm_cmplt_ss、_mm_cmplt_ps、_mm_cmplt_sd、_mm_cmplt_pd小于等于_mm_cmple_ss、_mm_cmple_ps、_mm_cmple_sd、_mm_cmple_pd大于_mm_cmpgt_ss、_mm_cmpgt_ps、_mm_cmpgt_sd、_mm_cmpgt_pd大于等于_mm_cmpge_ss、_mm_cmpge_ps、_mm_cmpge_sd、_mm_cmpge_pd不等于_mm_cmpneq_ss、_mm_cmpneq_ps、_mm_cmpneq_sd、_mm_cmpneq_pd不小于_mm_cmpnlt_ss、_mm_cmpnlt_ps、_mm_cmpnlt_sd、_mm_cmpnlt_pd不小于等于_mm_cmpnle_ss、_mm_cmpnle_ps、_mm_cmpnle_sd、_mm_cmpnle_pd不大于_mm_cmpngt_ss、_mm_cmpngt_ps、_mm_cmpngt_sd、_mm_cmpngt_pd不大于等于_mm_cmpnge_ss、_mm_cmpnge_ps、_mm_cmpnge_sd、_mm_cmpnge_pdAVX将浮点数比较指令统一成了_mm_cmp_xx和_mm256_cmp_xx这样的形式,然后通过一个常量来表示比较谓语。比较谓语如下表所示,两个数比较时若其中一个数为NaN,则ordered模式将返回false,unordered模式将返回true,另外signalling只影响MXCSR的值。
比较运算ordered (non-signalling)unordered (non-signalling)ordered (signalling)unordered (signalling)a < b_CMP_LT_OQ_CMP_NGE_UQ_CMP_LT_OS_CMP_NGE_USa = b_CMP_GE_OQ_CMP_NLT_UQ_CMP_GE_OS_CMP_NLT_USa > b_CMP_GT_OQ_CMP_NLE_UQ_CMP_GT_OS_CMP_NLE_UStrue_CMP_ORD_Q_CMP_TRUE_UQ_CMP_ORD_S_CMP_TRUE_USfalse_CMP_FALSE_OQ_CMP_UNORD_Q_CMP_FALSE_OS_CMP_UNORD_S浮点数比较指令返回另一个寄存器来保存结果,其中比较条件成立的值赋为全1(NaN),其它赋为全0(0.0f)。可以使用_mm_movemask_ps、_mm_movemask_pd或AVX中的等效指令来将结果发送到CPU通用寄存器,这些指令收集每个浮点数通道的最高有效位(恰好也是符号位)并打包成标量,然后复制到通用寄存器中。
  1. const __m128 zero = _mm_setzero_ps();
  2. const __m128 eq = _mm_cmpeq_ps(zero, zero);
  3. const int mask = _mm_movemask_ps(eq);
  4. printf("%i\n", mask);
复制代码
在上面这段代码中,对于__m128的所有4个通道,0 == 0的比较结果都是正确的,eq变量的所有128位都设置为1,然后_mm_movemask_ps收集并返回所有4个浮点数通道的符号位,最终打印出的mask值是15,即二进制的0b1111。比较结果的另外一些用途,就是可以将它们作为其它指令的参数(例如blendv指令)。
除了全通道比较函数外,也有一些函数可以只比较两个寄存器的最低通道,如下表所示:
运算符函数示例等于_mm_comieq_ss、_mm_comieq_sd不等于_mm_comineq_ss、_mm_comineq_sd小于_mm_comilt_ss、_mm_comilt_sd小于等于_mm_comile_ss、_mm_comile_sd大于_mm_comigt_ss、_mm_comigt_sd大于等于_mm_comige_ss、_mm_comige_sd3、洗牌指令

3.1、固定顺序洗牌

函数示例说明示意图_mm_movehl_ps将向量a中的高2个元素复制到dst的高2个元素中,将向量b中的高2个元素复制到dst的低2个元素中。_mm_movelh_ps将向量a中的低2个元素复制到dst的低2个元素中,将向量b中的低2个元素复制到dst的高2个元素中。_mm_unpacklo_ps取向量a和向量b的低半部分元素并交错存储到dst中。_mm_unpackhi_ps取向量a和向量b的高半部分元素并交错存储到dst中。_mm_movehdup_ps复制输入向量中的奇数索引元素,并存储到dst中。_mm_moveldup_ps复制输入向量中的偶数索引元素,并存储到dst中。_mm_broadcastss_ps将输入向量的最低通道元素广播到dst的所有元素中。3.2、编译时洗牌

这类函数都接收一个编译期确定的常量来控制洗牌顺序,如果传入的控制系数无法在编译期确定,那么将导致编译错误,例如:
  1. const __m128 zero = _mm_setzero_ps();
  2. _mm_shuffle_ps(zero, zero, rand()); //error C2057: expected constant expression
复制代码
下表仅列举了一些参数是__m128类型的洗牌函数,__m128d、__m256、__m256d也都有对应的函数,可以类推。示意图中蓝色箭头表示使用控制系数选择的内容,灰色箭头表示不同控制系数可能选择的内容。
函数示例说明示意图_mm_shuffle_ps右图中,控制常数是0x98(二进制 10 01 10 00)。输出向量的前2个通道来自第一个输入向量的0b00和0b10号通道,后2个通道来自第二个输入向量的0b01和0b10号通道。如果要对单个向量进行置换,可将两个输入向量都设为同一个向量。可以使用宏_MM_SHUFFLE来生成控制常数。_mm_blend_ps右图中,控制常数为1(二进制 0 0 0 1),所以只从第二个输入向量中提取了对应的0号通道,其余通道都取自第一个输入向量的对应通道。_mm_insert_ps插入单个通道,并可选择将某些通道清零。右图中,控制常数为0x61(二进制 01 10 0001):源索引为0b01,目标索引为0b10,所以第二个输入向量中0b01号通道的F被插入了输出的0b10号通道;最低4位为0b0001,因此0号输出通道被清零。此外,我们也可以选择性地将某些通道清零而无需插入,例如控制常数0b00001001将0号和3号通道清零。(也可以使用_mm_blend_ps和_mm_setzero_ps实现等价功能,但这就是两条指令,而不是一条。)_mm_permute_ps与_mm_shuffle_ps类似,区别在于仅对一个输入向量进行洗牌。右图中,控制常数是0x63(二进制 01 10 00 11)。3.3、运行时洗牌

_mm_blendv_ps、_mm_blendv_pd、_mm256_blendv_ps、_mm256_blendv_pd接收3个参数,通过掩码的符号位从向量a或向量b中选择通道。
_mm_permutevar_ps、_mm256_permutevar8x32_ps都接收一个包含源数据的浮点数寄存器和一个包含源索引的整数寄存器,根据整数寄存器中的索引值从浮点数寄存器中选择通道。
4、乘加融合指令

乘加运算函数示例(a · b) + c_mm_fmadd_ps、_mm256_fmadd_pd(a · b) - c_mm_fmsub_ps、_mm256_fmsub_pd-(a · b) + c_mm_fnmadd_ps、_mm256_fnmadd_pd-(a · b) - c_mm_fnmsub_ps、_mm256_fnmsub_pd相较于分别使用乘法和加法指令,乘加融合(fused multiply-add, FMA)指令除了性能较高外,还更加精确,因为这些指令只在计算完乘法与加法后进行一次舍入。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

缠丝猫

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表