Skip to content

Latest commit

 

History

History
150 lines (100 loc) · 6.43 KB

04_simd_intel_api.md

File metadata and controls

150 lines (100 loc) · 6.43 KB

intel主导的指令集常用函数

simd函数分为这几类:计算,加载和存储(Load/Store),设置常量值(Set constants),提取部分值,类型转换,重新排列。

计算

  • _mm256_add_epi32(a, b): — 将 __m256i 参数当作 8个32-bit整型进行相加。 如果a数组包含的32-bit整型为 a0, a1, a2, a3, a4, a5, a6, a7 而 b数组包含的整型为 b0, b1, b2, b3, b4, b5, b6, b7, 返回 a0 + b0, a1 + b1, a2 + b2, a3 + b3, a4 + b4, a5 + b5, a6 + a6, a7 + b7。(汇编指令vpaddd为类似的功能)
  • _mm256_add_epi16(a, b): — 类似于 _mm256_add_epi32但是是将参数当作16个16-bit整型. 如果a数组包含的16-bit整型为 a0, a1, ..., a15 而 b数组包含的整型为 b0, b1, b2, ..., b15, 返回 a0 + b0, a1 + b1, ..., a15 + b15. (汇编指令vpaddw为类似的功能)
  • _mm256_add_epi8(a, b): — 类似于 _mm256_add_epi32但是参数当作32个8-bit整型。
  • _mm256_mullo_epi16(x, y): 参数 x 和 y当作16-bit有符号整型数组, 将每对整数相乘,并将结果截断为 16-bit的整型。
  • _mm256_mulhi_epi16(x, y): 参数 x 和 y当作16-bit有符号整型,将每对整数相乘得到一个 32 位整数,然后返回每个 32 位整数结果的前 16 位。
  • _mm256_srli_epi16(x, N):参数 x 和 N 当作16-bit有符号整型,N为右移多少位的数组, a数组位a0,a1,...,a15而N数组为N0,N1,...,bN15,返回a0>>N0,a1>>N1,...,a15>>N15. (还有用于 32 或 64 位整数的 epi32 和 epi64 变体。)
  • _mm256_slli_epi16(x, N): 类似于_mm256_srli_epi16(x, N),只不过是左移。
  • _mm256_hadd_epi16(a, b): — (“horizontal add水平相加”) 将其 __m128i 参数视为 16 位整数数组。 如果 a 数组包含 a0, a1, a2, a3, ..., a15 而 b 数组包含 b0, b1, b2, b3, ..., b15, 返回 a0 + a1, a2 + a3, a4 + a5, a6 + a7, b0 + b1, b2 + b3, b4 + b5, b6 + b7, a8 + a9, a10 + a11, a12 + a13, a14 + a15, b8 + b9, b10 + b11, b12 + b13, b14 + b15. (注意,这通常比 _mm_add_epi16 慢得多。 (汇编指令vphaddw 指令。)

加载和存储

  • _mm256_setr_epi32 ---- 通过 32-bit整数数组赋给 __m256i 值。第一个整数参数在写入内存时在__m256i的最低地址,然后依次填充。例如:
__m256i value1 = _mm256_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7);

value1 中的值与 value2 中的值相同

int array[8] = {0, 1, 2, 3, 4, 5, 6, 7};
__m256i value2 = _mm256_loadu_si256((__m256i*) &array[0]);
  • _mm_setr_epi32 ---- 通过 32-bit整数数组赋给 __m128i 值。第一个整数参数在写入内存时在__m128i的最低地址,然后依次填充例如:
__m128i value1 = _mm_setr_epi32(0, 1, 2, 3);

下面是等价的load代码:

int array[4] = {0, 1, 2, 3, 4, 5, 6, 7};
__m128i value2 = _mm_loadu_si128((__m256i*) &array[0]);
  • _mm256_setr_epi16 ---- 类似于_mm256_setr_epi32只不过通过16-bit整数数组赋值。例如:
__m256i value1 = _mm256_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);

下面的等价的load代码:

short array[8] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
__m256i value2 = _mm256_loadu_si256((__m256i*) &array[0]);
  • _mm256_setr_epi8, _mm_setr_epi8 ---- 类似于_mm256_setr_epi32/_mm_setr_epi32但是是通过8-bit整型数组赋值。例如:

  • _mm_set1_epi32, _mm_set1_epi16, _mm_set1_epi8 ---- 给一个 __m128i 赋值值,该值代表适当大小的值数组,其中数组的每个元素都具有相同的值。(有点类似于memset函数) 下面是例子:

__m128i value = _mm_set1_epi16(42);

等价于_mm_setr_epi16赋值:

__m128i value = _mm_setr_epi16(42, 42, 42, 42,  42, 42, 42, 42);
  • _mm256_set_epi8, 等等. — 类似于_mm256_setr_epi8, 等. 但以相反的顺序获取其参数

更多细节需要参考intel的指令集文档。

提取部分值

  • _mm256_extract_epi32(a, index): 从 256-bit 向量 a 中提取索引为index的32-bit整数。如果将 a 复制到内存,则索引为 0 的整数将存储在最低内存地址处。索引必须是常量。

下面是编程案例:

__m256i a = _mm256_setr_epi32(0, 10, 20, 30, 40, 50, 60, 70);
int x = _mm256_extract_epi32(a, 2);

上面的x的值为20。

  • _mm_extract_epi32(a, index):从 128-bit 向量 a 中提取索引为index的32-bit整数。索引必须是常数。

  • _mm256_extract_epi16(a, index):类似于_mm256_extract_epi32但是是提取16-bit整数。

  • _mm256_extracti128_si256(a, index):从 256-bit 向量 a 中提取索引为index的 128-bit 向量。索引必须是常数。

下面是例子:

__m256i a = _mm256_setr_epi32(0, 10, 20, 30, 40, 50, 60, 70);
__m128i result = _mm256_extracti128_si256(a, 1);

result其实是a向量的第一位的数组。

__m128i result = _mm_setr_epi32(40, 50, 60, 70);

类型转换

类型转换用的较少。

  • _mm256_cvtepu8_epi16(eight_bit_numbers): 获取由 16 个 8-bit整型组成的 128-bit向量,并将其转换为由 16 个 16-bit有符号整数组成的 256-bit向量。 下面是实际例子:
__m128i value1 = _mm_setr_epi8(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150);
__m256i value2 = _mm256_cvtepu8_epi16(value1);

等价于如下:

__m256i value2 = _mm256_setr_epi16(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150);
  • _mm256_packus_epi16(a, b):获取 256-bit 向量 a 和 b 中的 所有16-bit 有符号整数,并将它们转换为 8 位无符号整数的 256-bit 向量。结果包含 a 的前 8 个整数、b 的前 8 个整数、a 的最后 8 个整数、b 的最后 8 个整数。超出范围的值设置为 255 或 0。
__m256i a = _mm256_setr_epi16(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160);
__m256i b = _mm256_setr_epi16(170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 25, 15, 5, -5, -15);

__m256i result = _mm256_packus_epi16(a, b)

下面的result可以直接设置:

__m256i result = _mm256_setr_epu8(
    10, 20, 30, 40, 50, 60, 70, 80, /* first 8 integers from a */
    170, 180, 190, 200, 210, 220, 230, 240, /* first eight integers from b */
    90, 100, 110, 120, 130, 140, 150, /* last 8 integers from a */
    250, 255, 255, 25, 15, 5, 0, 0,  /* last 8 integers from b */
        /* 260, 270 became 255;  -5, -15 became 0 */
);
  • _mm256_zextsi128_si256(a): 获取 128-bit 向量 a 并通过添加 0 值 将其转换为 256-bit 向量。

重新排列

这部分使用较少,不过多赘述了。