Ubuntu SSE指令集 编程实例—复数乘法与共轭乘法
如有相关问题,欢迎随时讨论交流 jxwxg@foxmail.com
1. SSE指令集介绍
1966 年,Micheal Flynn 提出根据指令和数据流的概念对计算机系统结构进行分类。他将计算机划分为四种基本类型:SISD,SIMD,MISD,MIMD。
SISD,即单指令单数据流,传统的冯诺依曼体系结构的计算机都属于这种类型,其体系结构如下图所示。
MISD,即多指令流单数据流,这种体系结构对单一数据进行多条不同指令的处理,比较适用于分类问题,其体系结构如下图所示。
SIMD,即单指令流多数据流,这种体系结构对不同的多组数据采用相同指令来处理,比较适合并行算法的实现,在语音、图像等信号的处理中应用广泛,其体系结构如下图所示。
MIMD,即多指令流多数据流,这种体系结构拥有若干数据流和若干指令,可以异步地同时执行多种操作,功能最为强大,也最难设计和实现,其系统结构如下图所示。
SSE(Streaming SIMD Extensions)指令是Intel 公司发布的SIMD的扩展指令,它可以方便地实现并行计算,其优势包括:
1. 更高分辨率的图像浏览和处理功能;
2. 语音识别占用更少CPU 资源;
3. 具备更高精度和更快响应速度。
SSE 是MMX(MultiMedia eXtensions)的超集,MMX 支持8 个64bit 的MMX 寄存器,支持在打包的字,字节,双字整数上的SIMD 操作。SSE 指令集的四个版本概要如下表所示。
2. SSE指令集原理
计算机CPU 的结构如下图 所示,CPU 首先从指令计数器寄存器中查看指令地址,然后从相应的指令地址中取出相应的指令存入指令寄存器中。接着CPU 根据操作数地址从内存中取出相应的操作数放入寄存器组中,最后运算器根据操作指令对操作数进行运算。SIMD 就是从内存中取一条指令放在指令寄存器中,再从内存中取一组数据放在SIMD 寄存器中,然后用这条指令并行处理这一组数据。
SSE2 以后就能一次处理128 比特数据,这128 比特数据可能的存储方式是16*8bit,8*16bit,4*32bit,2*64bit,1*128bit。如下图所示。
多数处理器都提供了SIMD指令集,并增加了专门用于执行SIMD指令的SIMD寄存器。SIMD 寄存器的位宽多为32 位、64 位或者128 位,这个位宽被称为“字”。这些SIMD寄存器可以划分为若干个较小的相互独立的单元,称为“亚字”。SIMD 指令对SIMD 寄存器中的所有亚字同时执行相同的操作。下图给出了一个典型的SIMD 指令执行模型: 两组数据各包括四个相互独立的单元,每对单元执行相同的操作,可同时得到四个运算结果。
例如将4 组int 型(32 比特)数据分别执行加运算,CPU 首先从内存中取一条指令(加指令)放入指令寄存器。然后从内存中取出4 组int 型数据放入SIMD 寄存器中,再通过运算器并行的把相对应寄存器中的数据相加,计算结果存入寄存器中。这实现了一条指令并行处理4 组int 型数据。
3. 常用SSE指令集
SSE 指令集包含整型算术指令,整型移位指令,整型逻辑指令,整型比较指令,整型混杂指令,整型读取存储指令,浮点型算术指令,浮点型逻辑指令,浮点型比较指令,浮点型读取存储指令等。此外,计算机处理整数速度比处理浮点数快。
查看SSE 指令时,可以参考以下规律:
1. SSE 指令名字包含三段。以_mm 开头,中间是所做运算的简写,最后指明操作数的单位。
例如_mm_add_epi16(_m128i a,_m128i b)表示将a 和b 中对应位置的16bit 有符号整数分别相加,其中 addadd 表示加操作, epi16epi16 表示操作数的单位是 16bit 有符号的整数。
2. 操作数的单位一般有 8bit ,16bit ,32bit ,64bit 64bit 四种,对应四种相应的的 SSE 指令, 如_mm_add_epi8 ,_mm_add_epi16,_mm_add_epi32 ,_mm_add_epi64 。
3. epi16epi16 表示 16bit 有符号整数, epu16epu16 表示 16bit 无符号整数。 lo 表示高, hi 表示 高,例如 高,例如_mm_unpacklo_epi16(_m128i a,_m128i b) 表示将寄存器 a和寄存器 和寄存器 b的 低 64bit 数以 16bit 为单位交织在一块。
一些常用的SSE指令如下表所示。
指令类型 | 指令 | 功能 |
---|---|---|
整型算术指令 | _mm_add_epi16(_m128i a,_m128i b) |
返回一个_m128i的寄存器,将a和b中对应位置的16bit有符号整数分别相加。 |
_mm_adds_epi16(_m128i a,_m128i b) | 返回一个_m128i的寄存器,将a和b中对应位置的16bit有符号整数分别相加,当计算结果溢出时将其置为边界值 | |
_mm_madd_epi16(_m128i a,_m128i b) | 返回一个_m128i的寄存器,它含有4个有符号32bit的整数,分别满足: r0 := (a0 * b0) + (a1 * b1), r1 := (a2 * b2) + (a3 * b3), r2 := (a4 * b4) + (a5 * b5), r3 := (a6 * b6) + (a7 * b7) |
|
_mm_mulhi_epi16(_m128i a,_m128i b) | 返回一个_128i的寄存器,它含8个有符号16bit的整数,分别为a和b对应位置的16bit有符号整数相乘结果的高16bit数据, 即,r0 := (a0 * b0)[31:16],r1 := (a1 * b1)[31:16],…,r7 := (a7 * b7)[31:16] |
|
整型移位指令 | _mm_slli_epi16(_m128i a, int count) | 返回一个_m128i的寄存器,将寄存器a中的8个16bit整数按照count进行相同的逻辑左移 |
_mm_sll_epi16(_m1 |