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个元素中。 | ![]() |
_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、编译时洗牌
这类函数都接收一个编译期确定的常量来控制洗牌顺序,如果传入的控制系数无法在编译期确定,那么将导致编译错误,例如:
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来生成控制常数。 |
![]() |
_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)指令除了性能较高外,还更加精确,因为这些指令只在计算完乘法与加法后进行一次舍入。
x86平台SIMD编程入门(3):浮点指令的更多相关文章
- x86平台转x64平台关于内联汇编不再支持的解决
x86平台转x64平台关于内联汇编不再支持的解决 2011/08/25 把自己碰到的问题以及解决方法给记录下来,留着备用! 工具:VS2005 编译器:cl.exe(X86 C/C+ ...
- X86平台乱序执行简要分析(翻译为主)
多处理器使用松散的内存模型可能会非常混乱,写操作可能会无序,读操作可能会返回不是我们想要的值,为了解决这些问题,我们需要使用内存栅栏(memory fences),或者说内存屏障(memory bar ...
- WinPcap编程入门实践
转自:http://www.cnblogs.com/blacksword/archive/2012/03/19/2406098.html WinPcap可能对大多数人都很陌生,我在这里就先简单介绍一下 ...
- PC游戏编程(入门篇)(前言写的很不错)
PC游戏编程(入门篇) 第一章 基石 1. 1 BOSS登场--GAF简介 第二章 2D图形程式初体验 2.l 饮水思源--第一个"游戏"程式 2.2 知其所以然一一2D图形学基础 ...
- 【浅墨著作】《OpenCV3编程入门》内容简单介绍&勘误&配套源码下载
经过近一年的沉淀和总结,<OpenCV3编程入门>一书最终和大家见面了. 近期有为数不少的小伙伴们发邮件给浅墨建议最好在博客里面贴出这本书的文件夹,方便大家更好的了解这本书的内容.事实上近 ...
- Matlab与.NET基于类型安全的接口混合编程入门
原文:[原创]Matlab与.NET基于类型安全的接口混合编程入门 如果这些文章对你有用,有帮助,期待更多开源组件介绍,请不要吝啬手中的鼠标. [原创分享]Matlab.NET混编调用Figure窗体 ...
- Mysql C语言API编程入门讲解
原文:Mysql C语言API编程入门讲解 软件开发中我们经常要访问数据库,存取数据,之前已经有网友提出让鸡啄米讲讲数据库编程的知识,本文就详细讲解如何使用Mysql的C语言API进行数据库编程. ...
- opencv编程入门技巧
opencv编程入门技巧 最近在项目中负责图像处理模块的编程工作,主要分为两个板块,一是视频图像去雾,二是可视.红外图像融合.为了提升开发效率,遂开始学习并使用opencv图像类库,效果很好的说~因为 ...
- 脑残式网络编程入门(五):每天都在用的Ping命令,它到底是什么?
本文引用了公众号纯洁的微笑作者奎哥的技术文章,感谢原作者的分享. 1.前言 老于网络编程熟手来说,在测试和部署网络通信应用(比如IM聊天.实时音视频等)时,如果发现网络连接超时,第一时间想到的就是 ...
- 脑残式网络编程入门(三):HTTP协议必知必会的一些知识
本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ...
随机推荐
- 蓝屏攻击 MS12-020
目录 1.蓝屏攻击 MS12-020 搜索对应活跃主机win7蓝屏漏洞端口 Nessus 执行漏洞扫描 Metasploit 漏洞利用 msfconsole Armitage 1.蓝屏攻击 MS12- ...
- mysql弱密码爆破
mySQL弱密码 靶场:/vulhub/mysql/CVE-2012-2122 启动: docker-compose up -d 扫描端口 nmap -Sv -Pn -T4 靶机ip 看到在33 ...
- P9119 [春季测试 2023] 圣诞树
参考博客: 春季测试 2023] 圣诞树 题解 - 洛谷专栏 (luogu.com.cn) 题意:给定二维平面上一个凸多边形的 \(n\) 个顶点, 求一种方案,使得从最高点开始,不重复地经过所有点后 ...
- PbRL | Christiano 2017 年的开山之作,以及 Preference PPO / PrefPPO
PrefPPO 首次(?)出现在 PEBBLE,作为 pebble 的一个 baseline,是用 PPO 复现 Christiano et al. (2017) 的 PbRL 算法. For eva ...
- Python网络爬虫第一弹
03.Python网络爬虫第一弹<Python网络爬虫相关基础概念> 爬虫介绍 引入 之前在授课过程中,好多同学都问过我这样的一个问题:为什么要学习爬虫,学习爬虫能够为我们以后的发展带来那 ...
- 鸿蒙NEXT开发案例:亲戚关系计算器
[引言] 在快节奏的现代生活中,人们往往因为忙碌而忽略了与亲戚间的互动,特别是在春节期间,面对众多的长辈和晚辈时,很多人会感到困惑,不知道该如何正确地称呼每一位亲戚.针对这一问题,我们开发了一款基于鸿 ...
- mysql5.7之JSON数据类型
1.json对象 1.1.方法 使用对象操作的方法进行查询:字段->'$.json属性' 使用函数进行查询:json_extract(字段, '$.json属性') 获取JSON数组/对象长度: ...
- 关于ConditionalOnClass注解
1. pom文件<optional>标签 在Java开发中,大家肯定在pom文件中添加过依赖(现在没有,以后也肯定会有的),不知道大家对<optional>标签的了解有多少,或 ...
- Javascript遍历目录时使用for..in循环无法获取Files对象和SubFolders对象问题的解决方法
1 Javascript遍历目录时使用for..in循环无法获取Files对象和SubFolders对象 1.1 问题场景 在JavaScript中遍历目录,使用for.. in循环时,无法获取到 ...
- MySQL底层概述—7.优化原则及慢查询
大纲 1.Explain概述 2.Explain详解 3.索引优化数据准备 4.索引优化原则详解 5.慢查询设置与测试 6.慢查询SQL优化思路 1.Explain概述 使用Explain关键字可以模 ...










