反復ごとに4つの要素をインクリメントして、SSEでそれを試してみることができます。
警告:テストされていないコードが続きます...
#include <stdint.h>
#include <emmintrin.h>
uint32_t bit_counter[64] __attribute__ ((aligned(16)));
// make sure bit_counter array is 16 byte aligned for SSE
void Count_SSE(uint64 bits)
{
const __m128i inc_table[16] = {
_mm_set_epi32(0, 0, 0, 0),
_mm_set_epi32(0, 0, 0, 1),
_mm_set_epi32(0, 0, 1, 0),
_mm_set_epi32(0, 0, 1, 1),
_mm_set_epi32(0, 1, 0, 0),
_mm_set_epi32(0, 1, 0, 1),
_mm_set_epi32(0, 1, 1, 0),
_mm_set_epi32(0, 1, 1, 1),
_mm_set_epi32(1, 0, 0, 0),
_mm_set_epi32(1, 0, 0, 1),
_mm_set_epi32(1, 0, 1, 0),
_mm_set_epi32(1, 0, 1, 1),
_mm_set_epi32(1, 1, 0, 0),
_mm_set_epi32(1, 1, 0, 1),
_mm_set_epi32(1, 1, 1, 0),
_mm_set_epi32(1, 1, 1, 1)
};
for (int i = 0; i < 64; i += 4)
{
__m128i vbit_counter = _mm_load_si128(&bit_counter[i]);
// load 4 ints from bit_counter
int index = (bits >> i) & 15; // get next 4 bits
__m128i vinc = inc_table[index]; // look up 4 increments from LUT
vbit_counter = _mm_add_epi32(vbit_counter, vinc);
// increment 4 elements of bit_counter
_mm_store_si128(&bit_counter[i], vbit_counter);
} // store 4 updated ints
}
仕組み:基本的に、ここで行っているのは、元のループをベクトル化して、ループの反復ごとに1ではなく4ビットを処理することです。したがって、64ではなく16のループ反復があります。反復ごとに、から4ビットをロードしbits
、を使用します。それらは、現在の4ビットの4つの増分のすべての可能な組み合わせを含むLUTへのインデックスとして使用されます。次に、これらの4つの増分をbit_counterの現在の4つの要素に追加します。
ロードとストアおよび追加の数は4分の1に削減されますが、これはLUTロードおよびその他のハウスキーピングによっていくらか相殺されます。ただし、2倍の速度が得られる場合があります。あなたがそれを試してみることにした場合、私は結果を知りたいと思います。