AVX512
最近接触到SIMD编码,就不可避免的查到了AVX指令集,两者有什么关系呢,了解一下?
问:AVX是什么?
答:是一套指令集
下面具体看:
AVX
以下内容主要转载自:AVX指令集是什么?它的应用又有哪些?,梳理的很清晰。
要搞明白AVX指令集的作用,首先要讲明白它是什么。定义很简单,它就是x86处理器上面的一套SIMD指令集,是经典的SSE系列指令集的直接继承者。那么SIMD又是什么呢?
SIMD
在计算机刚刚出现的早期阶段,冯·诺伊曼式计算机每次输入一个指令只能够操作一对数据,比如说"+,a,b"可以让ab进行相加,这就是单指令流单数据流(Single Instruction Stream, Single Data Stream)。显然,在面对大量数据的时候,这种操作数据的方法效率较低,程序员想要让一次操作就对多组数据生效,怎么办呢?
单指令流多数据流操作(Single Instruction Stream, Multiple Data Stream)的思路就被引入了,它让输入一次指令就操作多组数据变成了可能。下图更直观:
或者用代码表示:
//未使用SIMD
vectorAdd(const float* a, const float* b, const float* c){
for(int i=0; i<8; i++) {
c[i] = a[i] + b[i]; //一条代码仅能操作2个单个值进行运算
} //需要重复循环操作才能运算完整个数组间的加法
}
//使用AVX
__m256 vectorAdd(__m256 a, __m256 b, __m256 c) {
return _mm256_add_ps(a,b); //一个方法可以同时操作整个数组进行运算 - 一步到位
}
AVX的发展
MMX
上世纪八九十年代,很多处理器开发商都意识到了SIMD的前景,他们开始往自家的处理器里加入SIMD支持。1996年,Intel发布了基于新版P55C架构(最早一版Pentium处理器为P5架构)的Pentium MMX(下图)系列处理器,其中引入了新的MMX指令集,开始支持SIMD。
总结:
(1)MMX的优点
首次支持SIMD操作的指令集,提升了效率
SSE
Pentium MMX系列处理器上新引入的MMX指令集开创了x86处理器支持SIMD操作的先河,该指令集定义了8个64-bit宽度的寄存器,每个寄存器的64-bit容量中可以放入八个8-bit长度的整数或四个16-bit长度整数或两个32-bit整数,CPU在识别到MMX指令集的新指令时会自动将寄存器中的数据进行分割计算,这样一来,单个指令就成功操作了多个数据,实现了SIMD。
但MMX毕竟太嫩,它实际上是通过复用CPU内部x87浮点单元的寄存器来实现SIMD的,所以与运行浮点运算的x87指令集有冲突,两者不能交叉使用,必须先进行切换。另外,由于上述的冲突,它只支持整数操作,在即将要到来的3D时代中显得有些不够用。
Intel当然很清楚MMX指令集的局限之处,而竞争对手新捣鼓出来的3DNow!指令集(1998年,AMD K6-2)已经支持了浮点SIMD运算,于是他们赶紧在经典的奔3处理器上面引入了新的SSE(Streaming SIMD Extensions)指令集,时间点为1999年2月份。
总结:
(1)MMX的缺点
不支持浮点数运算(只支持整数)
(2)SSE的优点
支持浮点数运算
寄存器扩展到128bit,计算效率更高
AVX
SSE指令集解决了MMX指令集身上存在的两大问题,通过引入新的独立寄存器解决了与浮点运算间的冲突问题,同时也就支持了浮点SIMD运算。当然它相对于MMX有很大加强,表现在它的寄存器宽度随着处理器架构的进步而达到了128-bit,这样一来一次SIMD指令能够操作更多的数据,效率上有大幅度的提高。不过初代SSE指令集的单个寄存器只支持32-bit长度的浮点数,还是有很大的局限性,这个问题在Pentium 4(Willamette,2000年)上面引入的SSE2中被解决了,SIMD操作的灵活度高了很多。
随后在约莫8年的时间里,Intel一直在更新SSE指令集,从SSE出到SSE4,AMD方面则是一直在跟进,到了SSE4.2,AMD开始想要在指令集上面寻找自己的翻身点,于是推出了只有自家支持的SSE4a子集,随后更是提前于Intel提出了SSE5。
但Intel不干,我是x86的老大,我不能跟着你来。他们另起炉灶,准备在未来的Sandy Bridge架构中引入一套新的SIMD指令集,这套新指令集在2008年公布,被命名为高级向量扩展(Advanced Vector Extensions)。
相比起迭代了多年的SSE系列指令集,AVX指令集带来了巨大的革新,其中最为主要的是,它在兼容SSE指令集性的同时,将SSE时代最大宽度为128-bit的寄存器拓宽到了256-bit。
不过初代AVX指令集还是比较保守的,它没有将所有指令宽度拓宽到256-bit,而是选择停留在128-bit上面。全面进入256-bit时代这个任务,还是交给了随后的Haswell架构来完成(2013年6月份)。
下图是同处理器用不同指令集的能效对比
但如果以为Intel会就此停下脚步的话,那就大错特错了,他们很快捣鼓出了更宽的AVX-512指令集,顾名思义,其寄存器宽度再次加倍,来到512-bit。
首个支持AVX-512指令集的处理器其实是Intel的Xeon Phi加速卡,首次跑到CPU上已经是Skylake-X系列了。而AVX-512也并不再是一个单一的指令集,它实际上指代的是多个指令集的集合,目前这个数字是17,之后可能还会增多。所有支持AVX-512的处理器都必须支持AVX-512 Foundation子集,从命名上也可以看出,它其实是AVX-512指令集的基础。
AVX-512子集列表
现在是19了
目前只有基于Skylake-Server和Ice Lake这两个架构的处理器可以支持AVX-512(Cannon Lake死了,不然也算),使用门槛较高,一般新一点的应用也只是针对AVX2进行优化。
宽度越大,处理器的计算能力也就越强,尤其是在浮点运算方面,理论上提升有一倍之多,而实际应用中,如果优化得当,其提升幅度还要大一些。但是,新指令集在带来性能增长的同时也带来了另一个让人感到头痛的问题——功耗。
总结:
(1)SSE的缺点
寄存器支持最大宽度为128bit
(2)AVX的优点
寄存器最大支持512bit,叫做AVX-512。计算性再次提升!
AVX的缺点
功耗大
AVX指令集在带来更高性能的同时让CPU的峰值功耗也变高了,可以通过下面的例子进行理解:
飞机发动机是按照最大起飞重量设计的,如果实际的载重没有到最大起飞重量的话,飞行员就可以减推力起飞来降低油耗。CPU也是一样的,最吃功耗的执行单元是根据最大宽度来设计的,平时用不到最大宽度的时候它的功耗就小了,而一旦用到极限,它也就会全开,此时CPU的功耗就上去了。
现如今CPU的功耗是根据负载大小来的,在同频下面,AVX2的负载明显高于SSE负载,因此它的功耗也会大上去。为了让CPU的功耗保持在TDP范围之内,Intel特地设计了一个AVX偏移频率,让工作在AVX状态下面的处理器降低一点频率以减小发热量和功耗,保证使用安全。Intel官方也在2014年的一份AVX指令集优化白皮书中明确说明使用AVX指令集需要额外的电压和电流。
AVX的应用
对于我们这些要做跑分评测的编辑来说,最常接触到的AVX应用其实就是AIDA64了,那么可能有读者就要问了,这个指令集都已经推出十年了难道只能用来跑分烤机吗?当然不是,在Intel的推广之下,现如今已经有大量的生产力应用支持它了,主要在渲染、视频编码、加解密和数学计算等方面有应用,新的AVX-512还针对深度学习推出了AVX-512 VNNI子集,另外,普通玩家最为关心的游戏方面也是有越来越多的应用了,下面举几个例子。
AIDA64是一款测试软硬件系统信息的工具,它可以详细的显示出PC的每一个方面的信息。AIDA64不仅提供了诸如协助超频,硬件侦错,压力测试和传感器监测等多种功能,而且还可以对处理器,系统内存和磁盘驱动器的性能进行全面评估。
渲染、视频编码
渲染方面最常见的有Blender,它不仅仅在我们的测试中被用的多,是真的有很多人都会用它做动画或者CG图,它的渲染引擎可以调用AVX2指令集进行加速计算,吃满你的CPU。
Blender是一款免费开源三维图形图像软件,提供从建模、动画、材质、渲染、到音频处理、视频剪辑等一系列动画短片制作解决方案。
跟渲染方面有点搭边的就是视频编码了,x264和x265这两个知名开源视频编码器想必已经不用再多介绍了,它们都在前几年中纷纷加入了对于AVX指令集的支持,后者甚至加入了针对AVX-512的支持,不过还需要继续优化。另外,Intel方面自己也开源了一套名为SVT的视频编码器,配合不同后端可以实现不同的编码,对AVX和多核的优化相当好。
深度学习
深度学习方面,Google著名的开源深度学习框架Tensorflow在1.6版本之后就已经需要一颗支持AVX指令集的CPU了,换言之,它应用了AVX指令集。
另外,AVX-512的大宽度让它很适合用来跑深度学习,所以Intel也针对深度学习设计了一套子指令集——AVX-512 VNNI,用来加速深度学习相关的计算,在测试中,它表现出了相当的实力。
加解密
加解密计算场景中对CPU的计算吞吐量有较大的要求,此时AVX指令集就可以发挥作用,常见的软件支持就有OpenSSL这个堪称是互联网基石的加密库,另外像很多程序会使用的libsodium加密库也提供了从AVX到AVX-512的优化,而Linux内核也支持使用AVX和AVX2指令集进行加解密计算,还会配合AES-NI这个专用的指令集。实际上目前还有很多数字货币的计算过程支持使用AVX指令集,不过这个应该是真的没有人会用了
一般做加解密计算在GPU上更快,不知道GPU是否支持SIMD?
游戏
近两三年的大作基本都开始启用AVX指令集来进行计算了,一般在游戏中CPU负责除了图形以外的杂活,比如说计算各种NPC的运动路径,计算各种动体的轨迹这样的杂活。不过近两年也有厂商想让Intel参与进游戏图形计算,甚至是当下热门的光线追踪运算,比如Intel的光线追踪计算库Embree就可以被整合进游戏中,目前已经有《坦克世界》等游戏使用了它,Embree库高度依赖AVX指令集,也对CPU的游戏性能提出了新的阐述方式:直接参与图形渲染。
AVX512的使用
(1)查询Intel支持的所有指令集:链接
(2)查询mac支持的指令集
sysctl -a | grep machdep.cpu.features
(3)AVX512的函数:链接
基础
数据类型
函数
举例
参考:https://lusing.blog.csdn.net/article/details/130334103?spm=1001.2014.3001.5502
- SIMD
虽然多线程和OpenMP看起来都不错,都容易编程,但是,我们的优化并不是以简化编程为目的的。
虽然我们抱怨Intel是牙膏厂,每年的进步越来越有限。不过,还总是有新的指令增加到新的架构中来。这其中就有越来越强大的SIMD指令。
- AVX
SIMD就是一条机器指令可以实现多条数据的操作。在Intel平台上,早在1997年就推出了64位的MMX指令集。1999年又有了128位的SSE指令集。2011年,又推出了256位的AVX(Advanced Vector Extensions)指令,我们来个例子看看:
// main.cpp
#include <iostream>
#include <immintrin.h> // 包含 AVX 指令集头文件
void matrix_addition_avx(float* A, float* B, float* C, int size) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j += 8) { // 每次处理 8 个元素(AVX 可以处理 256 位,即 8 个单精度浮点数)
__m256 vecA = _mm256_loadu_ps(&A[i * size + j]);
__m256 vecB = _mm256_loadu_ps(&B[i * size + j]);
__m256 vecC = _mm256_add_ps(vecA, vecB);
_mm256_storeu_ps(&C[i * size + j], vecC);
}
}
}
int main() {
int size = 8; // 假设矩阵大小为 8x8
float A[64] = { /* ... */ }; // 初始化矩阵 A
float B[64] = { /* ... */ }; // 初始化矩阵 B
float C[64] = { 0 }; // 结果矩阵 C
matrix_addition_avx(A, B, C, size);
// 输出结果
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
std::cout << C[i * size + j] << " ";
}
std::cout << std::endl;
}
return 0;
}
我们来解释一下使用SIMD的几条语句:
__m256 vecA = _mm256_loadu_ps(&A[i * size + j])
:从矩阵 A 中加载 8 个浮点数(一次性处理 256 位数据),存储在一个名为 vecA 的 __m256 类型变量中。
__m256 vecB = _mm256_loadu_ps(&B[i * size + j])
:同样地,从矩阵 B 中加载 8 个浮点数,存储在一个名为 vecB 的 __m256 类型变量中。
__m256 vecC = _mm256_add_ps(vecA, vecB)
:使用 AVX 指令 _mm256_add_ps 对 vecA 和 vecB 中的浮点数分别进行逐元素加法,并将结果存储在名为 vecC 的 __m256 类型变量中。
_mm256_storeu_ps(&C[i * size + j], vecC)
:将 vecC 中的 8 个加法结果存储回矩阵 C 的相应位置。
这段代码使用了 AVX 指令集,实现了对浮点矩阵的加法运算。请注意,为了充分利用 AVX 的并行处理能力,矩阵尺寸应该是 8 的倍数。如果矩阵尺寸不是 8 的倍数,需要添加额外的逻辑来处理剩余的元素。
- AVX2
后来,Intel又推出了AVX2指令集,不过对于我们上边的代码并没有太多优化,而主要优化是在整数方面,我们这次使用AVX2提供的整数计算的加速来实现:
#include <iostream>
#include <immintrin.h> // 包含 AVX2 指令集头文件
void matrix_addition_avx2_int(int *A, int *B, int *C, int size) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j += 8) { // 每次处理 8 个元素(AVX2 可以处理 256 位,即 8 个 int32 整数)
__m256i vecA = _mm256_loadu_si256((__m256i *)&A[i * size + j]);
__m256i vecB = _mm256_loadu_si256((__m256i *)&B[i * size + j]);
__m256i vecC = _mm256_add_epi32(vecA, vecB);
_mm256_storeu_si256((__m256i *)&C[i * size + j], vecC);
}
}
}
int main() {
int size = 8; // 假设矩阵大小为 8x8
int A[64] = { /* ... */ }; // 初始化矩阵 A
int B[64] = { /* ... */ }; // 初始化矩阵 B
int C[64] = {0}; // 结果矩阵 C
matrix_addition_avx2_int(A, B, C, size);
// 输出结果
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
std::cout << C[i * size + j] << " ";
}
std::cout << std::endl;
}
return 0;
}
我们不惜折腾量化一把转换成整数的原因是,AVX中只有_mm_add_epi32指令,只能对两个128位整数向量的逐元素相加,而_mm256_add_epi32是256位,数据量加倍了。
不只是加法,AVX2 提供了一系列针对整数操作的新指令,例如乘法、位操作和打包/解包操作等。
AVX2指令的执行吞吐量(throughput)一般为1指令/周期,而AVX1为2指令/周期。所以在同频率下,AVX2的整数加法指令性能理论上可以提高一倍。
同时, 与其他AVX2指令结合使用,如_mm256_load_si256或_mm256_store_si256等,来从内存中加载或存储向量,这样可以提高内存访问的性能和带宽。
- AVX512
后来,Intel还推出了AVX512指令,基本上就把AVX1中的256换成512就可以了:
#include <iostream>
#include <immintrin.h> // 包含 AVX-512 指令集头文件
void matrix_addition_avx512(float *A, float *B, float *C, int size) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j += 16) { // 每次处理 16 个元素(AVX-512 可以处理 512 位,即 16 个单精度浮点数)
__m512 vecA = _mm512_loadu_ps(&A[i * size + j]);
__m512 vecB = _mm512_loadu_ps(&B[i * size + j]);
__m512 vecC = _mm512_add_ps(vecA, vecB);
_mm512_storeu_ps(&C[i * size + j], vecC);
}
}
}
int main() {
int size = 16; // 假设矩阵大小为 16x16
float A[256] = { /* ... */ }; // 初始化矩阵 A
float B[256] = { /* ... */ }; // 初始化矩阵 B
float C[256] = {0}; // 结果矩阵 C
matrix_addition_avx512(A, B, C, size);
// 输出结果
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
std::cout << C[i * size + j] << " ";
}
std::cout << std::endl;
}
return 0;
}
但是,优化并不总是一根筋地往上堆指令就可以的,AVX512是一种非常耗电的指令集,此时我们需要实测权衡一下。
针对手机上用的ARM CPU,可以使用NEON指令来实现SIMD功能:
#include <stdio.h>
#include <arm_neon.h>
void matrix_addition_neon(float *A, float *B, float *C, int size) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j += 4) { // 每次处理 4 个元素(NEON 可以处理 128 位,即 4 个单精度浮点数)
float32x4_t vecA = vld1q_f32(&A[i * size + j]);
float32x4_t vecB = vld1q_f32(&B[i * size + j]);
float32x4_t vecC = vaddq_f32(vecA, vecB);
vst1q_f32(&C[i * size + j], vecC);
}
}
}
int main() {
int size = 4; // 假设矩阵大小为 4x4
float A[16] = { /* ... */ }; // 初始化矩阵 A
float B[16] = { /* ... */ }; // 初始化矩阵 B
float C[16] = {0}; // 结果矩阵 C
matrix_addition_neon(A, B, C, size);
// 输出结果
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
printf("%f ", C[i * size + j]);
}
printf("\n");
}
return 0;
}
参考
1、AVX / AVX2 指令编程
2、simd指令集使用入门
AVX512的更多相关文章
- SSE指令集学习:Compiler Intrinsic
大多数的函数是在库中,Intrinsic Function却内嵌在编译器中(built in to the compiler). 1. Intrinsic Function Intrinsic Fun ...
- Qt5.7.0配置选项(configure非常详细的参数)
configure是一个命令行工具,用于配置Qt编译到指定平台.configure必须运行于Qt源码根目录.当运行configure时,编译源码使用的是所选工具链中的make工具. 一.源码目录.编译 ...
- 腾讯云总监手把手教你,如何成为AI工程师?
作者:朱建平 腾讯云技术总监,腾讯TEG架构平台部专家工程师 1.关于人工智能的若干个错误认知 人工智能是AI工程师的事情,跟我没有什么关系 大数据和机器学习(AI) 是解决问题的一种途径和手段,具有 ...
- SIMD---AVX系列
AVX全称Advanced Vcetor Extension,是对SSE的后续扩展,主要分为AVX.AVX2.AVX512三种.在目前常见的机器上,大多只支持到AVX系列,因此其他SIMD扩展指令我们 ...
- 号称“新至强,可拓展,赢当下”的Xeon可拓展处理器有多逆天?
目前企业数据中心正在发生重大变化,许多企业正在经历基于在线服务和数据的广泛转型.他们将这些数据用于功能强大的人工智能和分析应用程序,这些应用程序可以将其转化为改变业务的洞察力,然后推出可以使这些洞察力 ...
- 阿里如何实现海量数据实时分析技术-AnalyticDB
导读:随着数据量的快速增长,越来越多的企业迎来业务数据化时代,数据成为了最重要的生产资料和业务升级依据.本文由阿里AnalyticDB团队出品,近万字长文,首次深度解读阿里在海量数据实时分析领域的多项 ...
- ES 03 - 初探Elasticsearch的主要配置文件(以6.6.0版本为例)
目录 1 elasticsearch.yml(ES服务配置) 1.1 Cluster集群配置 1.2 Node节点配置 1.3 Paths路径配置 1.4 Memory内存配置 1.5 Network ...
- CPU二则
CPU二则 CPU二则 aligned load & unaligned load non-temporal store(streaming store) 参考文献 aligned load ...
- CPU TFLOPS 计算
CPU TFLOPS 计算 姚伟峰 yaoweifeng0301@126.com] http://www.cnblogs.com/Matrix_Yao/ 深度学习任务是一个计算密集型任务,所以很关注计 ...
- Game Engine Architecture 5
[Game Engine Architecture 5] 1.Memory Ordering Semantics These mysterious and vexing problems can on ...
随机推荐
- Codeforces 983 A-E
题解 A 难度:黄 算法标签:数学.进制 题目翻译:给定进制 \(b\) 和分数 \(\frac{p}{q}\),求这个分数在 \(b\) 进制下是否是有限小数. 题目分析: 首先将分数化简(不用说了 ...
- 学校官网应该使用哪种SSL证书?
学校官网在选择SSL证书时,应考虑多个因素,包括网站的性质.安全要求.预算以及证书的管理便捷性等.以下是关于学校官网应使用哪种SSL证书的详细分析: 多域名和子域名需求: 如果学校官网有多个子域名或者 ...
- Stratum挖矿协议&XMR挖矿流量分析
目录 前言 区块链和挖矿相关概念 挖矿木马 挖矿协议Stratum Stratum工作过程 XMR挖矿流量分析 环境搭建 流量分析 总结 前言 之前参与了一个关于"挖矿行为检测"的 ...
- Java Study For Seven Day( 面向对象三)
继承 class Person { String name; int age; } class Student extends Person { void study() { System.out.p ...
- 鸿蒙UI开发快速入门 —— part03: 组件的生命周期
1. 什么是组件的生命周期 组件的生命周期是我们开发一个组件必须要关注的内容,组件的生命周期,指的是组件的创建.渲染.销毁等过程.因为这个过程就类似于人从出生到离世的过程,从而称为:组件的生命周期. ...
- OpenEuler安装MongoDB并配置访问密码
1. 下载MongoDB.安装 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-4.4.18.tgz tar zxv ...
- IOS实现水波纹
IOS实现水波纹 需要实现一个水波纹效果 其实就是画两个正弦函数或者余弦函数的layer在view上面,根据屏幕刷新率来重绘,更新其左右偏移量来让其看起来是在左右移动 具体实现 定义两个layer,用 ...
- 实用干货分享(2) - Docker使用操作指南
一.Docker安装部署 1. 安装仓库 执行以下命令,安装Docker所需的包.其中yum-utils提供yum-config-manager工具:device-mapper-persistent- ...
- 【人工智能】【Python】Numpy基础
Numpy 目录 Numpy Numpy简介 ndarray与原生Python List运算效率对比 N阶数组 ndarray (1)创建数组 (2)生成数组 生成纯1数组 生成纯0数组 从现有数组生 ...
- Golang实战:深入解析国密算法在Go语言中的应用与优化
Golang实战:深入解析国密算法在Go语言中的应用与优化 引言 随着信息技术的迅猛发展,数据安全成为企业和个人关注的焦点.国密算法(SM系列算法)作为我国自主研发的密码算法标准,逐渐在各个领域中得到 ...