簡體   English   中英

如何使用Intel內部函數從8位整數數組構建32位整數?

[英]How to build 32bit integers from array of 8bit integers using Intel intrinsics?

我有一個由32個字節組成的數組。 我需要從該數組中構建8個4字節整數。 例如0x00,0x11,0x22,0x33 8位整數必須是一個0x00112233 32位整數。 我決定使用AVX指令,因為我可以使用一個命令將整個數組加載到寄存器中。

我寫的代碼:

#include <stdio.h>
#include "immintrin.h"

typedef unsigned int        uint32_t;
typedef unsigned char       uint8_t;

main() {
  const uint8_t block[32] __attribute((aligned(32))) = {
   0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff
  ,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff
  };
  uint32_t m[8] __attribute((aligned(32)));

  __m256i ymm9 = _mm256_set_epi8(
        block[ 0],block[ 1],block[ 2],block[ 3],block[ 4],block[ 5],block[ 6],block[ 7],
        block[ 8],block[ 9],block[10],block[11],block[12],block[13],block[14],block[15],
        block[16],block[17],block[18],block[19],block[20],block[21],block[22],block[23],
        block[24],block[25],block[26],block[27],block[28],block[29],block[30],block[31]);
  _mm256_store_si256(&(m[0]),ymm9);
  int i;
  for(i=0;i<32;++i) printf("i=%d, 0x%02x\n",i,block[i]);
  for(i=0;i<8;++i) printf("i=%d, 0x%08x\n",i,m[i]);
}

您認為它在性能方面是最佳的嗎? 能做得更好並且運行得更快嗎? 我使用Linux @ x86_64和gcc 4.8.2。

我是Intel內部函數領域的初學者。 謝謝你的幫助。

與往常一樣,檢查拆卸情況。 事實證明,無論如何,無論如何我使用的編譯器都依賴於該數據作為編譯時間常數,並且對其進行了重新排列,以便可以輕松地加載它。 如果實際代碼中確實是這種情況,那很好(但是為什么不使用uint數組開頭呢?)。 但是,正如我懷疑的那樣,如果這只是一個示例,而實際的數組具有可變性,那將是一場災難,請看一下:

movzx   eax, BYTE PTR [rsp+95]
xor ebx, ebx
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+93]
vmovd   xmm7, DWORD PTR [rsp]
vpinsrb xmm7, xmm7, BYTE PTR [rsp+94], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+91]
vmovd   xmm3, DWORD PTR [rsp]
vpinsrb xmm3, xmm3, BYTE PTR [rsp+92], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+89]
vmovd   xmm1, DWORD PTR [rsp]
vpinsrb xmm1, xmm1, BYTE PTR [rsp+90], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+87]
vmovd   xmm6, DWORD PTR [rsp]
vpunpcklwd  xmm3, xmm7, xmm3
vpinsrb xmm6, xmm6, BYTE PTR [rsp+88], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+85]
vmovd   xmm5, DWORD PTR [rsp]
vpinsrb xmm5, xmm5, BYTE PTR [rsp+86], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+83]
vmovd   xmm2, DWORD PTR [rsp]
vpunpcklwd  xmm1, xmm1, xmm6
vpinsrb xmm2, xmm2, BYTE PTR [rsp+84], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+81]
vmovd   xmm0, DWORD PTR [rsp]
vpunpckldq  xmm1, xmm3, xmm1
vpinsrb xmm0, xmm0, BYTE PTR [rsp+82], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+79]
vmovd   xmm4, DWORD PTR [rsp]
vpunpcklwd  xmm2, xmm5, xmm2
vpinsrb xmm4, xmm4, BYTE PTR [rsp+80], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+77]
vmovd   xmm8, DWORD PTR [rsp]
vpinsrb xmm8, xmm8, BYTE PTR [rsp+78], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+75]
vpunpcklwd  xmm0, xmm0, xmm4
vmovd   xmm4, DWORD PTR [rsp]
vpinsrb xmm4, xmm4, BYTE PTR [rsp+76], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+73]
vpunpckldq  xmm0, xmm2, xmm0
vmovd   xmm2, DWORD PTR [rsp]
vpinsrb xmm2, xmm2, BYTE PTR [rsp+74], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+71]
vmovd   xmm7, DWORD PTR [rsp]
vpunpcklqdq xmm1, xmm1, xmm0
vpunpcklwd  xmm4, xmm8, xmm4
vpinsrb xmm7, xmm7, BYTE PTR [rsp+72], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+69]
vmovd   xmm6, DWORD PTR [rsp]
vpinsrb xmm6, xmm6, BYTE PTR [rsp+70], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+67]
vmovd   xmm0, DWORD PTR [rsp]
vpunpcklwd  xmm2, xmm2, xmm7
vpinsrb xmm0, xmm0, BYTE PTR [rsp+68], 1
mov BYTE PTR [rsp], al
movzx   eax, BYTE PTR [rsp+65]
vmovd   xmm5, DWORD PTR [rsp]
vpunpckldq  xmm2, xmm4, xmm2
vpinsrb xmm5, xmm5, BYTE PTR [rsp+66], 1
mov BYTE PTR [rsp], al
vmovd   xmm3, DWORD PTR [rsp]
vpunpcklwd  xmm0, xmm6, xmm0
vpinsrb xmm3, xmm3, BYTE PTR [rsp+64], 1
vpunpcklwd  xmm3, xmm5, xmm3
vpunpckldq  xmm0, xmm0, xmm3
vpunpcklqdq xmm0, xmm2, xmm0
vinserti128 ymm0, ymm1, xmm0, 0x1
vmovdqa YMMWORD PTR [rsp+32], ymm0

哇。 好吧,不是很好。 確實比沒有內在函數完成同一件事更糟糕,但是並非所有內容都丟失了。 最好將數據加載為小的endian _mm256_shuffle_epi8 ,然后用_mm256_shuffle_epi8交換它們,就像這樣(但是請檢查一下shuffle掩碼,我沒有對其進行測試)

__m256i ymm9 = _mm256_shuffle_epi8(_mm256_load_si256((__m256i*)block), _mm256_set_epi8(
    0, 1, 2, 3,
    4, 5, 6, 7,
    8, 9, 10, 11,
    12, 13, 14, 15,
    0, 1, 2, 3,
    4, 5, 6, 7,
    8, 9, 10, 11,
    12, 13, 14, 15));
ymm9 = _mm256_permute2x128_si256(ymm9, ymm9, 1);
_mm256_store_si256((__m256i*)m, ymm9);

通常,對內在函數“ set”家族要非常小心,它們會編譯成非常糟糕的指令序列。

謝謝大家的評論。 特別是哈羅德(Harold)和茲博森(Zboson)。

這是我的第二次嘗試:

const uint8_t block[32] __attribute((aligned(32))) = {
  0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
  0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,
  0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
  0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff};
uint32_t m[8] __attribute((aligned(32)));
const uint8_t maska[16] __attribute((aligned(16))) = {
  0x0F,0x0E,0x0D,0x0C,0x0B,0x0A,0x09,0x08,
  0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00};
__m128i mask = _mm_load_si128(maska);
__m128i xmm0 = _mm_load_si128(block);
_mm_store_si128((__m128i*)&(m[0]),_mm_shuffle_epi8(xmm0, mask));
xmm0 = _mm_load_si128(block+16);
_mm_store_si128((__m128i*)&(m[4]),_mm_shuffle_epi8(xmm0, mask));

您如何看待? 我很確定還有改進的余地。 我不知道_mm_load_si128是否是從內存復制數據到寄存器的最佳方法。 第一次迭代的匯編程序:

/* Create a vector with element 0 as *P and the rest zero.  */

extern __inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_load_si128 (__m128i const *__P)
{
return *__P;
mov    0x8(%rsp),%rax
vmovdqa (%rax),%xmm0
    0x0F,0x0E,0x0D,0x0C,0x0B,0x0A,0x09,0x08,
    0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00};
__m128i mask = _mm_load_si128(maska);
vmovdqa %xmm0,0x30(%rsp)
lea    0xf0(%rsp),%rax
mov    %rax,0x10(%rsp)
mov    0x10(%rsp),%rax
vmovdqa (%rax),%xmm0
__m128i xmm0 = _mm_load_si128(block);
vmovdqa %xmm0,0x40(%rsp)
vmovdqa 0x40(%rsp),%xmm0
vmovdqa %xmm0,0x50(%rsp)
vmovdqa 0x30(%rsp),%xmm0
vmovdqa %xmm0,0x60(%rsp)
}

extern __inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_shuffle_epi8 (__m128i __X, __m128i __Y)
{
return (__m128i) __builtin_ia32_pshufb128 ((__v16qi)__X, (__v16qi)__Y);
vmovdqa 0x60(%rsp),%xmm1
vmovdqa 0x50(%rsp),%xmm0
vpshufb %xmm1,%xmm0,%xmm0
lea    0xb0(%rsp),%rax
mov    %rax,0x18(%rsp)
vmovdqa %xmm0,0x70(%rsp)
}

extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_store_si128 (__m128i *__P, __m128i __B)
{
*__P = __B;
mov    0x18(%rsp),%rax
vmovdqa 0x70(%rsp),%xmm0
vmovdqa %xmm0,(%rax)
lea    0xf0(%rsp),%rax
add    $0x10,%rax
mov    %rax,0x20(%rsp)

你怎么看 ?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM