以下の内容はhttps://msyksphinz.hatenablog.com/entry/2025/05/23/040000より取得しました。


Champsim の Hashed Perceptronの実装を読む (1. テーブルの定義)

github.com

この辺の実装がどのようになっているのかを勉強したい。ChatGPTにお願いしながら、読み進めていくことにする。


Hashed Perceptronのテーブルの定義

    using bits = champsim::data::bits;                 // saves some typing
    constexpr static std::size_t NTABLES = 16;         // this many tables
    constexpr static bits MAX_HIST{232};                // maximum history length
    constexpr static bits MIN_HIST{3};                  // minimum history length (for table 1; table 0 is biases)
    constexpr static std::size_t TABLE_SIZE = 1 << 12; // 12-bit indices for the tables
    constexpr static bits TABLE_INDEX_BITS{champsim::msl::lg2(TABLE_SIZE)};
    constexpr static int THRESHOLD = 1;

    constexpr static std::array<bits, NTABLES> history_lengths = {
        bits{}, MIN_HIST, bits{4}, bits{6}, bits{8}, bits{10}, bits{14}, bits{19},
        bits{26}, bits{36}, bits{49}, bits{67}, bits{91}, bits{125}, bits{170}, MAX_HIST}; // geometric global history lengths

    // tables of 8-bit weights
    std::array<std::array<champsim::msl::sfwcounter<8>, TABLE_SIZE>, NTABLES> tables{};

    // words that store the global history
    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;
    }();

テーブル数とサイズ、閾値などの定数

constexpr static std::size_t NTABLES = 16;

分割して並列に持つパーセプトロン・テーブル(重みテーブル)の数は16である。つまり、最終的に16個の小さなテーブルからそれぞれ重みを取り出して合算する設計となっている。

グローバル履歴の長さを設定

constexpr static bits MAX_HIST{232};
constexpr static bits MIN_HIST{3};
  • MAX_HIST = 232:グローバル履歴として最大232ビットまで使用することを想定している。
  • MIN_HIST = 3:最小で3ビットの履歴を使用するテーブル(table1)が存在する。table0はバイアス用であるため履歴長は0である。

テーブル0から15まですべて異なる「履歴長」を持たせるが、最小は3ビット、最大は232ビットまでの設定となっている。

各テーブルのサイズを決定

constexpr static std::size_t TABLE_SIZE = 1 << 12; // 4096 エントリ
constexpr static bits TABLE_INDEX_BITS{champsim::msl::lg2(TABLE_SIZE)};

各テーブルのサイズは212 = 4096要素である。 TABLE_INDEX_BITSlg2(TABLE_SIZE)であるため、この場合は12となる。 つまり「インデックスを12ビット分使用する」ことを意味する。 したがって、上記のget_index()で生成されるインデックス(XOR結果)は、最終的に0~4095の範囲に収まる。

しきい値の設定

constexpr static int THRESHOLD = 1;

パーセプトロン合計値youtを判定する閾値は1に設定されている。 合計が1以上ならtaken、0以下ならnot takenと予測する。

history_lengths:テーブルごとの履歴長を決める

constexpr static std::array<bits, NTABLES> history_lengths = {
    bits{}, MIN_HIST, bits{4}, bits{6}, bits{8}, bits{10}, bits{14}, bits{19},
    bits{26}, bits{36}, bits{49}, bits{67}, bits{91}, bits{125}, bits{170}, MAX_HIST
};

16個の配列の集合で、各インデックス(0~15)に対応する「そのテーブルで参照に用いるグローバル履歴長」をビット数で示している。

  • history_lengths[0] = bits{} → 長さ0(バイアスのみ)
  • history_lengths[1] = MIN_HIST = bits{3} → 3ビット履歴
  • history_lengths[2] = bits{4} → 4ビット履歴
  • history_lengths[15] = MAX_HIST = bits{232} → 232ビットすべての履歴

中身を見ると単純な線形増加ではなく、3, 4, 6, 8, 10, 14, 19, 26, 36, 49, 67, 91, 125, 170, 232といった幾何級数的に増加する履歴長となっている。 小さいテーブル(浅い履歴)から大きいテーブル(深い履歴)へ段階的に幅を持たせることで、短い履歴だけで済むパターンから非常に長い依存を必要とするパターンまでをカバーして精度を向上させる工夫となっている。

tables:8ビット重みの2次元配列

std::array<std::array<champsim::msl::sfwcounter<8>, TABLE_SIZE>, NTABLES> tables{};

テーブル数(16)、内側が各テーブルのエントリ数(4096)の2次元配列を構成している。 champsim::msl::sfwcounter<8>は「8ビット幅の符号付きまたは飽和カウンタ」である。 1つのエントリが−128~+127(または0~255)のいずれかで、更新時に飽和するようなカウンタとなっている。 tables[i][j]で「テーブルiのインデックスjに対応する重み」を表し、予測時にはこの重み値を合算していく。




以上の内容はhttps://msyksphinz.hatenablog.com/entry/2025/05/23/040000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14