i7で最も効率的な方法で、浮動ベクトルとビットベクトルの間の内積を計算しようとしています。実際には、128 次元または 256 次元のベクトルでこの操作を行っていますが、説明のために、問題を説明するために 64 次元のコードを書きましょう。
// a has 64 elements. b is a bitvector of 64 dimensions.
float dot(float *restrict a, uint64_t b) {
float sum = 0;
for(int i=0; b && i<64; i++, b>>=1) {
if (b & 1) sum += a[i];
}
return sum;
}
これはもちろん機能しますが、問題は、これがプログラム全体のタイム クリティカルな場所であるため (50 分間の実行で 95% の CPU 時間を消費する)、どうしても高速化する必要があることです。
私の推測では、上記の分岐はゲーム キラーです (順不同の実行を防ぎ、不適切な分岐予測を引き起こします)。ここでベクトル命令を使用して役立つかどうかはわかりません。-std=c99 -march=native -mtune=native -Ofast -funroll-loops で gcc 4.8 を使用すると、現在この出力が得られます
movl $4660, %edx
movl $5, %ecx
xorps %xmm0, %xmm0
.p2align 4,,10
.p2align 3
.L4:
testb $1, %cl
je .L2
addss (%rdx), %xmm0
.L2:
leaq 4(%rdx), %rax
shrq %rcx
testb $1, %cl
je .L8
addss 4(%rdx), %xmm0
.L8:
shrq %rcx
testb $1, %cl
je .L9
addss 4(%rax), %xmm0
.L9:
shrq %rcx
testb $1, %cl
je .L10
addss 8(%rax), %xmm0
.L10:
shrq %rcx
testb $1, %cl
je .L11
addss 12(%rax), %xmm0
.L11:
shrq %rcx
testb $1, %cl
je .L12
addss 16(%rax), %xmm0
.L12:
shrq %rcx
testb $1, %cl
je .L13
addss 20(%rax), %xmm0
.L13:
shrq %rcx
testb $1, %cl
je .L14
addss 24(%rax), %xmm0
.L14:
leaq 28(%rax), %rdx
shrq %rcx
cmpq $4916, %rdx
jne .L4
ret
編集データを並べ替えても問題ありません (並べ替えがすべてのパラメーターで同じである限り)。順序は関係ありません。
Chris Dodd の SSE2 コードの 3 倍以上の速度で動作するものがあるかどうか疑問に思っています。
新しいメモ: AVX/AVX2 コードも歓迎です!
編集 2 ビットベクトルが与えられた場合、128 (または 256 ビットの場合は 256) の異なる float ベクトルで乗算する必要があります (したがって、一度に複数の float ベクトルを使用しても問題ありません)。これがプロセス全体です。プロセス全体をスピードアップするものも大歓迎です!