转载: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~SSE4以及AVX系列指令集,给了他们单独的寄存器。
SSE首先就是有了属于自己的16个128位长的寄存器,被称为XMM0XMM15,其中XMM8XMM15只有系统是64位模式时才有效。SSE指令要求数据是16byte对齐的。
SSE2进一步支持双精度浮点数,由于寄存器长度没有变长,所以只能支持2个双精度浮点计算或是4个单精度浮点计算。另外,它在这组寄存器上实现了整型计算,从而代替了MMX。
SSE3支持一些更加复杂的算术计算。
SSE4增加了更多指令,并且在数据搬移上下了一番工夫,支持不对齐的数据迁移。
AVX系列指令集将寄存器从128位扩展到256位,同时引入了16个256位的寄存器,被称为YMM0~YMM15。
说起AVX来,还有一个有意思的故事:
SSE及其后面的进化过程中一直是增加其后面的数字,即SSE2, SSE3, SSE4,按理说下一次进化应该称为SSE5。实际上SSE5是存在的,只不过仅存在于AMD的处理器里。前几代SSE处理器都是Intel先出,然后AMD跟随,然而到SSE4之后,突然AMD先出了SSE5,抢了Intel的先。Intel不能忍,于是SSE5就没了。
2、判断当前设备CPU的支持能力
在命令行通过以下命令:
cat /proc/cpuinfo
在输出中查看flags一项,看是否包含avx、avx2等。
3、SIMD的数据类型
在SIMD相关函数中,经常能看到__m128、__m128d、__m128i、__m256、__m256d、__m256i等数据类型,这几种分别表示128位和256位的浮点型与整型数据,它们的定义如下:
typedef float __m128 __attribute__ ((__vector_size__ (16), __may_alias__));
typedef double __m128d __attribute__ ((__vector_size__ (16), __may_alias__));
typedef long long __m128i __attribute__ ((__vector_size__ (16), __may_alias__));
typedef float __m256 __attribute__ ((__vector_size__ (32), __may_alias__));
typedef double __m256d __attribute__ ((__vector_size__ (32), __may_alias__));
typedef long long __m256i __attribute__ ((__vector_size__ (32), __may_alias__));
其中typedef后面的数据类型如long long或double等,指定了新定义的数据类型的基础类型,属性中指定的__vector_size__指定了新类型的实际大小,以字节为单位。
3.1 128位与256位的vector floating-point数据
从SSE到SSE4.2的指令集里,使用128位XMM寄存器,支持128位的vector(矢量)浮点数据,在AVX和FMA指令里增加到了256位的YMM寄存器,可以使用256位的vector数据。
在128位的vector数据里,可以容纳4个单精度浮点数或者2个双精度浮点数。在AVX和VMA指令使用的YMM寄存器里面可以容纳8个单精度浮点数或者4个双精度浮点数。
3.2 128位的scalar floating-point数据
在SSE系统指令和AVX/FMA指令里处理的scalar数据是128位宽,如下图所示:
在128位的scalar数据里,单精度scalar只使用低32位,双精度scalar只使用低64位,高位不作为数据进行运算。
3.3 128位与256位的packed integer数据
SSE系列指令与AVX指令里处理的packed integer数据也是128位宽,包含以下几种:
- packed byte
- packed word
- packed doubleword
- packed quadword
多数情况下这些整型数据也区分unsigned和signed版本,由对应的SIMD指令使用。
在Ivy Bridge微架构及以后的微构架中,增加了AVX2指令,能够处理256位的packed integer数据。
128位的packed integer数据能容纳16个byte,或8个word,或4个doubleword,或2个quadword整 型值,如下图所示:
4、SIMD指令运算示例
比较两个IPv6地址:
int ipv6_addr_cmp_eq(struct in6_addr ip6_addr1,
struct in6_addr ip6_addr2)
{
__m128i addr1 = _mm_set_epi32(ip6_addr1.s6_addr32[0],
ip6_addr1.s6_addr32[1],
ip6_addr1.s6_addr32[2],
ip6_addr1.s6_addr32[3]);
__m128i addr2 = _mm_set_epi32(ip6_addr2.s6_addr32[0],
ip6_addr2.s6_addr32[1],
ip6_addr2.s6_addr32[2],
ip6_addr2.s6_addr32[3]);
__m128i vcmp = _mm_cmpeq_epi32(addr1, addr2);
int16_t mask = _mm_movemask_epi8(vcmp);
return (mask != 0);
}
4.1 _mm_set_epi32()
该函数的声明原型及函数实现原理如下:
即用4个32位整型,构造成一个128位的数据。
4.2 _mm_cmpeq_epi32()
该函数的声明原型及函数实现原理如下:
该函数的作用是,对a和b两个128位的数据,以32位为一组进行比较,比较的结果放到dst数组的对应位。如果比较的两部分值相等,则dst值为0xFFFFFFFF。
4.3 _mm_movemask_epi8()
该函数的声明原型及函数实现原理如下:
该函数的返回值是一个int整型,但通过操作原理可以看到,返回值只用了前16位,因此将该函数返回值赋值给一个16位整型也不会出问题。