この辺の実装がどのようになっているのかを勉強したい。ChatGPTにお願いしながら、読み進めていくことにする。
ghist_words:折り畳みシフトレジスタ(folded_shift_register)
using history_type = folded_shift_register<TABLE_INDEX_BITS>; std::array<history_type, NTABLES> ghist_words = []() { decltype(ghist_words) retval; std::transform(std::cbegin(history_lengths), std::cend(history_lengths), std::begin(retval), [](const auto len) { return history_type{len}; }); return retval; }();
一般に「折り畳みシフトレジスタ(folded shift register)」は、長いグローバル履歴ビット列を、指定されたビット数(ここでは12ビット)に"折り畳む"という仕組みを持っている。
たとえば「232ビット分の履歴」をそのまま4096要素のインデックスに使用するのは大きすぎるため、履歴中のビットをシフト&XORなどで畳み込みながら12ビット分に縮めておき、value()を呼ぶとその折り畳まれた「12ビット分のビットベクタ」を返す、という動作をする。
TABLE_INDEX_BITSが12であるため、各history_typeインスタンスは「12ビットに折り畳んで保持できる履歴レジスタ」を表す。
ghist_wordsの初期化
std::array<history_type, NTABLES> ghist_wordsは、16個のhistory_typeを格納する。
右辺のラムダ([]() { … }())を使って、次のように初期化する:
decltype(ghist_words) retval;でまず同じ型の空の配列を用意する。std::transform(std::cbegin(history_lengths), std::cend(history_lengths), std::begin(retval), [](const auto len) { return history_type{len}; });history_lengthsには16個の「履歴長」が並んでいる。- この
transformで、要素len(例えば3, 4, 6, …, 232)を取り出し、history_type{len}という形でコンストラクタを呼び出して生成したものをretval[i]に順次格納する。 - つまり、
ghist_words[i]はhistory_type(history_lengths[i])で初期化される。- 例:
ghist_words[0] = history_type{bits{0}}→ バイアス用であるため履歴長0、 ghist_words[1] = history_type{bits{3}}→ レジスタ長3ビット、ghist_words[15] = history_type{bits{232}}→ 232ビットを折り畳むレジスタ、という具合である。
- 例:
- 最後に
retvalを返してghist_wordsに代入する。
結果として、テーブルごとに異なる長さの「折り畳まれた履歴レジスタ」が16個用意される。
これを使用して、実際の分岐が起きるたびにグローバル履歴を更新し、value()を呼ぶとそれぞれの折り畳み済みビット列(12ビット分)を取り出せる仕組みとなっている。