SIMD、SSE、AVX指令集
指令集
指令集是指CPU能执行的所有指令的集合,每一指令对应一种操作,任何程序最终要编译成一条条指令才能让CPU识别并执行。CPU依靠指令来计算和控制系统,所以指令强弱是衡量CPU性能的重要指标,指令集也成为提高CPU效率的有效工具。
CPU都有一个基本的指令集,比如说目前英特尔和AMD的绝大部分处理器都使用的是X86指令集,因为它们都源自于X86架构。但无论CPU有多快,X86指令也只能一次处理一个数据,这样效率就很低下,毕竟在很多应用中,数据都是成组出现的,比如一个点的坐标(XYZ)和颜色(RGB)、多声道音频等。为了提高CPU在某些方面的性能,就必须增加一些特殊的指令满足时代进步的需求,这些新增的指令就构成了扩展指令集。该指令集采用单指令多数据(single instruction multiple data,简称 SIMD)扩展技术。
Intel扩展指令集的演变
Intel扩展指令集链接:intel扩展指令集机票
MMX
英特尔在1996年率先引入了MMX(Multi Media eXtensions)多媒体扩展指令集,也开创了SIMD(Single Instruction Multiple Data,单指令多数据)指令集之先河,即在一个周期内一个指令可以完成多个数据操作,MMX指令集的出现让当时的MMX Pentium大出风头。
SSE
SSE(Streaming SIMD Extensions,流式单指令多数据扩展)指令集是1999年英特尔在Pentium III处理器中率先推出的,并将矢量处理能力从64位扩展到了128位。在Willamette核心的Pentium 4中英特尔又将扩展指令集升级到SSE2(2000年),而SSE3指令集(2004年)是从Prescott核心的Pentium 4开始出现。
SSE4(2007年)指令集是自SSE以来最大的一次指令集扩展,它实际上分成Penryn中出现的SSE4.1和Nehalem中出现的SSE4.2,其中SSE4.1占据了大部分的指令,共有47条,Nehalem中的SSE4指令集更新很少,只有7条指令,这样一共有54条指令,称为SSE4.2。
AVX
2007年8月,AMD抢先宣布了SSE5指令集(SSE到SSE4均为英特尔出品),英特尔当即黑脸表示不支持SSE5,转而在2008年3月宣布Sandy Bridge微架构将引入全新的AVX指令集,同年4月英特尔公布AVX指令集规范,随后开始不断进行更新,业界普遍认为支持AVX指令集是Sandy Bridge最重要的进步,没有之一。
AVX(Advanced Vector Extensions,高级矢量扩展)指令集借鉴了一些AMD SSE5的设计思路,进行扩展和加强,形成一套新一代的完整SIMD指令集规范。
MMX、SSE、AVX
MMX
MMX系列指令集使用单独的64bit寄存器(MM寄存器),寄存器个数不清楚,一次处理64bit的数据。可以存放数据如下。
寄存器结构:
MM0 |
---|
MM1 |
MM2 |
MM3 |
MM4 |
… |
每个MM寄存器可以存储的值的大小和个数如下(bit, 1字节(B)= 8bit)
MMX指令只能处理整型(字符,短整,整型,这里的整形为32bit)
寄存器大小:
64bit |
---|
一次处理两个32bit整型
32bit | 32bit |
---|
一次处理4个16bit整型
16bit | 16bit | 16bit | 16bit |
---|
一次处理8个字符
8bit | 8bit | 8bit | 8bit | 8bit | 8bit | 8bit | 8bit |
---|
SSE
MMX系列指令集使用单独的128bit寄存器(XMM寄存器),寄存器个数16(不同计算机可能不同),一次处理128bit的数据。可以存放数据如下。
寄存器结构:
XMM0 |
---|
XMM1 |
XMM2 |
XMM3 |
XMM4 |
… |
每个XMM寄存器可以存储的值的大小和个数如下
SSE指令能处理整型,单精度浮点,双精度浮点
注:为什么会有16bit?拿整型来说,对于不同的计算机,占的字节不同,有一个size_t的数据类型,在不同计算机上可能占得字节不同(4B,或者2B),具体可以使用sizeof(数据类型)来查看此类型占得字节数。
寄存器大小:
128bit |
---|
一次处理2个64bit的数据类型
64bit | 64bit |
---|
一次处理4个32bit的数据类型
32bit | 32bit | 32bit | 32bit |
---|
一次处理8个16bit数据类型
16bit | 16bit | 16bit | 16bit | 16bit | 16bit | 16bit | 16bit |
---|
AVX
AVX高级矢量扩展,在SSE的基础上又把寄存器大小扩展为256bit。这次AVX将所有16个128位XMM寄存器扩充为256位的YMM寄存器,从而支持256位的矢量计算。理想状态下,浮点性能最高能达到前代的2倍水平。同时所有的SSE/SSE2/SSE3/SSSE3/SSE4指令是被AVX全面兼容的(AVX不兼容MMX),因此实际操作的是YMM寄存器的低128位,在这一点上与原来的SSE系列指令集无异。
支持256位矢量计算,浮点性能最大提升2倍
增强的数据重排,更有效存取数据
支持3操作数和4操作数,在矢量和标量代码中能更好使用寄存器
支持灵活的不对齐内存地址访问
支持灵活的扩展性强的VEX编码方式,可减少代码
寄存器结构:
YMM0 |
---|
YMM1 |
YMM2 |
YMM3 |
YMM4 |
… |
寄存器大小
256bit |
---|
一次处理4个64bit的数据类型
64bit | 64bit | 64bit | 64bit |
---|
一次处理8个32bit的数据类型
32bit | 32bit | 32bit | 32bit | 32bit | 32bit | 32bit | 32bit |
---|
指令的使用
指令功能介绍
这里只讲解指令的简单使用,内部原理及其寻址什么的流程,自行深抛。
程序必然包括数据和操作,要计算数据,肯定要去cpu的运算器,所以数据会从(如果数据小可能直接全放cache了)内存到cache,再从cache到register,然后进入运算器计算,计算得到数据如果短时间不用,或者想长久保存,则可能需要重写回内存或者硬盘。
简单来说,分三步:
- 写入数据
- 运算数据
- 数据写出
那么数据的写入和写出就对应了扩展指令集的访存指令,数据计算就对应了运算指令。另外还有许多指令类型。具体如下:
指令导读
指令的具体用法还是要查指令手册
每一个指令的构成都可以理解一个函数。
返回值类型 函数名 (形参列表)
1)类型
- __m128 128bit 存储单精度浮点float
__m128i 128bit 存储整形int
__m128d 128bit 存储双精度浮点double - __m256 256bit 存储单精度float
__m256i 256bit 存储整型int
__m256d 256bit 存储双精度double
2)函数名
_mm :128bit | _mm256 :256bit |
---|---|
_load :操作 | _load :操作 |
_ps :p=package,s=float | _ps :p=package,s=float |
_pd :p=package,s=double | _pd :p=package,s=double |
_ss :p=scalar,s=float | _ss :p=scalar,s=float |
package:是向量数据打包的意思 scalar是标量,一个数据的意思
3)形参:要传入的数据的类型指定,一般为地址
如下图:
函数
头文件
指令
编译Flags
Op:指令数据的操作规则
SSE,AVX为什么会提升性能
SIMD
SSE指令的不同形式
- 垂直计算形式
例如:_mm_add_ps();
- 水平计算形式
例如:_mm_hadd_ps();
_mm256_hadd_ps();
- 标量形式(scalar)
示例代码
1-loop.c
#include<stdio.h>
#include <x86intrin.h>
#define N 128int main(){double a[N][N],b[N][N],c[N][N];int i,j;for(i=0;i<N;i++)for(j=0;j<N;j++){a[i][j]=10;b[i][j]=6;}
/*for(i = 0; i < N; i++){for(j = 0; j < N; j++)c[i][j] = a[i][j] + b[i][j];}
*/int block = N / 4;int reserve = N % 4;__m256d ymm0, ymm1;__m256d avx_sum0 = _mm256_setzero_pd();for(i = 0; i < N; i++){for(j = 0; j < block; j ++){ymm0 = _mm256_loadu_pd(&a[i][j*4]);ymm1 = _mm256_loadu_pd(&b[i][j*4]);avx_sum0 = _mm256_add_pd(ymm0, ymm1);_mm256_storeu_pd(*(c+i)+j*4, avx_sum0);}}if(c[4][6]==16)printf("\n结果正确,测试完成!\n\n");elseprintf("\n结果不正确,测试完成!\n\n");return 0;
}
2-ctrl_flow-f4
#include<stdio.h>
#include <x86intrin.h>
#define N 128
int main(){float a[N]={0};float b[N]={1.1,2.2,3.3};float c[N]={1,2,3};int i, j;/*for(i=0;i<N;i++){if(b[i]<c[i])a[i]=b[i]+a[i];elsea[i]=c[i]-a[i];}*/int block = N / 8;__m256 ymm_a = _mm256_setzero_ps(); __m256 ymm_b = _mm256_setzero_ps(); __m256 ymm_c = _mm256_setzero_ps();__m256 avx_sum, avx_sub;__m256 mask;__m256 blendv;for(i = 0; i < 1; i++){ymm_a = _mm256_loadu_ps(a +i*8);ymm_b = _mm256_loadu_ps(b +i*8);ymm_c = _mm256_loadu_ps(c +i*8);avx_sum = _mm256_add_ps(ymm_b, ymm_c);avx_sub = _mm256_sub_ps(ymm_c, ymm_a);mask = _mm256_cmp_ps(ymm_b,ymm_c, 2); //30blendv = _mm256_blendv_ps(avx_sub, avx_sum, mask);printf("%f, %f, %f, %f, %f, %f, %f, %f\n", blendv[0], blendv[1], blendv[2], blendv[3],blendv[4], blendv[5], blendv[6], blendv[7]);_mm256_storeu_ps(a + i*8, blendv);}if(a[2]==3)printf("\n结果正确,测试完成!\n\n");elseprintf("\n结果不正确,测试完成!\n\n");return 0;
}
3-reducetion-d4.c
#include<stdio.h>
#define N 128
#include <x86intrin.h>int main()
{float sum = 1;float a[N]={1,2,3};int i;/*for(i=0;i<N;i++)sum+=a[i];*/__m256 avx_sum = _mm256_setzero_ps();__m256 ymm0;int block = N / 8;for(i = 0; i < block; i++){ymm0 = _mm256_loadu_ps(a + i*8);avx_sum = _mm256_hadd_ps(ymm0, avx_sum);}avx_sum = _mm256_hadd_ps(avx_sum, avx_sum);avx_sum = _mm256_hadd_ps(avx_sum, avx_sum);sum += avx_sum[0] + avx_sum[4];printf("sum = %f ", sum);if(sum==7)printf("\n结果正确,测试完成!\n\n");elseprintf("\n结果不正确,测试完成!\n\n");return 0;
}
4-unalign-d4.c
#include<stdio.h>
#define N 128
#include <x86intrin.h>
int main()
{double a[N],b[N],c[N];int i;for(i=0;i<N;i++)a[i]=3;for(i=0;i<N;i++)b[i]=4;/*for(i=0;i<N/4(32);i++)c[i]=a[i+1]+b[i+2];*/int block = N / 4 / 4;__m256d avx_sum0 = _mm256_setzero_pd();__m256d ymm0, ymm1;for(i = 0; i < block; i++){ymm0 = _mm256_loadu_pd(a + 1 +i*4);ymm1 = _mm256_loadu_pd(b + 1 +i*4);avx_sum0 = _mm256_add_pd(avx_sum0, ymm0);avx_sum0 = _mm256_add_pd(avx_sum0, ymm1);_mm256_storeu_pd(c + i*4, avx_sum0);avx_sum0 = _mm256_setzero_pd();}if(c[2]==7)printf("\n结果正确,测试完成!\n\n");elseprintf("\n结果不正确,测试完成!\n\n");return 0;
}
5-cvt-df.c
#include<stdio.h>
#include <x86intrin.h>
#define N 128
int main(){float a[N]={0};double b[N]={1,2,3};int i;/*for(i=0;i<N;i++)a[i]=b[i];*/int block = N / 4;__m256d ymm_pd;__m128 ymm_ps;for(i = 0; i < block; i++){ymm_pd = _mm256_loadu_pd(b + i*4);ymm_ps = _mm256_cvtpd_ps(ymm_pd);//printf("%f, %f, %f, %f\n", // ymm_ps[0], ymm_ps[1], ymm_ps[2], ymm_ps[3]);_mm_storeu_ps(a + i*4,ymm_ps);}if(a[2]==3) printf("\n结果正确,测试完成!\n\n");elseprintf("\n结果不正确,测试完成!\n\n");return 0;
}
SIMD、SSE、AVX指令集相关推荐
- SSE/AVX指令集学习笔记
因为最近在做SSE/AVX指令集优化视频编码的某些模块,所以要学习SSE指令集的用法.本帖主要记录本人用到的函数的用法. 一.SSE指令(128位寄存器) __m128i _mm_load_si1 ...
- X86 SSE/AVX指令集加速学习
在看了刘文志的<并行编程方法与优化实践>后决定写一写书中的例子或者实际工程中用到加速的一些sample,这本书的pdf我也有,可以在下面留言,我发给你. 1. 使用SSE指令实现了一些简单 ...
- 一文读懂SIMD指令集 目前最全SSE/AVX介绍
SIMD指令集 SSE/AVX 概述 参考手册 Intel® Intrinsics Guide Tommesani.com Docs Intel® 64 and IA-32 Architectures ...
- SIMD(MMX/SSE/AVX)变量命名规范心得
[转载]:SIMD(MMX/SSE/AVX)变量命名规范心得 当使用Intrinsics函数来操作SIMD指令集(MMX/SSE/AVX等)时,会面对不同长度的SIMD数据类型,其中又分为多种紧缩格式 ...
- [C] 跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac)...
作者:zyl910. 本文面对对SSE等SIMD指令集有一定基础的读者,以单精度浮点数组求和为例演示了如何跨平台使用SSE.AVX指令集.因使用了stdint.zintrin.ccpuid这三个模块, ...
- SIMD 编程的优势与SIMD指令:SSE/AVX 与编程demo
资源:https://download.csdn.net/download/Rong_Toa/18745608 <Benefits of SIMD Programming | SIMD的优势&g ...
- 单指令多数据SIMD的SSE/AVX指令集和API
https://software.intel.com/sites/landingpage/IntrinsicsGuide/# Technologies MMX SSE SSE2 SSE3 SSSE3 ...
- opencv cpu指令集SSE/AVX
SSE与AVX指令集 SSE指令集是英特尔提供的基于SIMD(单指令多数据,也就是说同一时间内,对多个不同的数据执行同一条命令)的硬件加速指令,通过使用寄存器来进行并行加速.经过几代的迭代,最新的SS ...
- TensorFlow CPU环境 SSE/AVX/FMA 指令集编译
TensorFlow CPU环境 SSE/AVX/FMA 指令集编译 sess.run()出现如下Warning W tensorflow/core/platform/cpu_feature_guar ...
最新文章
- Hessian的使用以及理解(转)
- Android 进程常驻(0)----MarsDaemon使用说明
- [bzoj 4869] [六省联考2017] 相逢是问候
- PHP7新增的主要特性
- 使用swipecard实现卡片视图左右滑动监听以及点击监听
- 震惊! Leftmost Digit
- C#LeetCode刷题之#27-移除元素(Remove Element)
- Bootstrap 字体图标Glyphicons
- ubuntu php 扩展目录_MacOS搭建PHP开发环境
- async and await 简单的入门
- 学MySQL,这篇万字总结,真的够用了
- 计算机的计算百分比的函数是哪个好,excel用函数计算百分比计算的方法
- 【基础】制作word模板
- 拜托,请给我贫寒简单的生活!
- 人工智能实战2019第七次作业(OpenPAI) 16721088 焦宇恒
- 不懂数据库的码农不是好程序员!
- LCD 3LCD DLP LED投影仪成像原理
- Latex 画图x, y轴格式
- 操作系统设计之哲学原理
- ebs r12多少钱 实施oracle_如何制定Oracle EBS R12升级检查清单
热门文章
- 女生适合linux运维吗,女生适合学Linux云计算运维吗?Linux入门
- 【尚硅谷 JavaWeb 笔记】onsubmit事件
- 差动变压器测位移实验
- 【UML】UML图--交互图(时序图和协作图)
- 【论文】A personalized recommendation algorithm based on large-scale real micro-blog data
- 【Windows锦囊妙计】 WindowBlinds使用透明主题时只有标题栏不透明的真正原因
- 2005-2019年全国硕士研究生入学统一考试英语(二)真题 试卷 词频
- 可以免费自学编程的12个网站,各种资料齐全!
- 别动 我把知识装你脑子里 冷宫霸主JVM
- oracle迁移到在线polardb