バイナリ検索アルゴリズムの glibc の実装をコピーし、必要に応じて少し変更しました。私はそれをテストし、GCC について学んだ他のこと (属性と組み込み) をテストすることにしました。コードは次のようになります。
int main() {
uint_fast16_t a[61] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61 };
uint64_t t1 = Time(0);
for(register uint_fast16_t i = 0; i < 10000000; ++i) {
binary_search(rand() % 62, a, 61);
}
printf("%ld\n", Time(0) - t1);
return 0;
}
さて、このプログラムは問題なく動作します。たとえば、次のようなコード行を追加すると、問題が発生します。
uint_fast16_t a[61] __attribute__ ((aligned (64) )) = /* ... */
この場合、より高速なコードが期待できますが、複数のテスト (数十回のテスト) の後でもパフォーマンスは変わりませんでした。また、プログラムを 8 と 1 の位置合わせでテストしましたが、変更はありませんでした。タイプサイズよりも小さいアラインメントを使用しているため(私の場合、64ビットマシンではuint_fast16_tは8バイトです)、gccがエラー/警告をスローすることさえ期待していましたが、何もありませんでした。次に、キャッシングを追加する別の変更 (GCC 9 で導入)。for ループの前に次のコードを追加しました。
caches(a, uint_fast16_t, uint_fast16_t, 61, 0, 3);
// where "caches" is:
#define caches(x, type, data_type, size, rw, l) ({ \
for(type Q2W0 = 0; Q2W0 < size; Q2W0 += 64 / sizeof(data_type)) { \
__builtin_prefetch(x + Q2W0, rw, l); \
} \
})
性能も変わらない。CPU が最初に配列を自動的にキャッシュしている可能性があることがわかったbinary_search
ので、ループを排除しfor
、キャッシュ ラインの有無にかかわらず再度数回測定しましたが、パフォーマンスの変化にも気づいていません。
詳しくは:
- CentOS8 64bit 最新カーネルを使用
- GCC 9.2.1 20191120 の使用
- でコンパイル、コンパイル
-O3 -Wall -pthread -lanl -Wno-missing-braces -Wmissing-field-initializers
中にエラー/警告なし - 物事は最適化されていません(チェックされたasm出力)
私は何かを知らないと確信しています/私は何か間違ったことをしています。
完全なコードはこちらから入手できます。