「MD5: RFC 1321 を実装してみる」にある C++ プログラムを弄って改良を試みます。
オリジナル・プログラム (テスト部分は改良版と同じものに変更)
//
// ヘッダ用
//
#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 レジスタの使用ですが、できるだけコンパイラの最適化に依存します。
改良を試みたプログラム
///////////////////////////////////////////////////////////////////////////////
//
// ヘッダ ファイル用
//
///////////////////////////////////////////////////////////////////////////////
#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 -*-
####
-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 |
手元の環境では、このような結果になりましたが、同一バイナリでも動作環境によって異なるので、使用する環境での確認が必要です。