簡體   English   中英

嘗試使用x86 asm SSSE3將大字節序轉換為小字節序

[英]Trying to convert big to little endian with x86 asm SSSE3

我已經做了一段時間的arm asm,並嘗試使用x86 asm ssse3優化簡單的循環。 我找不到將大字節序轉換為小字節序的方法。

ARM NEON僅具有一個向量指令即可完成此操作,而SSSE3沒有。 我嘗試使用2個移位,或一個或,但是如果我們向左移動8個(數據飽和),則需要將每個插槽的位數改為32位而不是16位。

我調查了PSHUFB,但使用它時,16位字的前半部分始終為0。

我在Android的x86上使用嵌入式asm。 很抱歉出現不正確的語法或其他錯誤,請理解我的意思(很難將其從我的代碼中刪除)。

# Data
uint16_t dataSrc[] = {0x7000, 0x4401, 0x3801, 0xf002, 0x4800, 0xb802, 0x1800, 
0x3c00, 0xd800.....
uint16_t* src = dataSrc;
uint8_t * dst = new uint8_t[16] = {0};
uint8_t * map = new uint8_t[16] = { 9,8, 11,10, 13,12, 15,14, 1,0,3,2,5,4,7,6,};

# I need to convert 0x7000 to 0x0077 by shifting each 16 bit by its byte vectorized.

asm volatile (
        "movdqu     (%0),%%xmm1\n"
        "pshufb     %2,%%xmm1\n"
        "movdqu     %%xmm1,(%1)\n"
:   "+r" (src),
"+r" (dst),
"+r" (map)
:
:   "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4"
);

如果我遍歷dataSrc變量,則前8個字節的輸出為:

0: 0
1: 0
2: 0
3: 0
4: 72
5: 696
6: 24
7: 60

即使最后4個順序錯誤,也只能交換。 為什么前4個全為零? 無論我如何更改地圖,第一個有時有時為0,接下來的3總是為零,為什么? 難道我做錯了什么?

編輯

我弄清楚了為什么它不起作用,地圖沒有正確傳遞到內聯匯編中,我不得不為其釋放一個輸入變量。

對於本征與手寫匯編的其他問題。 下面的代碼是將16字節視頻幀數據YUV42010BE轉換為YUVP420(8位),問題是隨機播放,如果我使用little endian,那么我將沒有該部分。

static const char map[16] = { 9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6 };
int dstStrideOffset = (dstStride - srcStride / 2);
asm volatile (
    "push       %%ebp\n"

    // All 0s for packing
    "xorps      %%xmm0, %%xmm0\n"

    "movdqu     (%5),%%xmm4\n"

    "yloop:\n"

    // Set the counter for the stride
    "mov %2,    %%ebp\n"

    "xloop:\n"

    // Load source data
    "movdqu     (%0),%%xmm1\n"
    "movdqu     16(%0),%%xmm2\n"
    "add        $32,%0\n"

    // The first 4 16-bytes are 0,0,0,0, this is the issue.
    "pshufb      %%xmm4, %%xmm1\n"
    "pshufb      %%xmm4, %%xmm2\n"

    // Shift each 16 bit to the right to convert
    "psrlw      $0x2,%%xmm1\n"
    "psrlw      $0x2,%%xmm2\n"

    // Merge both 16bit vectors into 1 8bit vector
    "packuswb   %%xmm0, %%xmm1\n"
    "packuswb   %%xmm0, %%xmm2\n"
    "unpcklpd   %%xmm2, %%xmm1\n"

    // Write the data
    "movdqu     %%xmm1,(%1)\n"
    "add        $16, %1\n"

    // End loop, x = srcStride; x >= 0 ; x -= 32
    "sub        $32, %%ebp\n"
    "jg         xloop\n"

    // End loop, y = height; y >= 0; --y
    "add %4,    %1\n"
    "sub $1,    %3\n"
    "jg         yloop\n"

    "pop        %%ebp\n"
:   "+r" (src),
    "+r" (dst),
    "+r" (srcStride),
    "+r" (height),
    "+r"(dstStrideOffset)
:   "x"(map)
:   "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4"
);

我還沒有使用Little Endian來實現內在函數的改組

const int dstStrideOffset = (dstStride - srcStride / 2);
__m128i mdata, mdata2;
const __m128i zeros = _mm_setzero_si128();
for (int y = height; y > 0; --y) {
    for (int x = srcStride; x > 0; x -= 32) {
        mdata = _mm_loadu_si128((const __m128i *)src);
        mdata2 = _mm_loadu_si128((const __m128i *)(src + 8));
        mdata = _mm_packus_epi16(_mm_srli_epi16(mdata, 2), zeros);
        mdata2 = _mm_packus_epi16(_mm_srli_epi16(mdata2, 2), zeros);
        _mm_storeu_si128( (__m128i *)dst, static_cast<__m128i>(_mm_unpacklo_pd(mdata, mdata2)));
        src += 16;
        dst += 16;
    }
    dst += dstStrideOffset;
}

可能未正確編寫,但在Android模擬器(API 27)上進行了基准測試,x86(SSSE3最高,i686)具有默認的編譯器設置,並添加了諸如此類的優化(盡管性能沒有差異) -Ofast -O3 -funroll-loops -mssse3- mfpmath = sse平均:

內部特征:1.9-2.1毫秒手寫:0.7-1毫秒

有沒有辦法加快速度? 也許我寫了本征函數是錯誤的,是否有可能使本征函數的手寫速度更快?

您的代碼無效,因為您將map的地址傳遞給了pshufb 我不確定gcc為此會生成什么代碼,我無法想象它會完全編譯。

通常將內聯匯編用於此類事情不是一個好主意。 而是使用內部函數:

#include <immintrin.h>

void byte_swap(char dst[16], const char src[16])
{
    __m128i msrc, map, mdst;

    msrc = _mm_loadu_si128((const _m128i *)src);
    map = _mm_setr_epi8(9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6);
    mdst = _mm_shuffle_epi8(msrc, map);
    _mm_storeu_si128((_m128i *)dst, mdst);
}

除了易於維護之外,由於取消了內聯匯編的鏈接,因此優化效果更好,因為編譯器可以內省內部函數並做出明智的決策,以發出哪些指令。 例如,在AVX目標上,它可能會發出VEX編碼的vpshufb而不是pshufb以避免由於AVX / SSE轉換而停頓。

如果由於某種原因您不能使用內部函數,請使用如下內聯匯編:

void byte_swap(char dst[16], const char src[16])
{
    typedef long long __m128i_u __attribute__ ((__vector_size__ (16), __may_alias__, __aligned__ (1)));
    static const char map[16] = { 9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6 };
    __m128i_u data = *(const __m128i_u *)src;

    asm ("pshufb %1, %0" : "+x"(data) : "xm"(* (__m128i_u *)map));
   *(__m128i_u *)dst = data;
}

暫無
暫無

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

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