LoginSignup
5
6

SSEでエンディアン変換

Last updated at Posted at 2014-04-30

サラリと検索して見当たらなかったので、覚書として、
SSEでエンディアンを変換したいときにどうすればいいか。

SSE2までのSSE命令を使っています。
コンパイラはVisualC++を使っています。
構文チェックをしていないので、セミコロンなどが抜けてるかもしれません。

16bitづつをひっくり返す場合。

  1. _mm_sll_epi16()及び_mm_srl_epi16()で16bitづつに区切って8bitづつシフト。
  2. それらをORで結合して出来上がり。
swap_word.cpp
/**
 * change byte endian in 8 words
 *
 * @note src = 1,2,3,4, ..., 16
 *       srca = 0,1,0,3, ..., 15
 *       srcb = 2,0,4,0, ..., 0
 *       result = 2,1,4,3, ..., 15
 */
__m128i swap_epi16(__m128i src)
{
  __declspec(align(16)) count8[2] = {
    8, 8
  };
  __m128i srca = _mm_sll_epi16(src, *(__m128i*)count8);
  __m128i srcb = _mm_srl_epi16(src, *(__m128i*)count8);
  __m128i result = _mm_or_si128(srca, srcb);

  return result;
}

32bitづつをひっくり返す場合。

  1. _mm_sll_epi16()及び_mm_srl_epi16()で16bitづつに区切って8bitづつシフト。
  2. それらをORで結合して第1段階完了。
  3. 更にそれを32bitづつに区切って16bitづつシフト。
  4. それらをORで結合して出来上がり。
swap_dword.cpp
/**
 * change byte endian in 4 dwords
 *
 * @note src = 1,2,3,4, ..., 16
 *       srca = 0,1,0,3, ..., 15
 *       srcb = 2,0,4,0, ..., 0
 *       srcc = 2,1,4,3, ..., 15
 *       srca = 0,0,2,1, ..., 13
 *       srcb = 4,3,0,0, ..., 0
 *       result = 4,3,2,1, ..., 13
 */
__m128i swap_epi32(__m128i src)
{
  __declspec(align(16)) count8[2] = {
    8, 8
  };
  __declspec(align(16)) count16[2] = {
    16, 16
  };
  __m128i srca = _mm_sll_epi16(src, *(__m128i*)count8);
  __m128i srcb = _mm_srl_epi16(src, *(__m128i*)count8);
  __m128i srcc = _mm_or_si128(srca, srcb);
  srca = _mm_sll_epi32(srcc, *(__m128i*)count16);
  srcb = _mm_srl_epi32(srcc, *(__m128i*)count16);
  //__m128i result = _mm_or_si128(src1a, src1b);  誤り
  __m128i result = _mm_or_si128(srca, srcb);

  return result;
}

count8をレジスタに読み込んどいたほうが速そうとか、count16はcount8を2倍(1bit左シフト)すれば使えるとかはお任せします。

64bitづつをひっくり返す場合。

  1. _mm_sll_epi16()及び_mm_srl_epi16()で16bitづつに区切って8bitづつシフト。
  2. それらをORで結合して第1段階完了。
  3. 更にそれを32bitづつに区切って16bitづつシフト。
  4. それらをORで結合して第2段階完了。
  5. 更にそれを64bitづつに区切って32bitづつシフト。
  6. それらをORで結合して出来上がり。
swap_qword.cpp
/**
 * change byte endian in 2 qwords
 *
 * @note src = 1,2,3,4, ..., 16
 *       srca = 0,1,0,3, ..., 15
 *       srcb = 2,0,4,0, ..., 0
 *       srcc = 2,1,4,3, ..., 15
 *       srca = 0,0,2,1, ..., 13
 *       srcb = 4,3,0,0, ..., 0
 *       srcc = 4,3,2,1, ..., 13
 *       srca = 0,0,0,0, ..., 9
 *       srcb = 8,7,6,5, ..., 0
 *       result = 8,7,6,5, ..., 9
 */
__m128i swap_epi64(__m128i src)
{
  __declspec(align(16)) count8[2] = {
    8, 8
  };
  __declspec(align(16)) count16[2] = {
    16, 16
  };
  __declspec(align(16)) count32[2] = {
    32, 32
  };
  __m128i srca = _mm_sll_epi16(src, *(__m128i*)count8);
  __m128i srcb = _mm_srl_epi16(src, *(__m128i*)count8);
  __m128i srcc = _mm_or_si128(srca, srcb);
  srca = _mm_sll_epi32(srcc, *(__m128i*)count16);
  srcb = _mm_srl_epi32(srcc, *(__m128i*)count16);
  srcc = _mm_or_si128(srca, srcb);
  srca = _mm_sll_epi64(srcc, *(__m128i*)count32);
  srcb = _mm_srl_epi64(srcc, *(__m128i*)count32);
  //__m128i result = _mm_or_si128(src1a, src1b);  誤り
  __m128i result = _mm_or_si128(srca, srcb);

  return result;
}

count8をレジスタに読み込んどいたほうが速そうとか、count32/count16はcount8を4/2倍(2/1bit左シフト)すれば使えるとかはお任せします。

avxで4バイトづつ(2023/06/21追記)

最近のCPUはavxまで載っていたりするので、shuffle命令版。

swap_4byte_avx.c
__m256i swap_epi32_avx(__m256i src)
    __m256i ptn = _mm256_set_epi32(
        0x0C0D0E0F, 0x08090A0B, 0x04050607, 0x00010203,
        0x0C0D0E0F, 0x08090A0B, 0x04050607, 0x00010203);
    return _mm256_shuffle_epi8(src, ptn);
}

__m128i swap_epi32_sse(__m128i src)
    __m128i ptn = _mm_set_epi32(0x0C0D0E0F, 0x08090A0B, 0x04050607, 0x00010203);
    // __m128i ptn = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
    return _mm_shuffle_epi8(src, ptn);
}

参考

こちらをかなり参考にさせて頂いています。--> http://www.officedaytime.com/tips/simd.html

余談

  • Visual C++であれば、_byteswap_uint64(), _byteswap_ulong(), _byteswap_ushort()といった便利な関数があるので、SIMDにしなくていい時はこれを使うのが良いのではないかと。
  • この調子だと、4bitづつのスワップも出来るのかと思ったら、8bit単位のビットシフト命令は無かったw
5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6