2
2

作った RFC 1321 (MD5) のプログラムの改良を試みる

Last updated at Posted at 2021-08-26

MD5: RFC 1321 を実装してみる」にある C++ プログラムを弄って改良を試みます。

**オリジナル・プログラム (テスト部分は改良版と同じものに変更)**
md5bs_orig.cpp
//
// ヘッダ用
//

#include <cstddef>
#include <cstdint>

/*
 * MD5ByteSum
 */

class MD5ByteSum
{
public:
    typedef uint32_t State[4];
    typedef uint32_t Block[16];
    typedef uint8_t Digest[16];
    typedef char HexDigest[33];

protected:
    static const uint8_t padding[64];

    State state;
    uint32_t bytes;
    Block input;
    bool active;

public:
    MD5ByteSum();
    MD5ByteSum(const void *message, size_t size);

    void digest(Digest &out);
    void hexdigest(HexDigest &out);

    void initialize();
    void update(const void *message, size_t size);
    void finalize();

protected:
    void update_state();
};

/*
 * MD5ByteSum - inline
 */

inline MD5ByteSum::MD5ByteSum()
{
    initialize();
}

//
// コンパイル用
//

/*
 * MD5ByteSum::State - update 16-Word block
 */

inline static uint32_t ROL(uint32_t x, int s)
{
    return ((x << s) | (x >> (32 - s)));
}

inline static uint32_t get_block_index(uint32_t index)
{
    /*
     * [y|y|x|x|x|x]
     *  (index * 1 + 0) & 15
     *  (index * 5 + 1) & 15
     *  (index * 3 + 5) & 15
     *  (index * 7 + 0) & 15
     */
    uint32_t x = index & 0x0f;
    uint32_t y = index & 0x30;
    uint32_t y1 = index & 0x10;
    uint32_t y2 = index & 0x20;
    uint32_t xmul = (y1 >> 2) | (y2 >> 4) | 1;
    uint32_t offs = (0x0510 >> (y >> 2)) & 7;
    return (x * xmul + offs) & 15;
}

inline static int get_rotate_bits(uint32_t index)
{
    /*
     * [y|y|.|.|x|x]
     *  7 12 17 22 : 3 8 13 18 : 3 3 3 3 : 3 3 3 2
     *  5  9 14 20 : 1 5 10 16 : 1 0 0 1 : 1 0 0 0
     *  4 11 16 23 : 0 7 12 19 : 0 2 2 4 : 0 2 2 3
     *  6 10 15 21 : 2 6 11 17 : 2 1 1 2 : 2 1 1 1
     *              -4          -(x*5)    -(x==3)
     *
     *  11 11 11 10 : 10_11_11_11 = bf
     *  01 00 00 00 : 00_00_00_01 = 01
     *  00 10 10 11 : 11_10_10_00 = e8
     *  10 01 01 01 : 01_01_01_10 = 56
     */
    uint32_t x = index & 0x03;
    uint32_t y = index & 0x30;
    uint32_t p = x | (y >> 2);
    uint32_t x0 = (0x56e801bf >> (p << 1)) & 3;
    uint32_t x3 = (x + 1) >> 2; // (x == 3) ? 1 : 0;
    uint32_t x5 = x * 5;
    return x0 + x3 + x5 + 4;
}

inline static uint32_t get_state_index(uint32_t index, uint32_t element)
{
    /*
     *  0 1 2 3
     *  3 0 1 2
     *  2 3 0 1
     *  1 2 3 0
     */
    return ((index * 3) + element) & 3;
}

inline static uint32_t md5bs_state_func(uint32_t index, uint32_t x, uint32_t y, uint32_t z)
{
    switch ((index >> 4) & 3)
    {
    case 0: return (y & x) | (z & ~x);
    case 1: return (x & z) | (y & ~z);
    case 2: return x ^ y ^ z;
    case 3: return y ^ (x | ~z);
    }
    return 0;
}

inline static void md5bs_state_update(uint32_t index, uint32_t *S, const MD5ByteSum::Block &X, uint32_t T)
{
    uint32_t a = get_state_index(index, 0);
    uint32_t b = get_state_index(index, 1);
    uint32_t c = get_state_index(index, 2);
    uint32_t d = get_state_index(index, 3);
    uint32_t x = get_block_index(index);
    uint32_t r = get_rotate_bits(index);

    S[a] = S[b] + ROL(X[x] + T + S[a] + md5bs_state_func(index, S[b], S[c], S[d]), r);
}

inline static void md5bs_state_update(uint32_t index, uint32_t *S,
                                      const MD5ByteSum::Block &X,
                                      uint32_t T1, uint32_t T2,
                                      uint32_t T3, uint32_t T4)
{
    md5bs_state_update(index + 0, S, X, T1);
    md5bs_state_update(index + 1, S, X, T2);
    md5bs_state_update(index + 2, S, X, T3);
    md5bs_state_update(index + 3, S, X, T4);
}

void MD5ByteSum::update_state()
{
    State p;

    p[0] = state[0];
    p[1] = state[1];
    p[2] = state[2];
    p[3] = state[3];

    // Round 1.
    md5bs_state_update( 0 * 4, state, input, 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee);
    md5bs_state_update( 1 * 4, state, input, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501);
    md5bs_state_update( 2 * 4, state, input, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be);
    md5bs_state_update( 3 * 4, state, input, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821);

    // Round 2.
    md5bs_state_update( 4 * 4, state, input, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa);
    md5bs_state_update( 5 * 4, state, input, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8);
    md5bs_state_update( 6 * 4, state, input, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed);
    md5bs_state_update( 7 * 4, state, input, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a);

    // Round 3.
    md5bs_state_update( 8 * 4, state, input, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c);
    md5bs_state_update( 9 * 4, state, input, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70);
    md5bs_state_update(10 * 4, state, input, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05);
    md5bs_state_update(11 * 4, state, input, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665);

    // Round 4.
    md5bs_state_update(12 * 4, state, input, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039);
    md5bs_state_update(13 * 4, state, input, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1);
    md5bs_state_update(14 * 4, state, input, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1);
    md5bs_state_update(15 * 4, state, input, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391);

    state[0] += p[0];
    state[1] += p[1];
    state[2] += p[2];
    state[3] += p[3];
}

/*
 * MD5ByteSum
 */

const uint8_t MD5ByteSum::padding[64] = { 0x80 };

MD5ByteSum::MD5ByteSum(const void *message, size_t size)
{
    initialize();
    update(message, size);
    // finalize();
}

void MD5ByteSum::initialize()
{
    state[0] = 0x67452301;
    state[1] = 0xefcdab89;
    state[2] = 0x98badcfe;
    state[3] = 0x10325476;
    bytes = 0;
    for (int i = 0; i < 16; i++)
        input[i] = 0;
    active = true;
}

void MD5ByteSum::finalize()
{
    if (!active)
        return;

    uint32_t l = bytes;
    uint32_t i = l & 0x3f;
    uint32_t n = ((i >= 56) ? 64 : 0) + 56 - i;

    update(padding, n);

    input[14] = l << 3;
    input[15] = l >> 29;
    update_state();

    active = false;
}

void MD5ByteSum::update(const void *message, size_t size)
{
    if (!active)
        initialize();

    const uint8_t *d = reinterpret_cast<const uint8_t *>(message);
    unsigned b = (bytes & 3) << 3;
    unsigned i = (bytes >> 2) & 0x0f;

    bytes += size;
    while (size-- > 0)
    {
        input[i] |= (*d++) << b;

        b = (b + 8) & 0x1f;
        if (b == 0)
        {
            i = (i + 1) & 0x0f;
            if (i == 0)
                update_state();
            input[i] = 0;
        }
    }
}

void MD5ByteSum::digest(Digest &out)
{
    finalize();

    for (int p = 0; p < 4; p++)
    {
        uint32_t s = state[p];

        for (int q = 0; q < 4; q++, s >>= 8)
            out[p * 4 + q] = uint8_t(s);
    }
}

void MD5ByteSum::hexdigest(HexDigest &out)
{
    finalize();

    for (int p = 0; p < 4; p++)
    {
        uint32_t s = state[p];

        for (unsigned q = 0; q < 4; q++, s >>= 8)
        {
            char h = (s >> 4) & 0x0f;
            char l = (s >> 0) & 0x0f;
            out[p * 8 + q * 2 + 0] = h + ((h < 10) ? '0' : ('a' - 10));
            out[p * 8 + q * 2 + 1] = l + ((l < 10) ? '0' : ('a' - 10));
        }
    }
    out[32] = 0;
}

//
// テスト用
//

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cinttypes>
#include <time.h>

inline uint64_t RDTSC()
{
#if defined(_MSC_VER) ||   \
    (defined(__clang__) && \
     (defined(__amd64) ||  \
      defined(__i386) ||   \
      defined(__x86) ||    \
      defined(__x86_64)))

    return __rdtsc();

#elif defined(__GNUC__) && \
    (defined(__amd64) ||   \
     defined(__i386) ||    \
     defined(__x86) ||     \
     defined(__x86_64))

    uint32_t eax, edx;
    __asm__ volatile ("RDTSC" : "=a" (eax), "=d" (edx));
    return ((uint64_t)edx << 32) | eax;

#else

    timespec c;
    clock_gettime(CLOCK_REALTIME, &c);
    return ((uint64_t)c.tv_sec * 1000 * 1000 * 1000 + c.tv_nsec);

#endif
}

static void prepare_message(uint8_t *d, size_t dlen, const uint8_t *s, size_t slen)
{
    size_t clen = slen;
    if (clen > dlen)
        clen = dlen;
    std::memcpy(d, s, clen);
    d += clen;
    dlen -= clen;
    if (dlen != 0)
        prepare_message(d, dlen, s, slen + clen);
}

static int string_message(const char *message, const char *digest = NULL)
{
    size_t msglen = strlen(message);
    MD5ByteSum md5(message, msglen);
    MD5ByteSum::HexDigest res;

    md5.hexdigest(res);
    printf("MD5 (\"%s\") = %s\n", message, res);
    if (!digest)
        return 0;
    if (!strcmp(digest, res))
        return 0;
    printf("BUG: %s != %s\n", digest, res);
    return 2;
}

static int file_message(const char *path)
{
    MD5ByteSum md5;
    MD5ByteSum::HexDigest res;

    size_t size = 1024 * 1024;
    void *buffer = malloc(size);

    if (!buffer)
    {
        printf("not enough memory.");
        return 2;
    }

    bool success = true;
    FILE *fp = fopen(path, "rb");

    if (!fp)
    {
        printf("open error: %s\n", path);
        return 2;
    }
    while (!feof(fp))
    {
        size_t rdsz = fread(buffer, 1, size, fp);
        if (ferror(fp))
        {
            success = false;
            break;
        }
        if (!rdsz)
            break;
        md5.update(buffer, rdsz);
    }
    fclose(fp);
    free(buffer);
    if (!success)
    {
        printf("read error: %s\n", path);
        return 2;
    }

    md5.hexdigest(res);
    printf("MD5 (%s) = %s\n", path, res);

    return 0;
}

static int benchmark(const char *message)
{
    if (!message)
        message = "benchmark";
    int rval = string_message(message);
    if (rval)
        return rval;

    size_t msglen = strlen(message);
    size_t blocks = (4 * 1024 * 1024);
    size_t bufsize = sizeof(MD5ByteSum::Block) * blocks;
    uint8_t *buffer = (uint8_t *)malloc(bufsize);
    if (!buffer)
    {
        puts("not enough memory.");
        return 2;
    }
    if (msglen)
    {
        uint8_t *d = buffer;
        std::memcpy(d, message, msglen);
        prepare_message(d + msglen, bufsize - msglen, buffer, msglen);
    }
    else
    {
        std::memset(buffer, -1, bufsize);
    }

    MD5ByteSum md5;
    MD5ByteSum::HexDigest res;

    uint64_t s_tsc = RDTSC();
    {
        const uint8_t *p = buffer;
        size_t s = bufsize;

#define STEP(n) md5.update(p, n); p += n; s -= n;
        // 動作テスト用に分解(計測への影響は極小)
        STEP(17);     // 0
        STEP(17);     // 17
        STEP(63);     // 34
        STEP(31+128); // 97
        STEP(s);
    }
    uint64_t e_tsc = RDTSC();
    uint64_t d_tsc = e_tsc - s_tsc;
    uint64_t ave_i = d_tsc / blocks;
    uint64_t ave_f = (100 * (d_tsc % blocks) + (blocks / 2)) / blocks;

    md5.hexdigest(res);
    printf("TEST digest: %s\n", res);
    // printf("timer start  : %" PRIu64 "\n", s_tsc);
    // printf("timer end    : %" PRIu64 "\n", e_tsc);
    printf("timer count  : %" PRIu64 "\n", d_tsc);
    printf("block average: %" PRIu64 ".%02" PRIu64 "\n", ave_i, ave_f);

    free(buffer);
    return 0;
}

static int test()
{
    static const char *tab[] = {
        "d41d8cd98f00b204e9800998ecf8427e", "",
        "0cc175b9c0f1b6a831c399e269772661", "a",
        "900150983cd24fb0d6963f7d28e17f72", "abc",
        "f96b697d7cb7938d525a2f31aaf161d0", "message digest",
        "c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz",
        "d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
        "ad76b175d22a98a4e3bc2ad72affc53e", "01234567890123456789012345678901234567890123456789012345678",
        NULL, NULL,
    };
    for (int i = 0; tab[i] != NULL; i += 2)
    {
        int res = string_message(tab[i + 1], tab[i + 0]);
        if (res != 0)
            return res;
    }
    return 0;
}

static const char *program;
static int usage()
{
    printf("Usage:\n"
           "  %s -b [message]\n"
           "  %s -f file\n"
           "  %s message\n",
           program, program, program);
    return 1;
}

int main(int argc, char **argv)
{
    program = argv[0];
    if (argc < 2)
        return usage();
    if (!strcmp(argv[1], "-b"))
    {
        if (argc < 4)
            return benchmark(argv[2]);
        return usage();
    }
    if (!strcmp(argv[1], "-f"))
    {
        if (argc == 3)
            return file_message(argv[2]);
        return usage();
    }
    if (!strcmp(argv[1], "-t"))
        return test();
    if (argc == 2)
        return string_message(argv[1]);
    return usage();
}

改良の試み

主に、メモリへのアクセスのパターンの変更と SIMD レジスタの使用ですが、できるだけコンパイラの最適化に依存します。

**改良を試みたプログラム**
md5bs.cpp
///////////////////////////////////////////////////////////////////////////////
//
// ヘッダ ファイル用
//
///////////////////////////////////////////////////////////////////////////////

#include <cstddef>
#include <cstdint>

/*
 * alignas(n) : バイト境界指定
 *   x86/x64 では alignas(16) で XMM レジスタの活用を期待する
 */

#if (__cplusplus < 201103L) && !defined(alignas)
#  if defined(__GNUC__)
#    define alignas(n)  __attribute__((__aligned__(n)))
#  elif defined(_MSC_VER)
#    define alignas(n)  __declspec(align(n))
#  else
#    define alignas(n)  /**/
#  endif
#endif

/*
 * MD5ByteSum クラス
 *   アプリケーションは、コンストラクタ,update,digest,hexdigest の呼び出しだけでよい
 *   initialize, finalize は必要に応じて自動的に呼び出される
 */
class MD5ByteSum
{
public:
    typedef uint32_t State[4];
    typedef uint32_t Block[16];
    typedef uint8_t Digest[16];
    typedef char HexDigest[33];

protected:
    alignas(64) Block input;
    alignas(64) Block adbuf[4];
    alignas(16) State state;
    uint32_t bytes;
    bool active;

public:
    MD5ByteSum();
    MD5ByteSum(const void *message, size_t size);

    void update(const void *message, size_t size);
    void digest(Digest &out);
    void hexdigest(HexDigest &out);

    void initialize();
    void finalize();

protected:
    void update_state();
    void update_state(const void *block);
};

/*
 * MD5ByteSum (inline)
 */

// コンストラクタ
inline MD5ByteSum::MD5ByteSum()
{
    initialize();
}

// ダイジェスト値(state)の更新:
//   入力するブロックは input メンバー変数を使う
inline void MD5ByteSum::update_state()
{
    update_state(input);
}

///////////////////////////////////////////////////////////////////////////////
//
// コンパイル ファイル用
//
///////////////////////////////////////////////////////////////////////////////

#include <cstring>

// ベクトル型の代用
namespace // anonymous
{
    template <typename T, int N>
    struct md5bs_vector
    {
        typedef md5bs_vector<T,N> self_type;

        T value[N];

        T &operator [](int n)
            { return value[n]; }
        T operator [](int n) const
            { return value[n]; }
        self_type operator +(const self_type &rhs) const
            {
                self_type r;

                for (int i = 0; i < N; i++)
                    r[i] = value[i] + rhs[i];
                return r;
            }
    };
}

/*
 * MD5BS_ISA_ARM : ARM 命令セット
 * MD5BS_ISA_X86 : x86 命令セット
 */
#if defined(__arm__)
#  define MD5BS_ISA_ARM  1
#endif
#if (defined(__amd64)  || \
     defined(__i386)   || \
     defined(__x86)    || \
     defined(__x86_64))
#  define MD5BS_ISA_X86  1
#endif

#ifndef MD5BS_ISA_ARM
#  define MD5BS_ISA_ARM  0
#endif
#ifndef MD5BS_ISA_X86
#  define MD5BS_ISA_X86  0
#endif

/*
 * __dummy_access    : コンパイラの最適化抑制用
 * __force_inline    : 強制インライン展開のお呪い
 * __no_inline       : インライン展開抑制のお呪い
 * md5bs_clear_cache : キャッシュのクリア
 * md5bs_prefetch    : キャッシュへの先読み
 */
#if defined(__GNUC__)
#  define __force_inline  __attribute__((always_inline)) inline
#  define __no_inline  __attribute__((noinline))
#  define md5bs_clear_cache(s,e)  __builtin___clear_cache((char*)(s), (char*)(e))
#  define md5bs_prefetch(p)  __builtin_prefetch(p)
#elif defined(_MSC_VER)
#  define __force_inline  __forceinline
#  define __no_inline     __noinline
#endif

#if !defined(__dummy_access)
#  if defined(__GNUC__)
#    define __dummy_access(m)  __asm__ volatile ("#dummy %0":/**/:"m" (m))
#  else
#    define __dummy_access(m)
#  endif
#endif
#if !defined(__force_inline)
#  define __force_inline  inline
#endif
#if !defined(__no_inline)
#  define __no_inline
#endif
#if !defined(md5bs_clear_cache)
#  define md5bs_clear_cache(s,e)  /*NOOP*/
#endif
#if !defined(md5bs_prefetch)
#  define md5bs_prefetch(p)  /*NOOP*/
#endif

/*
 * MD5BS_BIG_ENDIAN        : ビッグ・エンディアン
 * MD5BS_ADDRESS_ALIGN     : バイト境界アクセスが必要
 * MD5BS_CACHE_LINE_SIZE   : キャッシュのライン・サイズ
 * MD5BS_BLOCK_PREPARE     : int(abs(sin(i))*(1<<32)) の加算済みブロックを用意
 * MD5BS_BLOCK_X_ORDER     : 加算済みブロックの配列を T の並びから X の並びに変更
 * MD5BS_HAS_SIMD          : SIMD 機能を明示的に使用
 * MD5BS_SIMD_REGISTER     : コア・レジスタの代わりに SIMD レジスタを使用
 *       良 : 使用出来るレジスタが増えてスタック・フレームへのアクセスが減る場合がある
 *       悪 : SIMD 命令がコア・レジスタの命令よりスループットが遅い場合がある
 * MD5BS_SIMD_REGISTER_128 : 128ビット・レジスタの有無
 * MD5BS_SIMD_REGISTER_256 : 256ビット・レジスタの有無
 */

/*
 * ARM
 */

#if MD5BS_ISA_ARM
#  if !defined(MD5BS_CACHE_LINE_SIZE)
#    define MD5BS_CACHE_LINE_SIZE  32
#  endif
#  if defined(__ARM_NEON__) && !defined(MD5BS_HAS_SIMD)
#    define MD5BS_HAS_SIMD  1
#    define MD5BS_ISA_ARM_NEON  1
#    define MD5BS_BLOCK_PREPARE_ARM  0

#    include <arm_neon.h>

typedef uint32x2_t md5bs_simd2_t;
typedef uint32x4_t md5bs_simd4_t;
typedef md5bs_simd2_t md5bs_alt_uint32_t;

#  endif
#  if !defined(MD5BS_BLOCK_PREPARE_ARM)
#    define MD5BS_BLOCK_PREPARE_ARM  0
#  endif
#  if !defined(MD5BS_BLOCK_PREPARE)
#    define MD5BS_BLOCK_PREPARE  MD5BS_BLOCK_PREPARE_ARM
#  endif
#endif

/*
 * x86
 */

#if MD5BS_ISA_X86
#  define MD5BS_BIG_ENDIAN  0
#  if defined(__SSE4_1__) && !defined(MD5BS_HAS_SIMD)
#    define MD5BS_HAS_SIMD  1
#    define MD5BS_ISA_X86_SSE4_1  1
#    if !defined(MD5BS_CACHE_LINE_SIZE)
#      define MD5BS_CACHE_LINE_SIZE  64
#    endif
#    define MD5BS_BLOCK_PREPARE_X86  1
#    if !defined(MD5BS_BLOCK_X_ORDER)
#      define MD5BS_BLOCK_X_ORDER  1
#    endif
#    if defined(__clang__) && !defined(MD5BS_SIMD_REGISTER)
#      define MD5BS_SIMD_REGISTER  1
#    endif

#    include <immintrin.h>

typedef __v2si md5bs_simd2_t;
typedef __v4su md5bs_simd4_t;
typedef md5bs_simd4_t md5bs_alt_uint32_t;

#    if defined(__AVX__)
#      define MD5BS_SIMD_REGISTER_256 1

typedef __v8su md5bs_simd8_t;

#    endif

#  elif defined(__clang__)
#    define MD5BS_BLOCK_PREPARE_X86  1
#  endif
#  if !defined(MD5BS_BLOCK_PREPARE_X86)
#    define MD5BS_BLOCK_PREPARE_X86  0
#  endif
#  if !defined(MD5BS_BLOCK_PREPARE)
#    define MD5BS_BLOCK_PREPARE  MD5BS_BLOCK_PREPARE_X86
#  endif
#endif

/*
 * 未定義の解消
 */

#ifndef MD5BS_BIG_ENDIAN
#  if (defined(__BYTE_ORDER__) && \
       (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
#    define MD5BS_BIG_ENDIAN  1
#  else
#    define MD5BS_BIG_ENDIAN  0
#  endif
#endif
#ifndef MD5BS_ADDRESS_ALIGN
#  define MD5BS_ADDRESS_ALIGN  0
#endif
#ifndef MD5BS_CACHE_LINE_SIZE
#  define MD5BS_CACHE_LINE_SIZE  0
#endif
#ifndef MD5BS_BLOCK_PREPARE
#  define MD5BS_BLOCK_PREPARE  0
#endif
#ifndef MD5BS_BLOCK_X_ORDER
#  define MD5BS_BLOCK_X_ORDER  0
#endif

#ifndef MD5BS_HAS_SIMD
#  define MD5BS_HAS_SIMD  0
#endif
#ifndef MD5BS_SIMD_REGISTER
#  define MD5BS_SIMD_REGISTER  0
#elif !defined(MD5BS_SIMD_REGISTER_128)
#  define MD5BS_SIMD_REGISTER_128 1
#endif
#ifndef MD5BS_SIMD_REGISTER_128
#  define MD5BS_SIMD_REGISTER_128 0
#endif
#ifndef MD5BS_SIMD_REGISTER_256
#  define MD5BS_SIMD_REGISTER_256 0
#endif

#if !MD5BS_HAS_SIMD
typedef md5bs_vector<uint32_t,2> md5bs_simd2_t;
typedef md5bs_vector<uint32_t,4> md5bs_simd4_t;
typedef uint32_t md5bs_uint32_t;
#elif !MD5BS_SIMD_REGISTER /*  && MD5BS_HAS_SIMD */
typedef uint32_t md5bs_uint32_t;
#else /* MD5BS_HAS_SIMD && MD5BS_SIMD_REGISTER */
typedef md5bs_alt_uint32_t md5bs_uint32_t;
#endif
#if !MD5BS_SIMD_REGISTER_256
typedef md5bs_vector<md5bs_simd4_t,2> md5bs_simd8_t;
#endif

// 32ビットをリトル・エンディアンとして読み込む関数型
typedef uint32_t (*md5bs_load32)(const uint32_t &, bool);

// ポインタ型を uint8_t * 型に変換
__force_inline
static uint8_t *md5bs_uint8_p(void *p)
{
    return reinterpret_cast<uint8_t *>(p);
}

// const ポインタ型を const uint8_t * 型に変換
__force_inline
static const uint8_t *md5bs_uint8_p(const void *p)
{
    return reinterpret_cast<const uint8_t *>(p);
}

// const ポインタ型を const uint32_t * 型に変換
__force_inline
static const uint32_t *md5bs_uint32_p(const void *p)
{
    return reinterpret_cast<const uint32_t *>(p);
}

// ポインタ型を md5bs_simd4_t * 型に変換
__force_inline
static md5bs_simd4_t *md5bs_simd4_p(void *p)
{
    return reinterpret_cast<md5bs_simd4_t *>(p);
}

// const ポインタ型を const md5bs_simd4_t * 型に変換
__force_inline
static const md5bs_simd4_t *md5bs_simd4_p(const void *p)
{
    return reinterpret_cast<const md5bs_simd4_t *>(p);
}

// ポインタ型を md5bs_simd8_t * 型に変換
__force_inline
static md5bs_simd8_t *md5bs_simd8_p(void *p)
{
    return reinterpret_cast<md5bs_simd8_t *>(p);
}

// const ポインタ型を const md5bs_simd4_t * 型に変換
__force_inline
static const md5bs_simd8_t *md5bs_simd8_p(const void *p)
{
    return reinterpret_cast<const md5bs_simd8_t *>(p);
}

// ポインタ型を intptr_t 型に変換
__force_inline
static intptr_t md5bs_intptr(const void *p)
{
    return reinterpret_cast<intptr_t>(p);
}

// ポインタのアドレスが4バイト境界ならば true
__force_inline
static bool md5bs_is_align4(const void *p)
{
    return ((md5bs_intptr(p) & 3) == 0);
}

// ビッグ・エンディアン構成の CPU 用
//   リトル・エンディアン形式の32ビット整数を取り出す
//   aligned が true の場合は x のアドレスが4バイト境界ではない
//   [最適化によるバイト・オーダー変換命令の使用を期待する]
__force_inline
static uint32_t md5bs_load32be(const uint32_t &x, bool aligned)
{
    uint32_t b0, b1, b2, b3;

    if (aligned)
    {
        b3 = (x >>  0) & 0xff;
        b2 = (x >>  8) & 0xff;
        b1 = (x >> 16) & 0xff;
        b0 = (x >> 24) & 0xff;
    }
    else
    {
        const uint8_t *p = md5bs_uint8_p(&x);

        b0 = p[0];
        b1 = p[1];
        b2 = p[2];
        b3 = p[3];
    }
    return ((b3 << 24) | (b2 << 16) | (b1 << 8) | (b0 << 0));
}

// リトル・エンディアン構成の CPU 用
//   リトル・エンディアン形式の32ビット整数を取り出す
//   [最適化による非境界アクセス命令の使用を期待する]
__force_inline
static uint32_t md5bs_load32le(const uint32_t &x, bool aligned)
{
    if (aligned || !MD5BS_ADDRESS_ALIGN)
        return x;

    const uint8_t *p = md5bs_uint8_p(&x);
    uint32_t b0 = p[0];
    uint32_t b1 = p[1];
    uint32_t b2 = p[2];
    uint32_t b3 = p[3];

    return ((b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24));
}

// 32ビットをリトル・エンディアンとして読み込む関数を得る
__force_inline
static md5bs_load32 md5bs_load32_func(bool endian)
{
    return endian ? md5bs_load32be : md5bs_load32le;
}

/*
 * SIMD 向け
 */

#if MD5BS_HAS_SIMD
#  if MD5BS_ISA_ARM

//
// ARM
//

namespace // anonymous
{
    template <typename T>
    struct vfp_cast_type
    {
        template <typename U>
        __force_inline
        static T from(U s)
        {
#if MD5BS_BIG_ENDIAN
#error "TODO"
#endif
            union
            {
                T d;
                U s;
            } u;

            u.s = s;
            return u.d;
        }
    };

    template <>
    struct vfp_cast_type<uint32_t>
    {
        __force_inline
        static uint32_t from(uint32_t s)
            { return s; }

        template <typename U>
        __force_inline
        static uint32_t from(U s)
            { return s[0]; }
    };

} // anonymous

template <typename dT, typename sT>
__force_inline
static dT vfp_cast(sT s)
{
    return vfp_cast_type<dT>::from(s);
}

template <typename T>
__force_inline
static md5bs_uint32_t md5bs_uint32(T x)
{
    return vfp_cast<md5bs_uint32_t>(x);
}

template <typename T>
__force_inline
static md5bs_simd4_t md5bs_simd(T x)
{
    return vfp_cast<md5bs_simd4_t>(x);
}

#    if MD5BS_SIMD_REGISTER /* ARM */

__force_inline
static uint64x1_t md5bs_vshl_n_s64(uint64x1_t x, int s)
{
    switch (s)
    {
#define MD5BS_VSHL_N_S64_CASE_1(n) case (n): return vshl_n_s64(x, (n))
#define MD5BS_VSHL_N_S64_CASE_2(n)      \
        MD5BS_VSHL_N_S64_CASE_1(n + 0); \
        MD5BS_VSHL_N_S64_CASE_1(n + 1)
#define MD5BS_VSHL_N_S64_CASE_4(n)      \
        MD5BS_VSHL_N_S64_CASE_2(n + 0); \
        MD5BS_VSHL_N_S64_CASE_2(n + 2)
#define MD5BS_VSHL_N_S64_CASE_8(n)      \
        MD5BS_VSHL_N_S64_CASE_4(n + 0); \
        MD5BS_VSHL_N_S64_CASE_4(n + 4)
#define MD5BS_VSHL_N_S64_CASE_16(n)     \
        MD5BS_VSHL_N_S64_CASE_8(n + 0); \
        MD5BS_VSHL_N_S64_CASE_8(n + 8)
#define MD5BS_VSHL_N_S64_CASE_32(n)      \
        MD5BS_VSHL_N_S64_CASE_16(n + 0); \
        MD5BS_VSHL_N_S64_CASE_16(n + 16)

        MD5BS_VSHL_N_S64_CASE_32(0);
        MD5BS_VSHL_N_S64_CASE_32(32);
    }
    return (uint64x1_t){0};
}

__force_inline
static md5bs_simd4_t md5bs_simd_add(const md5bs_simd4_t &a, const md5bs_simd4_t &b)
{
    return vaddq_u32(a, b);
}

__force_inline
static md5bs_alt_uint32_t md5bs_rotate(md5bs_alt_uint32_t x, int s)
{
    x[1] = x[0];
    x = vfp_cast<uint32x2_t>(md5bs_vshl_n_s64(vfp_cast<uint64x1_t>(x), s));
    x[0] = x[1];
    return x;
}

__force_inline
static md5bs_uint32_t md5bs_simd_read(md5bs_simd4_t q, int n)
{
    return vfp_cast<md5bs_uint32_t>(q[n]);
}

__force_inline
static md5bs_simd4_t md5bs_simd_write(md5bs_simd4_t &p, int n, md5bs_uint32_t v)
{
    md5bs_simd4_t r = p;

    r[n] = vfp_cast<uint32_t>(v);
    return r;
}

#    endif
#  elif MD5BS_ISA_X86 /* x86 */

//
// x86
//

// uint32/xmm, xmm/xmm 型の変換
namespace // anonymous
{
    template <typename T>
    struct xmm_cast_type
    {
        __force_inline
        static T from(uint32_t s)
            { return (T)_mm_cvtsi32_si128(s); }
        template <typename U>
        __force_inline
        static T from(U s)
            { return (T)s; }
    };

    template <>
    struct xmm_cast_type<uint32_t>
    {
        __force_inline
        static uint32_t from(uint32_t s)
            { return s; }

        template <typename U>
        __force_inline
        static uint32_t from(U s)
            { return (uint32_t)_mm_cvtsi128_si32(s); }
    };

} // anonymous

template <typename dT, typename sT>
__force_inline
static dT xmm_cast(sT s)
{
    return xmm_cast_type<dT>::from(s);
}

template <typename T>
__force_inline
static __m128 md5bs_m128(T v)
{
    return xmm_cast<__m128>(v);
}

template <typename T>
__force_inline
static __m128i md5bs_m128i(T v)
{
    return xmm_cast<__m128i>(v);
}

template <typename T>
__force_inline
static md5bs_uint32_t md5bs_uint32(T x)
{
    return xmm_cast<md5bs_uint32_t>(x);
}

template <typename T>
__force_inline
static md5bs_simd4_t md5bs_simd(T x)
{
    return xmm_cast<md5bs_simd4_t>(x);
}

#    if MD5BS_SIMD_REGISTER /* x86 */

// xmm レジスタ型へのアクセス
__force_inline
static md5bs_uint32_t md5bs_simd_read(md5bs_simd4_t q, int n)
{
    __m128i p = md5bs_m128i(q);

    switch ((n & 3))
    {
    case 0: return md5bs_uint32(_mm_shuffle_epi32(p, 0x00));
    case 1: return md5bs_uint32(_mm_shuffle_epi32(p, 0x55));
    case 2: return md5bs_uint32(_mm_shuffle_epi32(p, 0xaa));
    case 3: return md5bs_uint32(_mm_shuffle_epi32(p, 0xff));
    }
    return md5bs_uint32(q ^ q); /* 警告抑制用:意味は無い */
    // return 0; // はエラーになる
}

__force_inline
static md5bs_simd4_t md5bs_simd_write(md5bs_simd4_t q, int n, md5bs_simd4_t w)
{
    __m128 p = md5bs_m128(q);
    __m128 v = md5bs_m128(w);

    switch ((n & 3))
    {
    case 0: return md5bs_simd(_mm_insert_ps(p, v, 0x00));
    case 1: return md5bs_simd(_mm_insert_ps(p, v, 0x10));
    case 2: return md5bs_simd(_mm_insert_ps(p, v, 0x20));
    case 3: return md5bs_simd(_mm_insert_ps(p, v, 0x30));
    }
    return (w ^ w); /* 警告抑制用:意味は無い */
    // return 0; // はエラーになる
}

// xmm を使った加算
__force_inline
static md5bs_simd4_t md5bs_simd_add(md5bs_simd4_t a, md5bs_simd4_t b)
{
    return a + b;
}

// xmm を使ったローテート
__force_inline
static md5bs_alt_uint32_t md5bs_rotate(md5bs_alt_uint32_t x, int s)
{
    return md5bs_simd(_mm_srli_epi64(_mm_shuffle_epi32(md5bs_m128i(x), 0), (32 - s)));
}

#    else  /* MD5BS_SIMD_REGISTER */
#    endif /* MD5BS_SIMD_REGISTER */

// メモリ・アクセス
__force_inline
static md5bs_simd4_t md5bs_simd_loadu(const md5bs_simd4_t *p)
{
    return md5bs_simd(_mm_lddqu_si128((const __m128i *)p));
}

#    if MD5BS_SIMD_REGISTER_256 // YMM

template <typename T>
__force_inline
static __m256 md5bs_m256(T v)
{
    return xmm_cast<__m256>(v);
}

template <typename T>
__force_inline
static __m256i md5bs_m256i(T v)
{
    return xmm_cast<__m256i>(v);
}

template <typename T>
__force_inline
static md5bs_simd8_t md5bs_simd8(T x)
{
    return xmm_cast<md5bs_simd8_t>(x);
}

__force_inline
static md5bs_simd8_t md5bs_simd_loadu(const md5bs_simd8_t *p)
{
    return md5bs_simd8(_mm256_lddqu_si256((const __m256i *)p));
}

#    endif /* MD5BS_SIMD_REGISTER_256 */
#  else /* MD5BS_ISA_??? */
#    error "unknown ISA"
#  endif /* MD5BS_ISA_??? */
#else /* !MD5BS_HAS_SIMD */

// C

#  if MD5BS_SIMD_REGISTER /*****/

__force_inline
static md5bs_simd4_t md5bs_simd(uint32_t x)
{
    md5bs_simd4_t r;

    r[0] = x;
    r[1] = 0;
    r[2] = 0;
    r[3] = 0;
    return r;
}

__force_inline
static md5bs_uint32_t md5bs_simd_read(const md5bs_simd4_t &p, int n)
{
    return p[n];
}

__force_inline
static md5bs_simd4_t md5bs_simd_write(md5bs_simd4_t &p, int n, uint32_t v)
{
    md5bs_simd4_t r = p;

    r[n] = v;
    return r;
}

#  endif /* MD5BS_SIMD_REGISTER */
#endif /* !MD5BS_HAS_SIMD */

namespace // anonymous
{

    template <typename T>
    __force_inline
    static T md5bs_simd_loadu(const T *p)
    {
        return *p;
    }

    template <typename T>
    __force_inline
    static void md5bs_simd_store(T *p, const T &v)
    {
        *p = v;
    }


} // anonymous

#if !MD5BS_HAS_SIMD || !MD5BS_SIMD_REGISTER

__force_inline
static md5bs_uint32_t md5bs_uint32(uint32_t x)
{
    return x;
}

__force_inline
static md5bs_simd4_t md5bs_simd_add(md5bs_simd4_t a, md5bs_simd4_t b)
{
    return a + b;
}

__force_inline
static uint32_t md5bs_rotate(uint32_t x, int s)
{
    return ((x << s) | (x >> (32 - s)));
}

#endif

/*****************************************
 *
 *     ブロックの計算
 *
 *****************************************/

// T[n] の値
static const uint32_t md5bs_block_t_order_table[64] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,

    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
    0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,

    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
    0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,

    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
    0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
};

// X[i] に合わせた T[n] の値
static const uint32_t md5bs_block_x_order_table[64] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,  //  0,  1,  2,  3
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,  //  4,  5,  6,  7
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,  //  8,  9, 10, 11
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,  // 12, 13, 14, 15

    0xe9b6c7aa, 0xf61e2562, 0xfcefa3f8, 0xf4d50d87,  //  1,  6, 11,  0
    0xe7d3fbc8, 0xd62f105d, 0xc040b340, 0x676f02d9,  //  5, 10, 15,  4
    0x455a14ed, 0x21e1cde6, 0x02441453, 0x265e5a51,  //  9, 14,  3,  8
    0x8d2a4c8a, 0xa9e3e905, 0xc33707d6, 0xd8a1e681,  // 13,  2,  7, 12

    0xeaa127fa, 0xa4beea44, 0xc4ac5665, 0xd4ef3085,  //  5,  8, 11, 14
    0x4bdecfa9, 0xfffa3942, 0x04881d05, 0xf6bb4b60,  //  1,  4,  7, 10
    0x8771f681, 0xd9d4d039, 0xbebfbc70, 0x6d9d6122,  // 13,  0,  3,  6
    0xe6db99e5, 0x289b7ec6, 0xfde5380c, 0x1fa27cf8,  //  9, 12, 15,  2

    0xf4292244, 0x85845dd1, 0x2ad7d2bb, 0x8f0ccc92,  //  0,  7, 14,  5
    0xf7537e82, 0xfc93a039, 0xa3014314, 0x432aff97,  // 12,  3, 10,  1
    0x6fa87e4f, 0xeb86d391, 0xffeff47d, 0xbd3af235,  //  8, 15,  6, 13
    0x655b59c3, 0x4e0811a1, 0xab9423a7, 0xfe2ce6e0,  //  4, 11,  2,  9
};

// 4バイト境界にないブロック・データを取得する
__force_inline
static void md5bs_block_load(MD5ByteSum::Block &out,
                             const uint32_t *inp,
                             bool endian)
{
    md5bs_load32 load = md5bs_load32_func(endian);

    for (int i = 0; i < 16; i++)
        out[i] = load(inp[i], false);
}

// 1ブロックの先読み(Dキャッシュ)を試みる
__force_inline
static void md5bs_block_prefetch(const void *block)
{
    const uint8_t *p = md5bs_uint8_p(block);

    if (MD5BS_CACHE_LINE_SIZE >= 16)
        for (int o = 0; o < 64; o += MD5BS_CACHE_LINE_SIZE)
            md5bs_prefetch(p + o);
}

/*
 * get_block_index()
 * get_rotate_bits()
 */
#if 0

/* テーブル参照方式 */

// 計算順番(インデックス)とブロックのワード番号の表
static const uint8_t md5bs_block_index_table[64] = {
    0, 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
    1, 6, 11,  0,  5, 10, 15,  4,  9, 14,  3,  8, 13,  2,  7, 12,
    5, 8, 11, 14,  1,  4,  7, 10, 13,  0,  3,  6,  9, 12, 15,  2,
    0, 7, 14,  5, 12,  3, 10,  1,  8, 15,  6, 13,  4, 11,  2,  9,
};

// 計算順番(インデックス)からブロックのワード番号の取得する
__force_inline
static uint32_t get_block_index(uint32_t index)
{
    return md5bs_block_index_table[index];
}

// 計算順番(インデックス)とローテート・ビット数の表
static const uint8_t md5bs_rotate_bits_table[64] = {
    7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
    5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
};

// 計算順番(インデックス)からローテート・ビット数を取得する
__force_inline
static int get_rotate_bits(uint32_t index)
{
    return md5bs_rotate_bits_table[index];
}

#else

/* 計算方式 */

// 計算順番(インデックス)からブロックのワード番号の取得する
//     0..15: (index * 1 + 0) & 15
//    16..31: (index * 5 + 1) & 15
//    32..47: (index * 3 + 5) & 15
//    48..63: (index * 7 + 0) & 15
//
__force_inline
static uint32_t get_block_index(uint32_t index)
{
    uint32_t x = index & 0x0f;
    uint32_t y = index & 0x30;
    uint32_t y1 = index & 0x10;
    uint32_t y2 = index & 0x20;
    uint32_t xmul = (y1 >> 2) | (y2 >> 4) | 1;  // {1,5,3,7}[y >> 4]
    uint32_t offs = (0x0510 >> (y >> 2)) & 7;   // {0,1,5,0}[y >> 4]
    return (x * xmul + offs) & 15;
}

// 計算順番(インデックス)からローテート・ビット数を取得する
//   {
//     7, 12, 17, 22,
//     5,  9, 14, 20,
//     4, 11, 16, 23,
//     6, 10, 15, 21,
//   } = {
//     4+(0*5)+0+[3], 4+(1*5)+0+[3], 4+(2*5)+0+[3], 4+(3*5)+1+[2],
//     4+(0*5)+0+[1], 4+(1*5)+0+[0], 4+(2*5)+0+[0], 4+(3*5)+1+[0],
//     4+(0*5)+0+[0], 4+(1*5)+0+[2], 4+(2*5)+0+[2], 4+(3*5)+1+[3],
//     4+(0*5)+0+[2], 4+(1*5)+0+[1], 4+(2*5)+0+[1], 4+(3*5)+1+[1],
//   }
//   ここから[n]を抜き出して
//   {
//     3, 3, 3, 2, // |2|3|3|3| = 10_11_11_11 = 0xbf
//     1, 0, 0, 0, // |0|0|0|1| = 00_00_00_01 = 0x01
//     0, 2, 2, 3, // |3|2|2|0| = 11_10_10_00 = 0xe8
//     2, 1, 1, 1, // |1|1|1|2| = 01_01_01_10 = 0x56
//   }
//
__force_inline
static int get_rotate_bits(uint32_t index)
{
    uint32_t x = index & 0x03;
    uint32_t y = index & 0x30;
    uint32_t p = x | (y >> 2);
    uint32_t x3 = (x + 1) >> 2;    // (x == 3) ? 1 : 0;
    uint32_t x0 = (0x56e801bf >> (p << 1)) & 3;
    return 4 + (x * 5) + x3 + x0;  // 4+(x*5)+x3+[x0]
}

#endif

// 計算順番(インデックス)によって異なる計算をする
template <typename T>
__force_inline
static T md5bs_state_func(uint32_t index, T x, T y, T z)
{
    switch ((index >> 4) & 3)
    {
    case 0: return (y & x) | (z & ~x); /* index: 0x00 - 0x0F */
    case 1: return (x & z) | (y & ~z); /* index: 0x10 - 0x1F */
    case 2: return x ^ y ^ z;          /* index: 0x20 - 0x2F */
    case 3: return y ^ (x | ~z);       /* index: 0x30 - 0x3F */
    }
    return (x ^ x); /* 警告抑制用:意味はない */
}

// ダイジェスト値(A,B,C,D)の更新準備: 計算1回(indexでは4個)分
__force_inline
static void md5bs_update_prepare4(uint32_t i,
                                  const uint32_t *X,
                                  const uint32_t *T,
                                  uint32_t *XT,
                                  bool aligned,
                                  bool endian,
                                  bool order)
{
    md5bs_load32 load = md5bs_load32_func(endian);
    uint32_t j = i & 0x0f;
    uint32_t x0 = order ? (j + 0) : get_block_index(i + 0);
    uint32_t x1 = order ? (j + 1) : get_block_index(i + 1);
    uint32_t x2 = order ? (j + 2) : get_block_index(i + 2);
    uint32_t x3 = order ? (j + 3) : get_block_index(i + 3);
    uint32_t *xt = XT;

    xt[j + 0] = T[i + 0] + load(X[x0], aligned);
    xt[j + 1] = T[i + 1] + load(X[x1], aligned);
    xt[j + 2] = T[i + 2] + load(X[x2], aligned);
    xt[j + 3] = T[i + 3] + load(X[x3], aligned);

    __dummy_access(*xt);
}

// ダイジェスト値(A,B,C,D)の更新準備: 計算4回(indexでは16個)分
__force_inline
static void md5bs_update_prepare16(uint32_t i,
                                   const uint32_t *X,
                                   const uint32_t *T,
                                   uint32_t *XT,
                                   bool aligned,
                                   bool endian,
                                   bool order)
{
    if (MD5BS_SIMD_REGISTER && aligned && order && !endian)
    {
        const md5bs_simd4_t *x = md5bs_simd4_p(X);
        const md5bs_simd4_t *t = md5bs_simd4_p(T + i);
        md5bs_simd4_t *xt = md5bs_simd4_p(XT);

        xt[0] = md5bs_simd_add(t[0], md5bs_simd_loadu(x + 0));
        xt[1] = md5bs_simd_add(t[1], md5bs_simd_loadu(x + 1));
        xt[2] = md5bs_simd_add(t[2], md5bs_simd_loadu(x + 2));
        xt[3] = md5bs_simd_add(t[3], md5bs_simd_loadu(x + 3));

        __dummy_access(xt[0]);
        __dummy_access(xt[1]);
        __dummy_access(xt[2]);
        __dummy_access(xt[3]);
    }
    else
    {
        md5bs_update_prepare4(i + 0 * 4, X, T, XT, aligned, endian, order);
        md5bs_update_prepare4(i + 1 * 4, X, T, XT, aligned, endian, order);
        md5bs_update_prepare4(i + 2 * 4, X, T, XT, aligned, endian, order);
        md5bs_update_prepare4(i + 3 * 4, X, T, XT, aligned, endian, order);
    }
}

// ダイジェスト値(A,B,C,D)の更新準備: 計算16回(indexでは64個)分
__force_inline
static void md5bs_update_prepare64(const uint32_t *X,
                                   const uint32_t *T,
                                   MD5ByteSum::Block *XT)
{
    const md5bs_simd8_t *wx = md5bs_simd8_p(X);
    const md5bs_simd8_t *wt = md5bs_simd8_p(T);
    md5bs_simd8_t *wxt = md5bs_simd8_p(XT);
    md5bs_simd8_t x0 = md5bs_simd_loadu(wx + 0);
    md5bs_simd8_t x1 = md5bs_simd_loadu(wx + 1);

    for (int i = 0; i < 4; i++)
    {
        int j = i * 2;

        wxt[j + 0] = x0 + wt[j + 0];
        wxt[j + 1] = x1 + wt[j + 1];

        __dummy_access(wxt[j + 0]);
        __dummy_access(wxt[j + 1]);
    }
}

// ダイジェスト値(A,B,C,D)の更新: 計算1個分
//   md5bs_update_prepare4 で用意したブロックを使う
__force_inline
static md5bs_uint32_t md5bs_update_execute1(uint32_t i,
                                            md5bs_uint32_t xt,
                                            md5bs_uint32_t a,
                                            md5bs_uint32_t b,
                                            md5bs_uint32_t c,
                                            md5bs_uint32_t d,
                                            int r)
{
    return b + md5bs_rotate(xt + a + md5bs_state_func(i, b, c, d), r);
}

// ダイジェスト値(A,B,C,D)の更新: 計算4個分
//   md5bs_update_prepare4 で用意したブロックを使う
__force_inline
static void md5bs_update_execute4(uint32_t i,
                                  const uint32_t *xt,
                                  md5bs_uint32_t &a,
                                  md5bs_uint32_t &b,
                                  md5bs_uint32_t &c,
                                  md5bs_uint32_t &d,
                                  bool order)
{
    uint32_t j = (i & 0x0f);
    uint32_t b0 = order ? get_block_index(i + 0) : (j + 0);
    uint32_t b1 = order ? get_block_index(i + 1) : (j + 1);
    uint32_t b2 = order ? get_block_index(i + 2) : (j + 2);
    uint32_t b3 = order ? get_block_index(i + 3) : (j + 3);

    int r0 = get_rotate_bits(i + 0);
    int r1 = get_rotate_bits(i + 1);
    int r2 = get_rotate_bits(i + 2);
    int r3 = get_rotate_bits(i + 3);

    md5bs_uint32_t xt0 = md5bs_uint32(xt[b0]);
    md5bs_uint32_t xt1 = md5bs_uint32(xt[b1]);
    md5bs_uint32_t xt2 = md5bs_uint32(xt[b2]);
    md5bs_uint32_t xt3 = md5bs_uint32(xt[b3]);

    a = md5bs_update_execute1(i + 0, xt0, a, b, c, d, r0);
    d = md5bs_update_execute1(i + 1, xt1, d, a, b, c, r1);
    c = md5bs_update_execute1(i + 2, xt2, c, d, a, b, r2);
    b = md5bs_update_execute1(i + 3, xt3, b, c, d, a, r3);
}

// ダイジェスト値(A,B,C,D)の更新: 計算16個分
__force_inline
static void md5bs_update_execute16(uint32_t i,
                                   const uint32_t *xt,
                                   md5bs_uint32_t &a,
                                   md5bs_uint32_t &b,
                                   md5bs_uint32_t &c,
                                   md5bs_uint32_t &d,
                                   bool order)
{
    md5bs_update_execute4(i + 0 * 4, xt, a, b, c, d, order);
    md5bs_update_execute4(i + 1 * 4, xt, a, b, c, d, order);
    md5bs_update_execute4(i + 2 * 4, xt, a, b, c, d, order);
    md5bs_update_execute4(i + 3 * 4, xt, a, b, c, d, order);
}

// ダイジェスト値(A,B,C,D)の更新: 計算1回分
__force_inline
static md5bs_uint32_t md5bs_update_step1(uint32_t i,
                                         md5bs_uint32_t X,
                                         md5bs_uint32_t T,
                                         md5bs_uint32_t a,
                                         md5bs_uint32_t b,
                                         md5bs_uint32_t c,
                                         md5bs_uint32_t d,
                                         int r)
{
    return b + md5bs_rotate(X + T + a + md5bs_state_func(i, b, c, d), r);
}

// ダイジェスト値(A,B,C,D)の更新: 計算4回分
__force_inline
static void md5bs_update_step4(uint32_t i,
                               const uint32_t *X,
                               const uint32_t *T,
                               md5bs_uint32_t &a,
                               md5bs_uint32_t &b,
                               md5bs_uint32_t &c,
                               md5bs_uint32_t &d,
                               bool aligned,
                               bool endian)
{
    md5bs_load32 load = md5bs_load32_func(endian);

    uint32_t b0 = get_block_index(i + 0);
    uint32_t b1 = get_block_index(i + 1);
    uint32_t b2 = get_block_index(i + 2);
    uint32_t b3 = get_block_index(i + 3);

    int r0 = get_rotate_bits(i + 0);
    int r1 = get_rotate_bits(i + 1);
    int r2 = get_rotate_bits(i + 2);
    int r3 = get_rotate_bits(i + 3);

    md5bs_uint32_t t0 = md5bs_uint32(T[i + 0]);
    md5bs_uint32_t t1 = md5bs_uint32(T[i + 1]);
    md5bs_uint32_t t2 = md5bs_uint32(T[i + 2]);
    md5bs_uint32_t t3 = md5bs_uint32(T[i + 3]);

    md5bs_uint32_t x0 = md5bs_uint32(load(X[b0], aligned));
    md5bs_uint32_t x1 = md5bs_uint32(load(X[b1], aligned));
    md5bs_uint32_t x2 = md5bs_uint32(load(X[b2], aligned));
    md5bs_uint32_t x3 = md5bs_uint32(load(X[b3], aligned));

    a = md5bs_update_step1(i + 0, x0, t0, a, b, c, d, r0);
    d = md5bs_update_step1(i + 1, x1, t1, d, a, b, c, r1);
    c = md5bs_update_step1(i + 2, x2, t2, c, d, a, b, r2);
    b = md5bs_update_step1(i + 3, x3, t3, b, c, d, a, r3);
}

// ダイジェスト値(A,B,C,D)の更新: 計算16回分
__force_inline
static void md5bs_update_step16(uint32_t i,
                                const uint32_t *X,
                                const uint32_t *T,
                                md5bs_uint32_t &a,
                                md5bs_uint32_t &b,
                                md5bs_uint32_t &c,
                                md5bs_uint32_t &d,
                                bool aligned,
                                bool endian)
{
    md5bs_update_step4(i + 0 * 4, X, T, a, b, c, d, aligned, endian);
    md5bs_update_step4(i + 1 * 4, X, T, a, b, c, d, aligned, endian);
    md5bs_update_step4(i + 2 * 4, X, T, a, b, c, d, aligned, endian);
    md5bs_update_step4(i + 3 * 4, X, T, a, b, c, d, aligned, endian);
}

// uint32_t 型用: state から a, b, c, d を取得する
__force_inline
static void md5bs_get_state(const MD5ByteSum::State &state,
                            md5bs_uint32_t &a,
                            md5bs_uint32_t &b,
                            md5bs_uint32_t &c,
                            md5bs_uint32_t &d)
{
#if !MD5BS_SIMD_REGISTER

    a = state[0];
    b = state[1];
    c = state[2];
    d = state[3];

#else

    const md5bs_simd4_t &p = *reinterpret_cast<const md5bs_simd4_t *>(&state);

    a = md5bs_simd_read(p, 0);
    b = md5bs_simd_read(p, 1);
    c = md5bs_simd_read(p, 2);
    d = md5bs_simd_read(p, 3);

#endif
}

// uint32_t 型用: state に a, b, c, d を加算する
__force_inline
static void md5bs_add_state(MD5ByteSum::State &state,
                            md5bs_uint32_t a,
                            md5bs_uint32_t b,
                            md5bs_uint32_t c,
                            md5bs_uint32_t d)
{
#if !MD5BS_SIMD_REGISTER

    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;

#else

    md5bs_simd4_t &s = *reinterpret_cast<md5bs_simd4_t *>(&state);
    md5bs_simd4_t t;

    t = md5bs_simd(a);
    t = md5bs_simd_write(t, 1, b);
    t = md5bs_simd_write(t, 2, c);
    t = md5bs_simd_write(t, 3, d);
    s = s + t;

#endif
}

// ダイジェスト値(state)の更新:
//   入力 block は任意のアドレスで、16ワードのデータがある
__force_inline
static void md5bs_state_update(MD5ByteSum::State &state,
                               MD5ByteSum::Block &input,
                               MD5ByteSum::Block xt[4],
                               const void *block,
                               bool aligned,
                               bool endian)
{
    const uint32_t *t = md5bs_block_t_order_table;
    const uint32_t *x = md5bs_uint32_p(block);
    md5bs_uint32_t a, b, c, d;

    md5bs_get_state(state, a, b, c, d);

    if (!MD5BS_BLOCK_PREPARE)
    {
        md5bs_update_step16(0 * 16, x, t, a, b, c, d, aligned, endian);
        md5bs_update_step16(1 * 16, x, t, a, b, c, d, aligned, endian);
        md5bs_update_step16(2 * 16, x, t, a, b, c, d, aligned, endian);
        md5bs_update_step16(3 * 16, x, t, a, b, c, d, aligned, endian);
    }
    else
    {
        // 1ブロック分の (xt[k] = T[i] + X[j]) を準備する版
        bool order = MD5BS_BLOCK_X_ORDER;

        if (!aligned && MD5BS_ADDRESS_ALIGN)
        {
            md5bs_block_load(input, x, endian);
            aligned = true;
            endian = false;
            x = input;
        }
        if (order)
            t = md5bs_block_x_order_table;

        if (MD5BS_SIMD_REGISTER_256 && aligned && order && !endian)
            md5bs_update_prepare64(x, t, xt);
        else
        {
            md5bs_update_prepare16(0 * 16, x, t, xt[0], aligned, endian, order);
            md5bs_update_prepare16(1 * 16, x, t, xt[1], aligned, endian, order);
            md5bs_update_prepare16(2 * 16, x, t, xt[2], aligned, endian, order);
            md5bs_update_prepare16(3 * 16, x, t, xt[3], aligned, endian, order);
        }

        md5bs_update_execute16(0 * 16, xt[0], a, b, c, d, order);
        md5bs_update_execute16(1 * 16, xt[1], a, b, c, d, order);
        md5bs_update_execute16(2 * 16, xt[2], a, b, c, d, order);
        md5bs_update_execute16(3 * 16, xt[3], a, b, c, d, order);
    }

    md5bs_add_state(state, a, b, c, d);
}

// 複数ブロックによるダイジェスト値(state)の更新:
//   入力 block は任意のアドレスで、16ワードのデータが count 個ある
template <bool aligned>
__force_inline
static void md5bs_state_update(MD5ByteSum::State &state,
                               MD5ByteSum::Block &input,
                               MD5ByteSum::Block adbuf[4],
                               const void *block,
                               unsigned count,
                               bool endian)
{
    const uint8_t *p = md5bs_uint8_p(block);

    if (MD5BS_CACHE_LINE_SIZE < 16)
    {
        do
        {
            md5bs_state_update(state, input, adbuf, p, aligned, endian);
            p += 64;
        }
        while (--count != 0);
    }
    else
    {
        md5bs_block_prefetch(p);
        while (--count > 0)
        {
            md5bs_block_prefetch(p + 64);
            md5bs_state_update(state, input, adbuf, p, aligned, endian);
            p += 64;
        }
        md5bs_state_update(state, input, adbuf, p, aligned, endian);
    }
}

// メッセージを入力ブロック(バッファ)にコピーする
//   s から n バイト(n>0)を、input のバイト換算 i の位置へ
__force_inline
static void md5bs_setinp(uint32_t *input, const void *message, unsigned i, uint32_t n, bool endian)
{
    uint8_t *d = md5bs_uint8_p(input);
    const uint8_t *s = md5bs_uint8_p(message);

    if (!endian)
    {
        std::memcpy(d + i, s, n);
        return;
    }

    while ((i & 3) != 0)
    {
        d[i++ ^ 3] = *s++;
        if (--n == 0)
            return;
    }
    for (uint32_t c = n >> 2; c > 0; c--, i += 4, s += 4)
        input[i >> 2] = md5bs_load32be(*md5bs_uint32_p(s), false);
    n &= 3;
    if (n == 0)
        return;
    while (n-- > 0)
        d[i++ ^ 3] = *s++;
}

// ワードの8ビット以上(iによる)を0クリアする(リトル・エンディアン版)
//   p = MD5ByteSum::input, i = MD5ByteSum::bytes
__force_inline
static uint32_t md5bs_set0le(uint8_t *p, uint32_t i)
{
    { if ((i & 3) == 0) return i; } p[i++] = 0;
    { if ((i & 3) == 0) return i; } p[i++] = 0;
    { if ((i & 3) == 0) return i; } p[i++] = 0;
    return i;
}

// ワードの8ビット以上(iによる)を0クリアする(ビッグ・エンディアン版)
//   p = MD5ByteSum::input, i = MD5ByteSum::bytes
__force_inline
static uint8_t md5bs_set0be(uint8_t *p, uint32_t i)
{
    { if ((i & 3) == 0) return i; } p[i++ ^ 3] = 0;
    { if ((i & 3) == 0) return i; } p[i++ ^ 3] = 0;
    { if ((i & 3) == 0) return i; } p[i++ ^ 3] = 0;
    return i;
}

/*****************************************
 *
 *    MD5ByteSum クラス
 *
 *****************************************/

// コンストラクタ
MD5ByteSum::MD5ByteSum(const void *message, size_t size)
{
    initialize();
    update(message, size);
}

// メンバー変数の初期化
void MD5ByteSum::initialize()
{
    std::memset(input, 0, sizeof(input));
    std::memset(adbuf, 0, sizeof(adbuf));
    state[0] = 0x67452301;
    state[1] = 0xefcdab89;
    state[2] = 0x98badcfe;
    state[3] = 0x10325476;
    bytes = 0;
    active = true;
}

// ダイジェスト値(state)の更新:
//   入力するブロックは任意のアドレスで 64 バイトのメッセージがある
void MD5ByteSum::update_state(const void *block)
{
    md5bs_state_update(state, input, adbuf, block, true, false);
}

// ダイジェスト値の確定処理
void MD5ByteSum::finalize()
{
    if (!active)
        return;

    uint32_t i = bytes & 0x3f;
    uint8_t *p = md5bs_uint8_p(input);

    if (!MD5BS_BIG_ENDIAN)
    {
        p[i++] = 0x80;
        if (i > 56)
        {
            if (i < 64)
            {
                i = md5bs_set0le(p, i);
                if (i == 60)
                    input[15] = 0;
            }
            update_state();
            i = 0;
        }
    }
    else
    {
        p[i++ ^ 3] = 0x80;
        i = md5bs_set0be(p, i);
        if (i >= 60)
        {
            if (i == 60)
                input[15] = 0;
            update_state();
            i = 0;
        }
    }
    std::memset(p + i, 0, (56 - i));
    input[14] = bytes << 3;
    input[15] = bytes >> 29;
    update_state();

    active = false;
}

// メッセージによるダイジェスト値の更新
void MD5ByteSum::update(const void *message, size_t size)
{
    if (!active)
        initialize();

    bool endian = MD5BS_BIG_ENDIAN;
    const uint8_t *p = md5bs_uint8_p(message);
    unsigned i = bytes & 0x3f;
    unsigned r;
    size_t s;

    for (;;)
    {
        if (size == 0)
            return;

        if ((i != 0) || (size < 64))
        {
            s = size + i;
            if (s >= 64)
                s = (64 - i);
            if (s > size)
                s = size;
            r = unsigned(s);

            md5bs_setinp(input, p, i, r, endian);
            bytes += r;
            size -= r;
            p += r;

            i = bytes & 0x3f;
            if (i == 0)
                update_state();
            continue;
        }

        r = unsigned(size & ~0x3f);
        bytes += r;
        size -= r;

        r >>= 6;
        if (md5bs_is_align4(p) || !MD5BS_ADDRESS_ALIGN)
            md5bs_state_update<true>(state, input, adbuf, p, r, endian);
        else
            md5bs_state_update<false>(state, input, adbuf, p, r, endian);
    }
}

// ダイジェスト値の確定と取得
void MD5ByteSum::digest(Digest &out)
{
    finalize();

    if (!MD5BS_BIG_ENDIAN)
    {
        std::memcpy(out, &state, 16);
    }
    else
    {
        uint8_t *d = out;
        const uint32_t *s = state;

        for (uint32_t i = 0; i < 4; i++)
            for (uint32_t w = *s++, j = 0; j < 4; j++, w >>= 8)
                *d++ = uint8_t(w);
    }
}

// ダイジェスト値の確定と16進数文字列による取得(NULL終端文字列)
void MD5ByteSum::hexdigest(HexDigest &out)
{
    finalize();

    for (int p = 0; p < 4; p++)
    {
        uint32_t s = state[p];

        for (unsigned q = 0; q < 4; q++, s >>= 8)
        {
            char h = (s >> 4) & 0x0f;
            char l = (s >> 0) & 0x0f;
            out[p * 8 + q * 2 + 0] = h + ((h < 10) ? '0' : ('a' - 10));
            out[p * 8 + q * 2 + 1] = l + ((l < 10) ? '0' : ('a' - 10));
        }
    }
    out[32] = 0;
}

///////////////////////////////////////////////////////////////////////////////
//
// テスト用
//
///////////////////////////////////////////////////////////////////////////////

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cinttypes>
#include <time.h>

inline uint64_t RDTSC()
{
#if defined(_MSC_VER) ||   \
    (defined(__clang__) && \
     (defined(__amd64) ||  \
      defined(__i386) ||   \
      defined(__x86) ||    \
      defined(__x86_64)))

    return __rdtsc();

#elif defined(__GNUC__) && \
    (defined(__amd64) ||   \
     defined(__i386) ||    \
     defined(__x86) ||     \
     defined(__x86_64))

    uint32_t eax, edx;
    __asm__ volatile ("RDTSC" : "=a" (eax), "=d" (edx));
    return ((uint64_t)edx << 32) | eax;

#else

    timespec c;
    clock_gettime(CLOCK_REALTIME, &c);
    return ((uint64_t)c.tv_sec * 1000 * 1000 * 1000 + c.tv_nsec);

#endif
}

static void prepare_message(uint8_t *d, size_t dlen, const uint8_t *s, size_t slen)
{
    size_t clen = slen;
    if (clen > dlen)
        clen = dlen;
    std::memcpy(d, s, clen);
    d += clen;
    dlen -= clen;
    if (dlen != 0)
        prepare_message(d, dlen, s, slen + clen);
}

static int string_message(const char *message, const char *digest = NULL)
{
    size_t msglen = strlen(message);
    MD5ByteSum md5(message, msglen);
    MD5ByteSum::HexDigest res;

    md5.hexdigest(res);
    printf("MD5 (\"%s\") = %s\n", message, res);
    if (!digest)
        return 0;
    if (!strcmp(digest, res))
        return 0;
    printf("BUG: %s != %s\n", digest, res);
    return 2;
}

static int file_message(const char *path)
{
    MD5ByteSum md5;
    MD5ByteSum::HexDigest res;

    size_t size = 1024 * 1024;
    void *buffer = malloc(size);

    if (!buffer)
    {
        printf("not enough memory.");
        return 2;
    }

    bool success = true;
    FILE *fp = fopen(path, "rb");

    if (!fp)
    {
        printf("open error: %s\n", path);
        return 2;
    }
    while (!feof(fp))
    {
        size_t rdsz = fread(buffer, 1, size, fp);
        if (ferror(fp))
        {
            success = false;
            break;
        }
        if (!rdsz)
            break;
        md5.update(buffer, rdsz);
    }
    fclose(fp);
    free(buffer);
    if (!success)
    {
        printf("read error: %s\n", path);
        return 2;
    }

    md5.hexdigest(res);
    printf("MD5 (%s) = %s\n", path, res);

    return 0;
}

static int benchmark(const char *message)
{
    if (!message)
        message = "benchmark";
    int rval = string_message(message);
    if (rval)
        return rval;

    size_t msglen = strlen(message);
    size_t blocks = (4 * 1024 * 1024);
    size_t bufsize = sizeof(MD5ByteSum::Block) * blocks;
    uint8_t *buffer = (uint8_t *)malloc(bufsize);
    if (!buffer)
    {
        puts("not enough memory.");
        return 2;
    }
    if (msglen)
    {
        uint8_t *d = buffer;
        std::memcpy(d, message, msglen);
        prepare_message(d + msglen, bufsize - msglen, buffer, msglen);
    }
    else
    {
        std::memset(buffer, -1, bufsize);
    }

    MD5ByteSum md5;
    MD5ByteSum::HexDigest res;

    uint64_t s_tsc = RDTSC();
    {
        const uint8_t *p = buffer;
        size_t s = bufsize;

#define STEP(n) md5.update(p, n); p += n; s -= n;
        // 動作テスト用に分解(計測への影響は極小)
        STEP(17);     // 0
        STEP(17);     // 17
        STEP(63);     // 34
        STEP(31+128); // 97
        STEP(s);
    }
    uint64_t e_tsc = RDTSC();
    uint64_t d_tsc = e_tsc - s_tsc;
    uint64_t ave_i = d_tsc / blocks;
    uint64_t ave_f = (100 * (d_tsc % blocks) + (blocks / 2)) / blocks;

    md5.hexdigest(res);
    printf("TEST digest: %s\n", res);
    // printf("timer start  : %" PRIu64 "\n", s_tsc);
    // printf("timer end    : %" PRIu64 "\n", e_tsc);
    printf("timer count  : %" PRIu64 "\n", d_tsc);
    printf("block average: %" PRIu64 ".%02" PRIu64 "\n", ave_i, ave_f);

    free(buffer);
    return 0;
}

static int test()
{
    static const char *tab[] = {
        "d41d8cd98f00b204e9800998ecf8427e", "",
        "0cc175b9c0f1b6a831c399e269772661", "a",
        "900150983cd24fb0d6963f7d28e17f72", "abc",
        "f96b697d7cb7938d525a2f31aaf161d0", "message digest",
        "c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz",
        "d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
        "ad76b175d22a98a4e3bc2ad72affc53e", "01234567890123456789012345678901234567890123456789012345678",
        NULL, NULL,
    };
    for (int i = 0; tab[i] != NULL; i += 2)
    {
        int res = string_message(tab[i + 1], tab[i + 0]);
        if (res != 0)
            return res;
    }
    return 0;
}

static const char *program;
static int usage()
{
    printf("Usage:\n"
           "  %s -b [message]\n"
           "  %s -f file\n"
           "  %s message\n",
           program, program, program);
    return 1;
}

int main(int argc, char **argv)
{
    program = argv[0];
    if (argc < 2)
        return usage();
    if (!strcmp(argv[1], "-b"))
    {
        if (argc < 4)
            return benchmark(argv[2]);
        return usage();
    }
    if (!strcmp(argv[1], "-f"))
    {
        if (argc == 3)
            return file_message(argv[2]);
        return usage();
    }
    if (!strcmp(argv[1], "-t"))
        return test();
    if (argc == 2)
        return string_message(argv[1]);
    return usage();
}

※ビッグ・エンディアン機での動作確認はしていません

Makefile (GNU make 用)
Makefile
# -*- Makefile -*-
####

-include local-variable.mk

####

DATETIME     := $(shell date +%Y%m%d-%H%M%S)
MACHINE      := $(shell uname -m)

####

ifeq "$(MACHINE)" "armv7l"
CPU_TYPE      = arm
else ifeq "$(MACHINE)" "i686"
CPU_TYPE      = x86
else ifeq "$(MACHINE)" "x86"
CPU_TYPE      = x86
else ifeq "$(MACHINE)" "x86_64"
CPU_TYPE      = x86
else
CPU_TYPE      = none
endif

####

CLANGXX      ?= clang++
GNUGXX       ?= g++

ifeq "$(COMPILER)" "clang"
CXX          = $(CLANGXX)
else ifeq "$(COMPILER)" "gcc"
CXX          = $(GNUGXX)
endif

ifndef COMPILER
flag_CLANG    = $(shell (c++ -v 2>&1 | grep -i clang > /dev/null) && echo clang)
flag_GCC      = $(shell (c++ -v 2>&1 | grep '^gcc' > /dev/null) && echo gcc)
COMPILER      = $(strip $(flag_CLANG) $(flag_GCC))
endif

####

ifeq "$(CPU_TYPE)" "arm"
CPU_ARCHS     = arm neon
else ifeq "$(CPU_TYPE)" "x86"
CPU_ARCHS     = x86 sse avx
endif
ifndef CPU_ARCHS
CPU_ARCHS     = host
endif

CARCH_host    = $(NULL)

CARCH_arm     = $(NULL) # -march=armv6
CARCH_neon    = -march=armv7 -mfpu=neon

CARCH_x86     = -mno-sse
CARCH_sse     = -msse4.2
CARCH_avx     = -mavx2

CMTUNE_arm    =
CMTUNE_x86    = -mtune=native

OPT_ARCH_host = host
OPT_ARCH_arm  = core
OPT_ARCH_x86  = gpr
OPT_ARCH_sse  = sse
OPT_ARCH_avx  = avx
OPT_ARCH_neon = neon
OPT_XARCH     = $(OPT_ARCH_$(OPT_ARCH))

OPT_stdc      = -DMD5BS_HAS_SIMD=0
OPT_core      = -DMD5BS_SIMD_REGISTER=0
OPT_simd      = -DMD5BS_SIMD_REGISTER=1
OPT_direct    = -DMD5BS_BLOCK_PREPARE=0
OPT_preadd    = -DMD5BS_BLOCK_PREPARE=1
OPT_torder    = $(OPT_preadd) -DMD5BS_BLOCK_X_ORDER=0
OPT_xorder    = $(OPT_preadd) -DMD5BS_BLOCK_X_ORDER=1

ifndef PREFETCH
PREFETCH      = default
endif
PREFETCH_off  = -DMD5BS_CACHE_LINE_SIZE=0
PREFETCH_on   = -DMD5BS_CACHE_LINE_SIZE=64

CARCH         = $(CARCH_$(OPT_ARCH)) $(CMTUNE_$(CPU_TYPE))
CDEFS         = $(OPT_$(OPT_SIMD)) $(OPT_$(OPT_BLOCK)) $(PREFETCH_$(PREFETCH))

ifndef COPTS
COPTS         = -O3
endif
ifndef CWARNS
CWARNS        = -W -Wall -Wno-ignored-attributes
endif
CFLAGS        = $(CARCH) $(COPTS) $(CWARNS) $(CSTD)

CXXARCH       = $(CARCH)
CXXDEFS       = $(CDEFS)
CXXOPTS       = $(COPTS)
CXXWARNS      = $(CWARNS)
CXXFLAGS      = $(patsubst %,%,$(CXXARCH) $(CXXOPTS) $(CXXWARNS) $(CXXSTD) $(CXXDEFS))

####

BUILDOPT      = COMPILER=$(COMPILER) PREFETCH=$(PREFETCH)

TEST_BINARY  ?= *

####

SUBDIRS       = asm bin log

####

all:		$(COMPILER)

gcc:
		$(MAKE) COMPILER=gcc build
clang:
		$(MAKE) COMPILER=clang build

build:		build-orig build-curr

build-orig:
		@(for arch in $(CPU_ARCHS);\
		do\
		  $(MAKE) COMPILER=$(COMPILER) OPT_ARCH=$$arch build-orig-exec;\
		done)
build-orig-exec: asm bin
		$(CXX) $(CXXFLAGS) md5bs_orig.cpp -o bin/$(COMPILER)-$(OPT_XARCH)-md5bs_orig
		$(CXX) $(CXXFLAGS) md5bs_orig.cpp -o asm/$(COMPILER)-$(OPT_XARCH)-md5bs_orig.s -S

build-new:
		@(for arch in $(CPU_ARCHS);\
		do\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=stdc OPT_BLOCK=direct OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=stdc OPT_BLOCK=torder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=stdc OPT_BLOCK=xorder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=core OPT_BLOCK=direct OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=core OPT_BLOCK=torder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=core OPT_BLOCK=xorder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=simd OPT_BLOCK=direct OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=simd OPT_BLOCK=torder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=simd OPT_BLOCK=xorder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		done)

build-new-exec: asm bin
		$(CXX) $(CXXFLAGS) md5bs.cpp -o bin/$(COMPILER)-$(OPT_XARCH)-md5bs-$(OPT_SIMD)-$(OPT_BLOCK)
		$(CXX) $(CXXFLAGS) md5bs.cpp -o asm/$(COMPILER)-$(OPT_XARCH)-md5bs-$(OPT_SIMD)-$(OPT_BLOCK).s -S

bench:		log
		@(cd bin;\
		for f in *;\
		do\
		  [ -x $$f ] || continue;\
		  echo "********" $$f -t "*******";\
		  for n in `seq 5`; do ./$$f -b | tee -a ../log/$(DATETIME)-$$f.log; done;\
		done)

####

check:
		@(cd bin;\
		for f in $(TEST_BINARY);\
		do\
		  [ -x $$f ] || continue;\
		  echo $$f -t;\
		  ./$$f -t || exit $$?;\
		done)

####

clean:
		rm -f *.[os] *.log *~
		rm -rf $(SUBDIRS)
clean-output:
		rm -rf asm bin
clean-log:
		rm -rf log

$(SUBDIRS) $(VMSHAREDIR):
		mkdir $@

####

-include local-rule.mk

コンパイルと実行

$ make clean gcc clang check bench

コンパイル・オプション

改良版は、以下の3通り

略号 コンパイル・オプション 内容
D -DMD5BS_BLOCK_PREPARE=0 メッセージの記憶域を直接参照する
T -DMD5BS_BLOCK_PREPARE=1
-DMD5BS_BLOCK_X_ORDER=0
ブロックの計算開始時に X[j] + T[i] を i 順に用意
X -DMD5BS_BLOCK_PREPARE=1
-DMD5BS_BLOCK_X_ORDER=1
ブロックの計算開始時に X[j] + T[i] を j 順に用意

を、それぞれ以下の3通り

略号 コンパイル・オプション 内容
N -DMD5BS_HAS_SIMD=0 明示的に SIMD は使用しない
B -DMD5BS_SIMD_REGISTER=0 ブロックの計算開始時を除いて
明示的に SIMD は使用しない
S -DMD5BS_SIMD_REGISTER=1 明示的に SIMD を使用する

で、9通りのビルド・パターンがあります。

実行時のコマンド・ラインは

$ ./md5bs_orig -b
$ ./md5bs -b

として、 5回実行し、出力の "block average" を比較します。計測は、(1<<22)個のブロック全体の処理時間を測り、(1<<22)で割っています。

ARM (RaspberryPI 3B+)

改良版の9通りに対して、命令セット(ISA)の2通り

略号 コンパイル・オプション 内容
CORE デフォルトの最適化
NEON -march=armv7 -mfpu=neon NEON を使用した最適化

で18通りのビルド・パターンを2つのコンパイラ

コンパイラ
Clang version 12.0.1
GCC version 11.2.0

で行い、これを RaspberryPI 3B+ の RaspberryPI OS 10.10で実行します。全部で36通りです。

CORE,B:S の組み合わせでは SIMD の代替を用意しています。

x86/x86_64

改良版の9通りに対して、命令セット(ISA)の3通り

略号 コンパイル・オプション 内容
GPR -mno-sse -mtune=native -O3 SSE なしで最適化
SSE -msse4.2 -mtune=native -O3 SSE4.2 を使用した最適化
AVX -mavx2 -mtune=native -O3 AVX2 を使用した最適化

で27通りのビルド・パターンを2つのコンパイラ

コンパイラ
Clang version 14.0.0
GCC version 11.2.0

で行い、これを2つの実行環境

CPU Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
x86 Debian 10.10 (i386)
x86_64 Debian 10.10 (amd64)

で実行します。全部で108通りです。

GRP,B:S の組み合わせでは SIMD の代替を用意しています。

実行結果

ARM (RaspberryPI 3B+)

計測は clock_gettime を使用しているので、概ね ナノ秒(ns)になります。 RaspberryPI 3B+ は 1.4GHz 動作らしいので、1.4 倍で CPU サイクル数に相当します。

実行結果一覧(長いので折りたたみ)

オリジナル(md5bs_orig)の結果

ISA 1回目 2回目 3回目 4回目 5回目 平均
GCC CORE 1227.88 1200.95 1227.22 1201.41 1230.51 1217.59
GCC NEON 776.77 770.64 776.64 772.85 775.67 774.51
Clang CORE 967.45 958.84 966.88 962.38 966.46 964.40
Clang NEON 967.45 966.36 952.18 967.03 951.40 960.88

改良版(md5bs)の結果

ISA 1回目 2回目 3回目 4回目 5回目 平均
GCC CORE N D 332.38 331.38 332.54 331.93 332.57 332.16
GCC CORE N T 557.36 556.45 557.38 556.19 557.34 556.94
GCC CORE N X 559.18 559.18 558.75 556.10 558.76 558.39
GCC CORE B D 329.78 330.20 329.44 330.73 329.83 330.00
GCC CORE B T 553.59 557.48 553.72 556.88 553.79 555.09
GCC CORE B X 558.58 555.80 558.90 555.55 560.23 557.81
GCC CORE S D 336.25 336.46 336.26 336.23 332.90 335.62
GCC CORE S T 561.28 558.27 559.14 558.24 559.11 559.21
GCC CORE S X 559.67 561.68 560.02 562.40 556.93 560.14
GCC NEON N D 307.95 308.03 306.86 308.61 306.68 307.63
GCC NEON N T 520.61 525.04 521.11 524.06 521.23 522.41
GCC NEON N X 537.60 533.93 537.28 533.89 537.65 536.07
GCC NEON B D 307.20 305.58 306.40 306.27 306.41 306.37
GCC NEON B T 521.12 520.38 520.92 521.05 521.51 521.00
GCC NEON B X 534.80 538.76 534.50 538.05 534.09 536.04
GCC NEON S D 1046.08 1051.76 1052.14 1043.85 1045.98 1047.96
GCC NEON S T 1115.89 1119.64 1120.17 1125.41 1119.80 1120.18
GCC NEON S X 1020.62 1026.44 1021.06 1020.98 1025.80 1022.98
Clang CORE N D 335.22 334.81 333.06 334.76 334.14 334.40
Clang CORE N T 483.41 482.87 486.73 483.20 482.90 483.82
Clang CORE N X 486.73 480.72 486.96 483.17 486.81 484.88
Clang CORE B D 332.63 335.06 332.51 333.11 332.67 333.20
Clang CORE B T 484.56 480.44 480.38 480.34 483.46 481.84
Clang CORE B X 480.33 480.22 483.21 480.17 483.16 481.42
Clang CORE S D 975.93 979.16 976.14 979.95 976.03 977.44
Clang CORE S T 1373.85 1364.28 1373.68 1363.78 1363.86 1367.89
Clang CORE S X 1204.28 1204.10 1203.96 1203.96 1203.88 1204.04
Clang NEON N D 335.14 332.84 335.12 333.05 333.53 333.94
Clang NEON N T 483.33 480.61 483.12 480.93 480.45 481.69
Clang NEON N X 480.11 482.80 480.26 483.17 480.51 481.37
Clang NEON B D 332.51 337.50 332.95 334.56 333.03 334.11
Clang NEON B T 483.19 486.70 480.40 486.63 481.29 483.64
Clang NEON B X 483.14 486.69 483.13 486.77 483.01 484.55
Clang NEON S D 975.90 979.21 976.13 978.92 976.49 977.33
Clang NEON S T 1373.42 1364.28 1373.26 1364.35 1364.02 1367.87
Clang NEON S X 1208.53 1203.87 1209.26 1203.23 1204.65 1205.91

改良版(md5bs)の平均

 
ISA
GCC
CORE
GCC
NEON
Clang
CORE
Clang
NEON
N:D 332.16 307.63 334.40 333.94
N:T 556.94 522.41 483.82 481.69
N:X 558.39 536.07 484.88 481.37
B:D 330.00 306.37 333.20 334.11
B:T 555.09 521.00 481.84 483.64
B:X 557.81 536.04 481.42 484.55
S:D 335.62 1047.96 977.44 977.33
S:T 559.21 1120.18 1367.89 1367.87
S:X 560.14 1022.98 1204.04 1205.91

GCC では NEON,B:D の場合が 306.37 で、Clang では CORE,B:D の場合が 333.20 で最小になりました。

平均でソートした表
コンパイラ 種類 平均 コンパイラ 種類 平均
GCC NEON,B:D 306.37 Clang CORE,B:D 333.20
GCC NEON,N:D 307.63 Clang NEON,N:D 333.94
GCC CORE,B:D 330.00 Clang NEON,B:D 334.11
GCC CORE,N:D 332.16 Clang CORE,N:D 334.40
GCC CORE,S:D 335.62 Clang NEON,N:X 481.37
GCC NEON,B:T 521.00 Clang CORE,B:X 481.42
GCC NEON,N:T 522.41 Clang NEON,N:T 481.69
GCC NEON,B:X 536.04 Clang CORE,B:T 481.84
GCC NEON,N:X 536.07 Clang NEON,B:T 483.64
GCC CORE,B:T 555.09 Clang CORE,N:T 483.82
GCC CORE,N:T 556.94 Clang NEON,B:X 484.55
GCC CORE,B:X 557.81 Clang CORE,N:X 484.88
GCC CORE,N:X 558.39 Clang NEON,S:D 977.33
GCC CORE,S:T 559.21 Clang CORE,S:D 977.44
GCC CORE,S:X 560.14 Clang CORE,S:X 1204.04
GCC NEON,S:X 1022.98 Clang NEON,S:X 1205.91
GCC NEON,S:D 1047.96 Clang NEON,S:T 1367.87
GCC NEON,S:T 1120.18 Clang CORE,S:T 1367.89

x86/x86_64

x86 では、RDTSC を使用しているので、概ね CPU サイクル数になります。

実行結果一覧(長いので折りたたみ)

オリジナル(md5bs_orig:x86)の結果

ISA 1回目 2回目 3回目 4回目 5回目 平均
GCC GPR 407.97 398.59 401.21 398.68 398.12 400.91
GCC SSE 400.45 398.58 398.89 397.89 399.72 399.11
GCC AVX 428.43 426.58 426.93 428.53 427.38 427.57
Clang GPR 457.11 458.43 455.37 459.71 455.57 457.24
Clang SSE 473.62 473.85 473.85 472.85 473.78 473.59
Clang AVX 477.69 477.86 476.72 480.49 474.50 477.45

オリジナル(md5bs_orig:x86_64)の結果

ISA 1回目 2回目 3回目 4回目 5回目 平均
GCC GPR 425.67 424.85 425.23 426.17 423.01 424.99
GCC SSE 437.09 436.23 437.59 435.42 437.42 436.75
GCC AVX 433.30 438.24 436.30 435.33 436.27 435.89
Clang GPR 456.02 457.86 461.90 457.10 458.76 458.33
Clang SSE 461.80 463.15 462.28 461.82 463.53 462.52
Clang AVX 461.90 463.72 463.61 463.22 466.09 463.71

改良版(md5bs:x86)の結果

ISA 1回目 2回目 3回目 4回目 5回目 平均
GCC GPR N D 262.77 262.95 261.93 262.42 263.05 262.62
GCC GPR N T 320.92 321.77 321.22 322.16 321.53 321.52
GCC GPR N X 321.04 321.28 320.62 321.84 320.76 321.11
GCC GPR B D 263.50 262.13 262.35 263.10 262.20 262.66
GCC GPR B T 322.77 320.79 321.15 322.33 321.55 321.72
GCC GPR B X 322.08 321.94 322.91 320.76 321.66 321.87
GCC GPR S D 258.92 258.82 259.55 259.22 258.74 259.05
GCC GPR S T 321.87 320.09 321.68 320.41 321.60 321.13
GCC GPR S X 322.83 324.13 322.73 322.48 322.83 323.00
GCC SSE N D 261.86 262.34 263.79 261.92 263.59 262.70
GCC SSE N T 320.71 321.19 321.97 322.05 322.58 321.70
GCC SSE N X 321.64 322.28 322.42 321.63 322.08 322.01
GCC SSE B D 260.70 259.84 259.08 260.29 258.94 259.77
GCC SSE B T 320.81 320.24 320.50 321.37 320.63 320.71
GCC SSE B X 321.34 320.51 322.11 320.81 321.82 321.32
GCC SSE S D 348.15 348.05 348.71 348.29 348.63 348.37
GCC SSE S T 370.97 372.03 371.33 371.69 373.95 371.99
GCC SSE S X 301.67 301.40 302.90 301.58 302.58 302.03
GCC AVX N D 261.82 262.97 261.65 261.86 263.56 262.37
GCC AVX N T 322.11 321.10 321.81 324.40 322.59 322.40
GCC AVX N X 322.12 321.82 321.52 321.89 320.47 321.56
GCC AVX B D 259.20 260.34 259.10 259.12 260.41 259.63
GCC AVX B T 320.30 321.56 320.68 320.10 321.58 320.84
GCC AVX B X 259.58 259.81 259.04 258.07 259.83 259.27
GCC AVX S D 348.34 349.27 348.80 350.05 348.10 348.91
GCC AVX S T 372.02 371.80 372.16 371.16 372.35 371.90
GCC AVX S X 313.90 314.35 312.90 314.03 314.38 313.91
Clang GPR N D 306.05 308.56 305.89 306.34 307.45 306.86
Clang GPR N T 295.40 296.06 293.19 295.77 294.45 294.97
Clang GPR N X 296.26 295.02 294.07 295.97 294.53 295.17
Clang GPR B D 306.62 308.30 305.83 306.35 307.51 306.92
Clang GPR B T 293.67 296.13 294.20 295.70 295.32 295.00
Clang GPR B X 294.21 296.56 296.30 296.33 293.29 295.34
Clang GPR S D 305.90 306.56 305.36 305.58 306.09 305.90
Clang GPR S T 294.87 293.45 293.75 295.20 297.43 294.94
Clang GPR S X 339.56 342.89 336.89 342.88 338.08 340.06
Clang SSE N D 305.96 305.61 306.30 304.84 305.31 305.60
Clang SSE N T 293.79 294.15 294.43 294.62 295.37 294.47
Clang SSE N X 294.13 293.57 294.68 293.91 294.85 294.23
Clang SSE B D 313.45 326.08 304.71 302.66 303.99 310.18
Clang SSE B T 305.73 308.47 306.43 306.55 307.09 306.85
Clang SSE B X 306.76 306.84 306.66 306.61 305.85 306.54
Clang SSE S D 286.51 287.99 287.31 288.10 287.17 287.42
Clang SSE S T 302.26 300.22 299.80 301.23 300.24 300.75
Clang SSE S X 281.74 280.64 280.77 281.87 280.55 281.11
Clang AVX N D 307.69 308.49 305.87 308.33 307.04 307.48
Clang AVX N T 298.05 298.38 296.24 296.35 294.17 296.64
Clang AVX N X 295.33 294.32 297.56 294.17 295.85 295.45
Clang AVX B D 305.28 303.31 304.80 303.21 305.33 304.39
Clang AVX B T 309.11 306.97 312.09 307.02 306.03 308.24
Clang AVX B X 252.19 251.88 252.52 252.22 251.21 252.00
Clang AVX S D 294.33 295.06 294.19 293.54 291.73 293.77
Clang AVX S T 299.37 299.94 302.42 300.17 299.48 300.28
Clang AVX S X 281.69 283.01 281.51 282.62 282.68 282.30

改良版(md5bs:x86_64)の結果

ISA 1回目 2回目 3回目 4回目 5回目 平均
GCC GPR N D 256.96 257.21 256.06 257.59 255.54 256.67
GCC GPR N T 357.57 356.19 358.66 354.65 359.62 357.34
GCC GPR N X 352.60 355.81 353.47 355.36 352.42 353.93
GCC GPR B D 256.31 256.41 258.40 255.47 256.45 256.61
GCC GPR B T 357.87 356.56 358.40 354.31 356.23 356.67
GCC GPR B X 354.08 354.31 353.27 353.20 352.54 353.48
GCC GPR S D 255.50 258.72 255.21 257.10 255.14 256.33
GCC GPR S T 354.59 356.76 355.84 357.69 356.14 356.20
GCC GPR S X 367.23 363.54 362.16 361.69 363.25 363.57
GCC SSE N D 257.35 259.63 257.12 257.03 255.62 257.35
GCC SSE N T 314.19 314.64 313.42 313.90 313.86 314.00
GCC SSE N X 312.98 313.91 311.79 314.15 312.18 313.00
GCC SSE B D 263.13 265.71 263.71 264.56 261.81 263.78
GCC SSE B T 310.23 313.00 313.28 312.44 311.76 312.14
GCC SSE B X 308.33 311.29 308.94 312.98 310.15 310.34
GCC SSE S D 345.95 342.75 346.63 344.33 347.49 345.43
GCC SSE S T 357.23 358.31 358.05 357.48 360.34 358.28
GCC SSE S X 302.82 304.24 302.93 302.95 303.91 303.37
GCC AVX N D 257.54 255.75 253.70 256.45 255.62 255.81
GCC AVX N T 313.38 308.89 313.26 312.11 311.77 311.88
GCC AVX N X 313.57 312.99 314.11 312.09 313.88 313.33
GCC AVX B D 263.60 262.46 264.83 262.05 264.58 263.50
GCC AVX B T 310.24 311.77 312.41 311.48 312.30 311.64
GCC AVX B X 263.60 263.59 265.41 265.45 265.01 264.61
GCC AVX S D 350.30 345.10 341.74 344.86 345.25 345.45
GCC AVX S T 358.64 358.57 358.21 359.84 358.64 358.78
GCC AVX S X 313.22 311.52 313.15 312.47 309.78 312.03
Clang GPR N D 300.57 302.18 300.31 300.31 302.04 301.08
Clang GPR N T 278.92 280.39 278.86 278.66 282.17 279.80
Clang GPR N X 278.86 279.28 277.39 278.68 280.04 278.85
Clang GPR B D 300.46 301.16 301.44 299.97 299.21 300.45
Clang GPR B T 279.32 280.46 278.16 278.72 279.91 279.31
Clang GPR B X 279.80 280.98 281.75 279.37 280.72 280.52
Clang GPR S D 299.30 300.89 300.16 299.37 301.26 300.20
Clang GPR S T 279.73 281.44 279.93 280.17 281.48 280.55
Clang GPR S X 291.58 293.09 291.58 292.20 292.52 292.19
Clang SSE N D 300.35 301.89 300.53 300.02 301.63 300.88
Clang SSE N T 278.76 281.00 279.40 279.15 279.41 279.54
Clang SSE N X 278.96 280.83 279.18 281.02 280.06 280.01
Clang SSE B D 301.03 300.04 299.74 300.78 302.69 300.86
Clang SSE B T 279.05 280.84 279.97 279.25 280.33 279.89
Clang SSE B X 278.90 281.16 277.63 279.39 281.28 279.67
Clang SSE S D 293.28 292.23 290.10 293.19 292.92 292.34
Clang SSE S T 299.61 302.07 307.42 300.91 301.28 302.26
Clang SSE S X 281.94 282.91 281.32 279.91 283.43 281.90
Clang AVX N D 299.80 301.19 300.86 301.99 300.21 300.81
Clang AVX N T 278.06 278.10 280.44 279.53 278.99 279.02
Clang AVX N X 278.85 280.27 278.93 279.24 280.24 279.51
Clang AVX B D 301.25 302.76 301.16 301.76 300.56 301.50
Clang AVX B T 280.73 279.43 279.48 280.34 279.65 279.93
Clang AVX B X 251.00 251.88 249.67 251.93 250.70 251.04
Clang AVX S D 288.59 289.64 288.52 288.43 288.12 288.66
Clang AVX S T 295.95 298.04 296.20 297.59 295.22 296.60
Clang AVX S X 280.93 282.61 281.30 282.59 280.97 281.68

改良版(md5bs:x86)の平均

 
ISA
GCC
GPR
GCC
SSE
GCC
AVX
Clang
GPR
Clang
SSE
Clang
AVX
N:D 262.62 262.70 262.37 306.86 305.60 307.48
N:T 321.52 321.70 322.40 294.97 294.47 296.64
N:X 321.11 322.01 321.56 295.17 294.23 295.45
B:D 262.66 259.77 259.63 306.92 310.18 304.39
B:T 321.72 320.71 320.84 295.00 306.85 308.24
B:X 321.87 321.32 259.27 295.34 306.54 252.00
S:D 259.05 348.37 348.91 305.90 287.42 293.77
S:T 321.13 371.99 371.90 294.94 300.75 300.28
S:X 323.00 302.03 313.91 340.06 281.11 282.30

GCC では GPR,S:D の場合が 259.05 で、Clang では AVX,B:X の場合が 252.00 で最小になりました。

平均でソートした表
コンパイラ 種類 平均 コンパイラ 種類 平均
GCC GPR,S:D 259.05 Clang AVX,B:X 252.00
GCC AVX,B:X 259.27 Clang SSE,S:X 281.11
GCC AVX,B:D 259.63 Clang AVX,S:X 282.30
GCC SSE,B:D 259.77 Clang SSE,S:D 287.42
GCC AVX,N:D 262.37 Clang AVX,S:D 293.77
GCC GPR,N:D 262.62 Clang SSE,N:X 294.23
GCC GPR,B:D 262.66 Clang SSE,N:T 294.47
GCC SSE,N:D 262.70 Clang GPR,S:T 294.94
GCC SSE,S:X 302.03 Clang GPR,N:T 294.97
GCC AVX,S:X 313.91 Clang GPR,B:T 295.00
GCC SSE,B:T 320.71 Clang GPR,N:X 295.17
GCC AVX,B:T 320.84 Clang GPR,B:X 295.34
GCC GPR,N:X 321.11 Clang AVX,N:X 295.45
GCC GPR,S:T 321.13 Clang AVX,N:T 296.64
GCC SSE,B:X 321.32 Clang AVX,S:T 300.28
GCC GPR,N:T 321.52 Clang SSE,S:T 300.75
GCC AVX,N:X 321.56 Clang AVX,B:D 304.39
GCC SSE,N:T 321.70 Clang SSE,N:D 305.60
GCC GPR,B:T 321.72 Clang GPR,S:D 305.90
GCC GPR,B:X 321.87 Clang SSE,B:X 306.54
GCC SSE,N:X 322.01 Clang SSE,B:T 306.85
GCC AVX,N:T 322.40 Clang GPR,N:D 306.86
GCC GPR,S:X 323.00 Clang GPR,B:D 306.92
GCC SSE,S:D 348.37 Clang AVX,N:D 307.48
GCC AVX,S:D 348.91 Clang AVX,B:T 308.24
GCC AVX,S:T 371.90 Clang SSE,B:D 310.18
GCC SSE,S:T 371.99 Clang GPR,S:X 340.06

改良版(md5bs:x86_64)の平均

 
ISA
GCC
GPR
GCC
SSE
GCC
AVX
Clang
GPR
Clang
SSE
Clang
AVX
N:D 256.67 257.35 255.81 301.08 300.88 300.81
N:T 357.34 314.00 311.88 279.80 279.54 279.02
N:X 353.93 313.00 313.33 278.85 280.01 279.51
B:D 256.61 263.78 263.50 300.45 300.86 301.50
B:T 356.67 312.14 311.64 279.31 279.89 279.93
B:X 353.48 310.34 264.61 280.52 279.67 251.04
S:D 256.33 345.43 345.45 300.20 292.34 288.66
S:T 356.20 358.28 358.78 280.55 302.26 296.60
S:X 363.57 303.37 312.03 292.19 281.90 281.68

GCC では AVX,N:D の場合が 255.81 で、Clang では AVX,B:X の場合が 251.04 で最小になりました。

平均でソートした表
コンパイラ 種類 平均 コンパイラ 種類 平均
GCC AVX,N:D 255.81 Clang AVX,B:X 251.04
GCC GPR,S:D 256.33 Clang GPR,N:X 278.85
GCC GPR,B:D 256.61 Clang AVX,N:T 279.02
GCC GPR,N:D 256.67 Clang GPR,B:T 279.31
GCC SSE,N:D 257.35 Clang AVX,N:X 279.51
GCC AVX,B:D 263.50 Clang SSE,N:T 279.54
GCC SSE,B:D 263.78 Clang SSE,B:X 279.67
GCC AVX,B:X 264.61 Clang GPR,N:T 279.80
GCC SSE,S:X 303.37 Clang SSE,B:T 279.89
GCC SSE,B:X 310.34 Clang AVX,B:T 279.93
GCC AVX,B:T 311.64 Clang SSE,N:X 280.01
GCC AVX,N:T 311.88 Clang GPR,B:X 280.52
GCC AVX,S:X 312.03 Clang GPR,S:T 280.55
GCC SSE,B:T 312.14 Clang AVX,S:X 281.68
GCC SSE,N:X 313.00 Clang SSE,S:X 281.90
GCC AVX,N:X 313.33 Clang AVX,S:D 288.66
GCC SSE,N:T 314.00 Clang GPR,S:X 292.19
GCC SSE,S:D 345.43 Clang SSE,S:D 292.34
GCC AVX,S:D 345.45 Clang AVX,S:T 296.60
GCC GPR,B:X 353.48 Clang GPR,S:D 300.20
GCC GPR,N:X 353.93 Clang GPR,B:D 300.45
GCC GPR,S:T 356.20 Clang AVX,N:D 300.81
GCC GPR,B:T 356.67 Clang SSE,B:D 300.86
GCC GPR,N:T 357.34 Clang SSE,N:D 300.88
GCC SSE,S:T 358.28 Clang GPR,N:D 301.08
GCC AVX,S:T 358.78 Clang AVX,B:D 301.50
GCC GPR,S:X 363.57 Clang SSE,S:T 302.26

手元の環境では、このような結果になりましたが、同一バイナリでも動作環境によって異なるので、使用する環境での確認が必要です。

2
2
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
2
2