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

这篇具有很好参考价值的文章主要介绍了x86平台SIMD编程入门(3):浮点指令。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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_pd

x86 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_pd

AVX将浮点数比较指令统一成了_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_US
a <= b _CMP_LE_OQ _CMP_NGT_UQ _CMP_LE_OS _CMP_NGT_US
a == b _CMP_EQ_OQ _CMP_EQ_UQ _CMP_EQ_OS _CMP_EQ_US
a != b _CMP_NEQ_OQ _CMP_NEQ_UQ _CMP_NEQ_OS _CMP_NEQ_US
a >= b _CMP_GE_OQ _CMP_NLT_UQ _CMP_GE_OS _CMP_NLT_US
a > b _CMP_GT_OQ _CMP_NLE_UQ _CMP_GT_OS _CMP_NLE_US
true _CMP_ORD_Q _CMP_TRUE_UQ _CMP_ORD_S _CMP_TRUE_US
false _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通用寄存器,这些指令收集每个浮点数通道的最高有效位(恰好也是符号位)并打包成标量,然后复制到通用寄存器中。

const __m128 zero = _mm_setzero_ps();
const __m128 eq = _mm_cmpeq_ps(zero, zero);
const int mask = _mm_movemask_ps(eq);
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_sd

3、洗牌指令

3.1、固定顺序洗牌

函数示例 说明 示意图
_mm_movehl_ps 将向量a中的高2个元素复制到dst的高2个元素中,将向量b中的高2个元素复制到dst的低2个元素中。 x86平台SIMD编程入门(3):浮点指令
_mm_movelh_ps 将向量a中的低2个元素复制到dst的低2个元素中,将向量b中的低2个元素复制到dst的高2个元素中。 x86平台SIMD编程入门(3):浮点指令
_mm_unpacklo_ps 取向量a和向量b的低半部分元素并交错存储到dst中。 x86平台SIMD编程入门(3):浮点指令
_mm_unpackhi_ps 取向量a和向量b的高半部分元素并交错存储到dst中。 x86平台SIMD编程入门(3):浮点指令
_mm_movehdup_ps 复制输入向量中的奇数索引元素,并存储到dst中。 x86平台SIMD编程入门(3):浮点指令
_mm_moveldup_ps 复制输入向量中的偶数索引元素,并存储到dst中。 x86平台SIMD编程入门(3):浮点指令
_mm_broadcastss_ps 将输入向量的最低通道元素广播到dst的所有元素中。 x86平台SIMD编程入门(3):浮点指令

3.2、编译时洗牌

这类函数都接收一个编译期确定的常量来控制洗牌顺序,如果传入的控制系数无法在编译期确定,那么将导致编译错误,例如:

const __m128 zero = _mm_setzero_ps();
_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来生成控制常数。 x86平台SIMD编程入门(3):浮点指令
_mm_blend_ps 右图中,控制常数为1(二进制 0 0 0 1),所以只从第二个输入向量中提取了对应的0号通道,其余通道都取自第一个输入向量的对应通道。 x86平台SIMD编程入门(3):浮点指令
_mm_insert_ps 插入单个通道,并可选择将某些通道清零。右图中,控制常数为0x61(二进制 01 10 0001):源索引为0b01,目标索引为0b10,所以第二个输入向量中0b01号通道的F被插入了输出的0b10号通道;最低4位为0b0001,因此0号输出通道被清零。此外,我们也可以选择性地将某些通道清零而无需插入,例如控制常数0b00001001将0号和3号通道清零。(也可以使用_mm_blend_ps_mm_setzero_ps实现等价功能,但这就是两条指令,而不是一条。) x86平台SIMD编程入门(3):浮点指令
_mm_permute_ps _mm_shuffle_ps类似,区别在于仅对一个输入向量进行洗牌。右图中,控制常数是0x63(二进制 01 10 00 11)。 x86平台SIMD编程入门(3):浮点指令

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)指令除了性能较高外,还更加精确,因为这些指令只在计算完乘法与加法后进行一次舍入。文章来源地址https://www.toymoban.com/news/detail-741778.html

到了这里,关于x86平台SIMD编程入门(3):浮点指令的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【ARMv8 SIMD和浮点指令编程】浮点数据转换指令——数据类型互转必备

    浮点数据转换指令包括不同的浮点精度数之间的转换,还包括整型和浮点数之间的转化。 在了解数据转换指令前,必须学习 IEEE 754 定义的五种舍入规则。前两条规则舍入到最接近的值,其他的称为定向舍入: 舍入到最接近的值 Round to nearest, ties to even – rounds to the nearest va

    2024年02月02日
    浏览(54)
  • 【ARMv8 SIMD和浮点指令编程】浮点加减乘除指令——四则运算

    浮点指令有专门的加减乘除四则运算指令,比如 FADD、FSUB、FMUL、FDIV 等。 1 FADD (scalar) 浮点加法(标量)。该指令将两个源 SIMDFP 寄存器的浮点值相加,并将结果写入目标 SIMDFP 寄存器。 该指令可以产生浮点异常。根据 FPCR 中的设置,异常会导致在 FPSR 中设置标志,或者生成同

    2024年02月05日
    浏览(51)
  • 【ARMv8 SIMD和浮点指令编程】NEON 乘法指令——乘法知多少?

    NEON 乘法指令包括向量乘法、向量乘加和向量乘减,还有和饱和相关的指令。总之,乘法指令是必修课,在我们的实际开发中会经常遇到。 1 MUL (by element) 乘(向量,按元素)。该指令将第一个源 SIMDFP 寄存器中的向量元素乘以第二个源 SIMDFP 寄存器中的指定值,将结果放入向

    2024年02月08日
    浏览(41)
  • 【ARMv8 SIMD和浮点指令编程】NEON 通用数据处理指令——复制、反转、提取、转置...

    NEON 通用数据处理指令包括以下指令(不限于): • DUP 将标量复制到向量的所有向量线。 • EXT 提取。 • REV16、REV32、REV64 反转向量中的元素。 • TBL、TBX 向量表查找。 • TRN 向量转置。 • UZP、ZIP 向量交叉存取和反向交叉存取。 1 DUP (element) 将向量元素复制为向量或标量。

    2024年02月07日
    浏览(42)
  • 【ARMv8 SIMD和浮点指令编程】NEON 存储指令——如何将数据从寄存器存储到内存?

    和加载指令一样,NEON 有一系列的存储指令。比如 ST1、ST2、ST3、ST4。 1 ST1 (multiple structures) 从一个、两个、三个或四个寄存器存储多个单元素结构。该指令将元素从一个、两个、三个或四个 SIMDFP 寄存器存储到内存,无需交错。每个寄存器的每个元素都被存储。 无偏移 一个寄

    2024年02月07日
    浏览(47)
  • 【ARMv8 SIMD和浮点指令编程】NEON 加载指令——如何将数据从内存搬到寄存器(其它指令)?

    除了基础的 LDx 指令,还有 LDP、LDR 这些指令,我们也需要关注。 1 LDNP (SIMDFP) 加载 SIMDFP 寄存器对,带有非临时提示。该指令从内存加载一对 SIMDFP 寄存器, 向内存系统发出访问是非临时的提示 。用于加载的地址是根据基址寄存器值和可选的立即偏移量计算得出的。 32-bit (

    2024年02月07日
    浏览(43)
  • C# 使用SIMD向量类型加速浮点数组求和运算(5):如何查看Release程序运行时汇编代码

    作者: zyl910 目录 一、引言 二、办法说明 2.1 基本办法 2.2 Release程序如何设置断点 2.3 如何避免“分层编译”的误导 2.4 实际演练(汇编调试) 2.4.1 进入断点 2.4.2 单步调试 2.4.3 观察主循环的汇编代码 三、结语 参考文献 前面的几篇文章里,介绍了 C# 编写向量算法的各种办法

    2024年02月12日
    浏览(36)
  • C# 使用SIMD向量类型加速浮点数组求和运算(4):用引用代替指针, 摆脱unsafe关键字,兼谈Unsafe类的使用

    作者: zyl910 目录 一、引言 二、办法说明 2.1 历史 2.2 局部引用变量与引用所指的值(类似指针的 地址运算符 、间接运算符 * ) 2.3 重新分配局部引用变量(类似指针直接赋值) 2.4 引用地址调整(类似指针加减法) 2.5 引用地址比较(类似指针比较) 2.6 重新解释(类似C++的

    2024年02月15日
    浏览(42)
  • (汇编) 基于VS的x86汇编基础指令

    visual studio 选择x86运行 示例代码 OV 溢出 超出表示范围为溢出 1,否则 0 UP 增量 1:以递减顺序对数据串处理;0:以递增顺序对数据串处理 EI 允许中断 CPU允许中断1,否则0 PL 正 运算结果为正则为1,否则0 ZR 零 运算结果为0则为1,否则0 AC 辅助进位 低4位向高位进位1,否则0 P

    2024年02月06日
    浏览(43)
  • x86汇编_MUL/IMUL乘法指令_笔记52

    32位模式下整数乘法可以实现32、16或8位的操作,64位下还可以使用64位操作数。MUL执行无符号乘法,IMUL执行有符号乘法。 MUL指令:无符号数乘法 32 位模式下,MUL(无符号数乘法)指令有三种类型: 执行 8 位操作数与 AL 寄存器的乘法; 执行 16 位操作数与 AX 寄存器的乘法;

    2024年02月07日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包