SIMD

SIMD指令集分析(C/C++) 阅读代码的时候遇到了__m128i、_mm_set1_epi8、_mm_loadu_si128、_mm_max_epu8、_mm_min_epu8、_mm_store_si128、_mm_unpackhi_epi8、_mm_adds_epi16、_mm_srli_si128等SIMD指令集,所以想着作一个总结。 0. SIMD基础知识 SIMD是单指令多数据技术,目前Intel处理器支持的SIMD技术包括MMX、SSE以及AVX。 MMX是MultiMedia eXtensions(多媒体扩展)的缩写,是 阅读详情

SIMD单指令流多数据流(SingleInstruction Multiple Data,SIMD)是一种采用一个控制器来控制多个处理器,同时对一组数据(又称“数据向量”)中的每一个分别执行相同的操作从而实现空间上的并行性的技术。在微处理器中,单指令流多数据流技术则是一个控制器控制多个平行的处理微元,例如Intel的MMX或SSE以及AMD的3D Now!技术。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

MMX是由英特尔开发的一种SIMD多媒体指令集,共有57条指令。它于1996年集成在英特尔奔腾 (Pentium) MMX处理器上,以提高其多媒体数据的处理能力。

其优点是增加了处理器关于多媒体方面的处理能力,缺点是占用浮点数寄存器进行运算(64位MMX寄存器实际上就是浮点数寄存器的别名)以至于MMX指令和浮点数操作不能同时工作。为了减少在MMX和浮点数模式切换之间所消耗的时间,程序员们尽可能减少模式切换的次数,也就是说,这两种操作在应用上是互斥的。后来英特尔在此基础上发展出SSE指令集;AMD在此基础上发展出3DNow!指令集。现在新开发的程序不再仅使用MMX来优化软件执行效能,而是改使用如SSE、3DNOW!等更容易优化效能的新一代多媒体指令集,不过目前的处理器仍可以执行针对MMX优化的较早期软件。

SIMD是指单指令多数据技术,它已经成为Intel处理器的重要性能扩展。目前Intel处理器支持的SIMD技术包括MMX,SSE,AVX.
MMX提供了8个64bit的寄存器进行SIMD操作,SSE系列提供了128bit的8个寄存器进行SIMD指令操作。而最新的AVX指令则支持256bit的SIMD操作。
目前SIMD指令可以有四种方法进行使用分别是汇编语言,C++类,编译器Intrisincs和自动矢量化。我们用下面的求一个整数数组的和作为例子:
<n;i++)
<n;i++)

  1. int SumAarray(int *buf,int N)
  2. {
  3. int i,sum=0;
  4. for(i=0;i<N;i++)
  5.    sum+=buf[i];

  6. return sum;
  7. }



方法一:自动矢量化
Intel编译器支持自动矢量化,通过选择特定的处理器选项/Q[a]{T|P|N|W|K},上面的代码可以采用下来命令进行编译:
icl /QxP/c /Fa test_vec.c
test_vec.c(4):(col.1)remark:LOOP WAS VECTORIZED.从上面的报告中可以看到第四行的循环自动向量化,还可以通过生成的汇编代码,看到使用了SIMD指令。

方法二:C++类
Intel C++编译器和C++类库中提供了一些新的C++类,这些类对应那些可以直接使用的SIMD指令(支持MMX,SSE,SSE2,不支持SSE3)的数据类型,使用时需要包含如下头文件:

  1. #include <ivec.h>//MMX
  2. #include <fvec.h>//SSE(also include ivec.h)
  3. #include <dvec.h>//SSE2(also include fvec.h)


这些支持SIMD的向量类型采取下面的命名规则:前面用I和F分别表示是支持浮点还是整数SIMD指令,接下来是数字取值为8,16,32,64,表示组向量的基本元素大小。然后后面为字符串vec,最后的数组取值为8,4,2,1,表示组成向量的基本元素的个数。使用64bit的MMX技术的整数类包括I64vec1,I32vec2,I16vec4和I8vec8,而使用128bit的XMM寄存器的浮点类则包括F32vec4,F32vec1,F64vec2。SSE2中使用128bit的XMM寄存器,整数类包括:I128vec1,I64vec2,I32vec4,I16vec8,I8vec16,为了进一步区分封装的是有符号整数还是无符号整数,在那些整数之后也可以包含一个符号标志s或者u,比如Iu32vec4.
通过类的封装,程序员无须关心那些对于类的运算到底使用了哪些汇编指令或者SIMD intrinsic函数,应用易于阅读和编码,并且没有直接使用SIMD代码,在不同的处理器之间不需要任何改动,但是其缺点是无法访问所有的指令和数据类型的组合。

下面的代码给出了SumAarray采用C++类的实现。

点击(此处)折叠或打开

  1. #include
  2. int SumAarray(int *buf,int N)
  3. {
  4. int i;
  5. I32vec4 *vec4 = (I32vec4 *)buf;
  6. I32vec4 sum(0,0,0,0);
  7. for(i=0;i<N;i++)
  8.  sum += vec4[i];
  9.  return sum[0]+sum[1]+sum[2]+sum[3];
  10. }

方法三:使用intrinsics
SIMD intrinsics有些类似于C语言中的函数,可以被其它的代码直接调用。可以像其它函数调用一样给它传递参数,Intel C++编译器支持SIMD intrinsics,并且可以针对intrinsics函数进行内联等优化。intrinsics能够被直接映射到一条或者多条SIMD指令以及其它汇编指令。至于寄存器分配、指令调度和寻址模式,则留给编译器处理。因此,相比汇编语言来说更容易使用。但是,对于生成指令的控制能力则更小。为了使用与SIMD技术相关的intrinsics,首先需要包含那些定义了数据类型和函数的头文件。

  1. #include <mmintrin.h>//mmx
  2. #include <xmmintrin.h>//sse
  3. #include <emmintrin.h>//sse2
  4. #include <pmmintrin.h>//sse3
这些头文件定义了一些数据类型对应于那些SIMD指令要使用的封装浮点数和整数,这些数据类型名以两个下划线开始:
__m64用于表示一个MMX寄存器,表示封装了8个8bit,4个16bit,2个32bit,1个64bit的整数;_
_m128用于SSE,表示封装了;4个32bit的单精度浮点数;
_m128d可以封装2个64bit的双精度浮点数;
_m128i用于表示128bit SIMD整数运算的XMM寄存器。
__m128,__m128d,__m128i的内存变量位于16byte的边界;

  1. int SumArray(int *buf,int N)
  2. {
  3.  int i;
  4. __m128i *vec128 = (__m128i *)buf;
  5. __m128 sum;
  6. sum = _mm_sub_epi32(sum,sum);
  7. for(i=0;i<N/4;i++)
  8.    sum = _mm_add_epi32(sum,vec128[i]);
  9. sum = _mm_add_epi32(sum,_mm_srli_si128(sum,8));
  10. sum = _mm_add_epi32(sum,_mm_srli_si128(sum,4));
  11. return _mm_cvtsi128_si32(sum);
  12. }
  13. }


我们再来看一段代码,注意在循环体有一个条件语句,使用/QxP选项进行编译会发现Intel编译器并不会进行自动矢量化。
<n 4;i++)="" 假设n可以被4整除=""   ="" sum+="vec[i];
return sum[0]+sum[1]+sum[2]+sum[3];
}

假设上述代码中的N=1000,那么原来的代码需要进行1000次加法,而现在循环中只需要250次加法,在return的时候多了额外四个加法。此外,循环次数从1000次减少到250次,减少了很多跳转指令。

方法三:Intrinsics
SIMD  intrinsics有一些类似于C语言中的函数,可以被其它代码直接调用,可以像其它函数一样给它传递参数,Intel C++编译器支持SIMD intrinsics(VS2005/2010也支持,GCC也支持),并且可以针对函数进行内联等优化。需要包含的头文件:
#include   //MMX
#include   //SSE (include mmintrin.h)
#include   //SSE2 (include xmmintrin.h)
#include   //SSE3 (include emmintrin.h)
这些头文件定了一些数据类型对应于那些SIMD指令要适应的浮点数和整数。这些数据类型名以两个下划线开始:
__m64用于表示一个MMX寄存器的内容,用于64bi的MMX整数指令,可以封装正8个8bit,4个16bit,2个32bit和一个64bit的整数。
__m128用于SSE,表示封装了四个32bit的单精度浮点数的SSE寄存器。
__m128d可以封装2个64bit的双精度浮点数;__m128i用于表示支持128bi的内存变量,位于16B的边界。声明为__m64的内存变量位于8B的边界。
注意:定义的intrinsics数据类型,并不是一个标准的C数据类型,所以,这些变量只能用于赋值语句的左边,返回值或者函数调用参数,不能进行加法减法等操作。
SIMD intrinsics的SumArray函数的实现:
int SumArray(int *buf,int N)
{
int i;
__m128i *vec128 = (__128i *)buf;
__m128i sum;
sum = _mm_sub_epi32(sum,sum);//set to zero 这个方法其它SMD置零操作谁更快呢?
for(i=0;i<n 4;i++)=" "="" 假设   ="" n可以被4整除="" {="" sum="" +="_mm_add_epi32(sum,vec128[i]); }

sum = _mm_add_epi32(sum,_mm_srli_si128(sum,8));
sum = _mm_add_epi32(sum,_mm_srli_si128(sum,4));
return _mmcvtsi128_si32(sum);
}

SMD intrinsics函数采用一个非常标准的命名格式,大部分采取:_mm__的格式,函数名以_mm开头,然后表示函数要执行的SIMD指令,比如,上面的add,sub,srli分别表示加法,减法,以为运算,最后是后缀,后缀的一部分给出了药进行运算的函数的数据范围,其中p表示封装操作,s表示变量操作,而ep表示扩展操作,接下来的部分表示要进行运算的数据类型,其中s表示单精度操作,d表示双精度操作,i表示整数操作,u表示无符号整数,数字表示整数的比特数。

所以的这些intrinsics函数可以通过Intel提供个一个软件查询,名称是:Intel_Intrinsics_Guide-windows

再来看一个例子:
下面的这段代码,由于循环体当中有一个条件语句,使用/QxP选项进行编译会发现,Intel编译器并不会进行自动矢量化。

点击(此处)折叠或打开

  1. #define SIZE 128
  2. __decspec(align(16)) short int aa[SIZE],bb[SIZE],cc[SIZE],dd[SIZE];
  3. void Branch_Loop(short int g)
  4. {
  5.   int i;
  6.    for(i=0;i<SIZE;i++)
  7.    {
  8.      aa[i] = (bb[i]>0)?(cc[i]+2):(dd[i]+g);
  9.    }
  10. }
我们可以手工来实现上面的函数,通过使用SIMD指令来消除循环上的分支,同时可以一次完成8个处理,减少循环次数。__mm_cmpgt_epi16()函数可以在一次执行时完成8个比较操作,如果其中某个组元素大于0,则返回的XMM寄存器中对应的比特位全为1,否则全为0,。获得这些掩码之后就可以通过下面代码中的三条操作完成分支赋值了。
代码如下:

点击(此处)折叠或打开

  1. #include <emmintrin.h>
  2. #define TOVectorAddress(x) ((__m128i *)&(x))

  3. void Branch_Loop(short int g)
  4. {
  5.   __m128i a,b,c,d,mask,zero,two,g_broadcast;
  6.   int i;
  7.  zero = _mm_set1_epi(16);
  8.  two = _mm_set1_epi16(2);
  9.  g_broadcast = _mm_set1_epi16(g);
  10.  for(i=0;i<SIZE;i+=8)
  11.  {
  12.   b = _mm_load_si128(ToVectorAddress(bb[i]));
  13.   c = _mm_load_si128(ToVectorAddress(cc[i]));
  14.   d = _mm_load_si128(ToVectorAddress(dd[i]));
  15.   c = _mm_add_epi16(c,tw0);
  16.   d = _mm_add_epi16(d,g_broadcast);
  17.   maks = _mm_cmpgt_epi16(b,zero);
  18.  //注意下面三行代码,它完成了之前代码中的分支赋值操作,从而便于软件流水执行
  19.   a = _mm_and_si128(c,mask);
  20.   mask = _mm_andnot_epi16(mask,d);
  21.   a = _mm_or_si128(a,mask);
  22.    _mm_store_si128(ToVectorAddress(aa[i]),a);
  23.  }
  24. }

【AI系统】SIMD & SIMT 与芯片架构 为了进一步探讨 SIMD/SIMT 与 AI 芯片之间的关系,本文将详细介绍 SIMD 单指令多数据和 SIMT 单指令多线程的计算本质,以及对 NVIDIA CUDA 底层实现 SIMD/SIMT 的原理进行讲解。 阅读详情

相关推荐

C++ SIMD入门

Intel SIMD入门 我前些日子优化一个程序,需要使用C++的SIMD。我查阅了很多资料,很多说的都不详细,遇到了很多问题,踩了很多坑,心灵很受伤。为了总结一下前些日子的学习情况,让后面需要学习SIMD的人少走弯路,写这篇博客。 什么是SIMD? SIMD的全称叫做,单指令集多数据(Single Instruction Multiple Data)。最直观的理解就是,向量计算。比如一个加法指...

sinat_26210035的博客 9603

SIMD简介

SIMD简介 - 知乎本篇文章包含的内容有SIMD指令集简介以及简短的practice环节。 1.SIMD的历史与分类SIMD( Single Instruction Multiple Data)即单指令流多数据流,是一种采用一个控制器来控制多个处理器,同时对一组数据(又称“数…https://zhuanlan.zhihu.com/p/55327037本篇文章包含的内容有SIMD指令集简介以及简短的practice环节。SIMD(Single Instruction Multiple Data)即单指令流多

永远飞翔的鸟 5245

WebAssembly中simd使用调研

目前对WebAssembly的使用主要是做计算密集型的工作,比如软解播放通过WebAssembly计算提供解码能力,WebAssembly执行完全依赖CPU计算,不能借助GPU硬件加速,所以需要尽量挖掘CPU执行提升程序效率的手段。目前两个主要优化手段为多线程和SIMD。对于多线程能力的使用,从WebAssembly指令支持层面、编译工具链、线程间内存共享方式、浏览器对WASM标准的实现方面都有相应的支持而SIMD是另一种能显著提升程序执行效率的方式,需要调研下使用到SIMD特性的源代码编译成WASM的可行

军火库 1971

SIMD加速矩阵运算

【代码】SIMD加速矩阵运算。

少年的博客 5727

SIMD优化应用

SIMD优化应用 SIMD 介绍 费林分类法(Flynn’s Taxonomy)  单一指令流单一数据流(SISD)  单一指令流多数据流(SIMD)  多指令流单一数据流(MISD)  多指令流多数据流(MIMD) SIMD(Single Instruction Multiple Data) 是一种处理器指令类型,即单个指令可以同时处理多个数据。这里的多个,一般指的就是就是多个 CPU 寄存器。 例如,以下为加法的标量(Scalar)与SIMD(Vector)两种执行方式: 现代大多数处理器指

吴建明wujianming_110117 1281

C++中使用SIMD

图7列出了使用SIMD技术的多种方法,我们先按从上至下的顺序简要介绍每一种,然后重点介绍汇编语言方法。 图7 使用SIMD技术的多种方法 第一种方法是使用著名的IPP库,IPP的全称是Intel Integrated Performance Primitives, 是英特尔公司开发的一套跨平台软件函数库,提供了非常广泛的功能,包括各种常用的图形图像、音视频处理函数。因为其中的很多函数都已经使用SIMD技术做了优化,所以使用这个库是使用SIMD技术的一个快捷途径。通过链接https://soft.

u013939918的博客 1488

SIMD、SIMD、SIMT、MISD、MIMD详解与比较

1、SISD SISD(Single Instruction Single Data stream)单指令流单数据流,计算机体系结构分类的一种。 按照计算机同时处于一个执行阶段的指令或数据的最大可能个数划分,可分为SISD、SIMD、MISD、MIMD; SISD(Single Instruction Single Data stream)单指令流单数据流(SISD):SISD其实就是传统的...

键盘的起始页 3万+

SIMD性能优化

SIMD(Single Instruction Multiple Data)是CPU硬件层面支持的用于对数据进行并行操作。原理:采用一个控制器来控制多个处理器,同时对一组数据(又称“数据向量”)中的每一个分别执行相同的操作从而实现空间上的并行性的技术它的指令集存在如下:X86下的实现为MMXSSEAVX指令集ARM下的实现为NEON指令集。

csdn546229768的博客 1880

SIMD指令

转载:https://www.sunxidong.com/357.html SIMD原理 1、SIMD简介 SIMD全称Single Instruction Multiple Data,单指令多数据流,即一条指令处理多条数据,是对CPU基本指令集对扩展。 Intel的初代SIMD指令集是MMX,Multi-Media Extension, 即多媒体扩展,因为它的首要目标是为了支持MPEG视频解码。MMX将64位寄存当作2X32或8X8来用,只能处理整型计算。 后来Intel进一步实现了SSE、SSE2~SS

Sylvia_Wu51的博客 5245

SIMD 介绍

AVX指令集是Sandy Bridge和Larrabee架构下的新指令集。AVX是在之前的128bit扩展到和256bit的SIMD(Single Instruction, Multiple Data)。而Sandy Bridge的SIMD演算单元扩展到256bits的同时数据传输也获得了提升,所以从理论上看CPU内核浮点运算性能提升到了2倍。 Intel AVX指令集,在SIMD计算性能增强的同时也沿用了的MMX/SSE指令集。不过和MMX/SSE的不同点在于增强的AVX指令,从指令的格式上就发生了很大.

zhixingheyi_tian的博客 5605

Numpy快速入门(np)!看这一篇就够了

Numpy快速入门(np)!看这一篇就够了

weixin_74009895的博客 2679

SIMT与SIMD的区别?

来源: SIMD指令和SIMT指令有什么区别?在出现SIMD和SIMT之前,占主导地位的是SISD(Single instruction single data)。但很快人们发现,在一些逻辑简单但计算量大的场景下,这些计算可以并行完成。因此,直接想到的解决方案就是多块芯片并行计算,即多核系统,与此对应的编程模型是多线程编程。多核系统的产生使得编程变得轻松,只需解决多线程互斥、同步等一些小问题即可。但很快有人意识到,从硬件的角度看,这么做很不划算。我们只是为了并行计算,按理说只需要多块计算元件和寄存器就行了,

bdfpxsfh的专栏 4711

神经网络基础-从零开始搭建一个神经网络

人工神经网络(Articial Neural Network,简写为ANN)也称为神经网络(NN),是一种模仿生物神经网络和功能的计算模型,人脑可以看做是一个生物神经网络,由众多的神经元连接而成,各个神经元传递复杂的电信号,树突接收到输入信号,然后对信号进行处理,通过轴突输出信号。人工神经网络也是类似,通过模仿人脑的“学习机器”,通过不断“看”数据(比如图片,数字),自己总结规律,最终学会预测(比如分类图片)在深度学习中,损失函数是用来衡量模型参数的质量的函数,衡量的方式是比较网络输出和真实输出的差异。

Zach的博客 4014

北京交通大学高性能作业——CPU SIMD + GPU SIMD

高性能拯救者!想了解的,也可以通过这篇文章对高性能有更深的了解!!!

weixin_62021811的博客 1237

PHP实现获取当地网络时间​(附完整源码)

PHP实现获取当地网络时间​(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 236

C#工业通信完整项目实战:从协议解析到数据库存储,打造工业级全链路数据闭环(完整可复用源码)

工业自动化的核心诉求是「数据闭环」:从工业设备采集真实生产数据 → 解析校验获得有效数据 → 持久化存储到数据库形成生产台账 → 数据分析/报表展示/异常预警,形成采集→解析→处理→存储→应用的全链路数据流转,这也是工业上位机开发的终极落地形态,区别于单纯的「数据采集」demo。本次实战是一套工业级完整投产级项目,基于C# WinForm + .NET Framework 4.8(工业标配)✅双通信链路完美适配:同时封装TCP网口 + 串口(RS232/485)

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1035

Rust 高性能计算:std::arch 手动 SIMD 与 portable_simd

rayon(第六篇已介绍)提供了易用的数据并行,但 Rust(和 LLVM)的自动矢量化(Auto-vectorization)能力力是有限的。为了榨干 CPU 的最后一点性能,我们需要使用SIMD(Single Instruction, Multiple Data,单指令多数据流)。std::arch(平台特定的 Intrinsics,内部函数)和。

七夜zippoe的博客 1219

11、基于多阵列电极的表面肌电信号运动估计

本文介绍了一种基于多阵列电极的表面肌电信号运动估计技术,旨在实现对手腕和手指运动的高精度估计,解决传统人机接口在操控多自由度机器人时的局限性。通过多阵列电极系统采集前臂肌肉活动信号,结合独立成分分析(ICA)和非负矩阵分解(NMF)等信号处理方法,提取肌肉协同作用,进而生成运动指令以控制机器人或假肢手。文章对比了多阵列电极与传统电极的优势,展示了运动估计算法的整体流程,并以假肢手控制为例说明其实际应用。最后探讨了该技术在测量精度、应用拓展、智能化和便携化等方面的未来发展方向。该技术为假肢控制、康复训练和人机

ik678901234的博客 87

SIMD指令集

概述 SIMD,即Single Instruction, Multiple Data,一条指令操作多个数据。是CPU基本指令集的扩展。主要用于提供fine grain parallelism,即小碎数据的并行操作。如说图像处理,图像的数据常用的数据类型是RGB565, RGBA8888, YUV422等格式,这些格式的数据特点是一个像素点的一个分量总是用小于等于8bit的数据表示的...

dulingwen的博客 3129

Transformer大模型实战 文本摘要任务

Transformer, 文本摘要, 自然语言处理, 预训练模型, 序列到序列, BERT, BART, T5 1. 背景介绍 文本摘要任务是自然语言处理 (NLP) 领域的重要研究方向之一,旨在从长篇文本中提取关键信息,生成简洁、准确的摘要。随着深度学习技术的快速发展,基于 Tra

AI大模型应用之禅 1339

《备考系统分析师》 - 专栏介绍和目录

本专栏提供软考高级系统分析师全套备考资料,包含四大核心内容:1.详细考点梳理(覆盖基础知识、关键技术、案例实践三大模块16个章节);2.历年真题及解析(2009-2022年所有真题含综合知识、案例分析和论文);3.100+篇高质量论文范文;4.预测模拟题。早鸟价仅需9.9元,可节约80%资料搜集时间,帮助考生高效备考、顺利通过考试。专栏持续更新,有问题可随时联系作者。

数据知道的博客 1万+

SIMD指令学习笔记

SIMD发展 所谓的SIMD指令,指的是single instruction multiple data,即单指令多数据运算,其目的就在于帮助CPU实现数据并行,提高运算效率。 MMX MMX是由57条指令组成的SIMD多媒体指令集,MMX将64位寄存当作2个32位或8个8位寄存器来用,只能处理整形计算,这样的64位寄存器有8组,分别命名为MM0~MM7.这些寄存器不是为MMX单独设置的,而是借用...

weixin_34038652的博客 3473

数仓建模—数据语义层

如今,企业产生大量数据,需要以正确的方式进行分析才能做出重要决策。数据可能来自多个来源并采用不同的格式,这使得清楚地了解其含义和重要性成为一项挑战。这就是语义层的用武之地。语义层存在于数据仓库和最终用户使用的应用程序之间。它为用户提供了简化且一致的数据视图,无论其基础数据源的复杂性如何。该逻辑层有助于映射物理数据结构以创建概念数据模型。它定义了数据元素之间的所有规则和关系,并以业务术语提供了数据的通用词汇表。然后,用户可以轻松地与数据进行交互,而无需了解其数据源的技术知识。

7万+
上一篇: nodejs多线程,真正的非阻塞
下一篇: 前端面试题 之 JavaScript
hellochenlu
博客等级 码龄14年 97粉丝 184原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值