#include <ctime>
#include <iostream>
#include <cstdint>
#ifdef __BMI2__
#include <immintrin.h>
#endif
// テスト用のmix(この関数は遅いよ!これより早いの作ってね!)
uint32_t mix_test(uint16_t a, uint16_t b) {
uint32_t o = 0;
for (int i = 0; i < 16; i++) {
if (a & (1 << i)) o |= 1 << (i * 2 + 0);
if (b & (1 << i)) o |= 1 << (i * 2 + 1);
}
return o;
}
// 16bit を 0b0101…(偶数ビット)に展開
static inline uint32_t part1by1(uint32_t x) {
x &= 0x0000FFFFu;
x = (x | (x << 8)) & 0x00FF00FFu;
x = (x | (x << 4)) & 0x0F0F0F0Fu;
x = (x | (x << 2)) & 0x33333333u;
x = (x | (x << 1)) & 0x55555555u;
return x;
}
// mixはaとbのbitを交互に詰めて返す。aが下位(偶数)ビット側。
uint32_t mix(uint16_t a, uint16_t b) {
#ifdef __BMI2__
// BMI2 があるならビットデポジットで最速(-mbmi2 などで有効化)
uint32_t ae = _pdep_u32(a, 0x55555555u); // 偶数ビットに a を配置
uint32_t bo = _pdep_u32(b, 0xAAAAAAAAu); // 奇数ビットに b を配置
return ae | bo;
#else
// ポータブルな定数マスク法
return part1by1(a) | (part1by1(b) << 1);
#endif
}
// テスト
void test(uint32_t(*func)(uint16_t, uint16_t)) {
std::cout << "test...";
for (int i = 0; i < 100; i++) {
uint16_t a = rand(), b = rand();
if (func(a, b) != mix_test(a, b)) {
std::cout << "failed" << std::endl;
exit(EXIT_FAILURE);
}
}
std::cout << "passed!" << std::endl;
}
// ベンチマーク
void benchmark(uint32_t (*func)(uint16_t, uint16_t)) {
std::cout << "benchmark...";
uint32_t dammy = 0;
double start = clock();
for (int i = 0; i < 50000000; i++) dammy ^= func(rand(), rand());
double end = clock();
std::cout << "end! elapsed:" << (end - start) / CLOCKS_PER_SEC << "s " << dammy << std::endl;
}
int main() {
test(mix);
benchmark(mix);
return 0;
}