Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configs/example/kmhv3.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def setKmhV3Params(args, system):
cpu.branchPred.mbtb.enabled = True
cpu.branchPred.tage.enabled = True
cpu.branchPred.ittage.enabled = True
cpu.branchPred.mgsc.enabled = False
cpu.branchPred.mgsc.enabled = True
cpu.branchPred.ras.enabled = True

# l1 cache per core
Expand Down
54 changes: 54 additions & 0 deletions docs/Gem5_Docs/frontend/mgsc_notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# MGSC 预测器备忘

面向本仓库 BTB 侧的 MGSC(Multi-Geometric Statistical Corrector)总结:设计特点、关键结构、改进思路、统计计数器、以及便于验证的敏感小测试。

## 1. MGSC 设计要点
- 核心角色:TAGE 做“主判官”,MGSC 做“量化纠错”。MGSC 拿到 TAGE 的预测与置信度后,基于多类独立的历史特征表(几何长度)求和,给出一个带权的分数;只有当分数绝对值高于动态阈值时才覆盖 TAGE,否则保持原判。
- 特征多样性(降维防冲突):
- **G** 全局方向历史:捕获长程相关;
- **P** 路径历史:区分同一分支的不同来路;
- **L** 局部(per-PC)历史:解决每个分支的重复模式;
- **BW** 后向历史:强化循环/尾递归偏好;
- **I/IMLI** 迭代计数:对“第 N 次迭代取反”类模式敏感;
- **Bias** 静态偏置:兜底偏好。
这些表使用折叠历史压缩到较短索引位宽,降低端口和存储压力,同时减少与 TAGE 的直接结构耦合。
- 决策与门控:各表计数器 percsum 做线性求和,并用简化权重(1x/2x 档)缩放,合成 `total_sum`。阈值由全局+PC 两级组成,并根据 TAGE 置信度动态放宽/收紧(高/中/低置信度对应 |sum| > thres/2, /4, /8)。这样在 TAGE 自信时,需要更强证据才覆盖。
- 自适应更新:仅在 SC 预测错或低置信时更新,避免破坏已稳定的正确判决;权重只在“该表是否关键到翻转决策”时调整;阈值在 SC 与真实冲突时上调,吻合时下调,使覆盖策略自动收敛到合适激进度。

## 2. 关键数据结构(参考 `src/cpu/pred/btb/btb_mgsc.hh/.cc`)
- `MgscPrediction`:一次预测的完整快照,包含:
- 总分 `total_sum`,是否使用 MGSC (`use_mgsc`),最终 taken 判定;
- TAGE 原始预测与置信度(高/中/低);
- 各表索引、raw/scaled percsum、`weight_scale_diff`(权重翻倍/归零是否翻转决策)、组合阈值。
这些字段既驱动训练,也被统计模块消费。
- 折叠历史:`index{G,P,Bw,L,I}FoldedHist`,把长历史折叠成较短索引用;Bias 索引还混入 TAGE 主预测、低置信标志,形成轻量级“条件偏置”。
- 预测与权重表:六类计数器矩阵 `bw/l/i/g/p/biasTable[table][index][way]`,存储方向倾向;对应的 `*_WeightTable[index]` 只用 PC hash,提供 1x/2x 缩放权重(-32..31,经简化映射)。
- 阈值:全局 `updateThreshold`(单计数器)+ PC 表 `pUpdateThreshold`(按 PC hash 的多计数器),共同决定覆盖门槛,随误差自适应。
- 元数据:`MgscMeta` 在 fetch 时缓存预测与折叠历史,用于后续恢复/回滚,保证推测一致性。

## 3. 新增统计计数器及含义(已合入)
路径:`system.cpu.branchPred.mgsc.*`
- SC/TAGE 关系:`scCorrectTageWrong`、`scWrongTageCorrect`、`scCorrectTageCorrect`、`scWrongTageWrong`、`scUsed`、`scNotUsed`、`scPredCorrect/Wrong`、`predHit/Miss`。
- 权重关键性:`*WeightScaleDiff`(bw/l/i/g/p/bias)——该表权重翻倍/去掉会翻转决策的次数。
- Raw percsum 符号正确率:`*PercsumCorrect/Wrong` —— 该表自身方向是否与真实一致。
- 阈值方向:`pcThresholdInc/Dec`、`globalThresholdInc/Dec` —— SC 与真实冲突多则递增,反之递减。
- 按 TAGE 置信度分桶的 SC 使用/绕过:`scHigh/Mid/LowUseCorrect/Wrong`、`scHigh/Mid/LowBypass`。

解读小贴士:
- 某表 `WeightScaleDiff` 高且 `PercsumCorrect` 高:表在“起关键作用”。若后者低,则可能误导。
- `pc/globalThresholdDec` ≫ `Inc`:SC 表现好,门槛在下降;反之说明冲突多。
- `scCorrectTageWrong` ≫ `scWrongTageCorrect`:SC 对 TAGE 有正纠错价值。

## 4. 改进方向(未实现,仅供参考)
1) 权重档位更细(0.5/1/1.5/2 等),提升相关性刻画精度。
2) 权重表索引加入部分历史哈希(而非仅 PC),降低别名。
3) 阈值更新考虑 TAGE 置信度(高置信错时更快抬阈,低置信错时慢抬),减少误覆盖。
4) 更丰富的可观测性:按表/置信度输出热分支热点,或导出 per-PC 纠错热度。

## 5. MGSC 敏感小测试(C 版,放在 'nexus-am/tests/frontendtest/mgsc_test/tests/`)
Comment thread
jensen-yan marked this conversation as resolved.
已提供源码,可按现有 Makefile 通配编译:
- `long_period_flip.c`:长周期偶发翻转 + 噪声。期望 I/G/P 纠偏远距稀疏翻转。
- `xor_dependency.c`:B2 方向 = 上一轮 B0^B1,跨分支相关。期望路径/局部/全局组合纠错。
- `alias_many_branches.c`:16 个相邻分支,各有不同小周期,制造索引/标签冲突。期望 MGSC 通过局部/路径缓解别名。

使用建议:开/关 MGSC 对比 `condMiss`、`mgsc.scCorrectTageWrong/scWrongTageCorrect`、各表 `*PercsumWrong` 与阈值增减,定位是哪类表在纠错或噪声。必要时可缩短 TAGE 历史或减小表尺寸以放大差异。
174 changes: 143 additions & 31 deletions src/cpu/pred/btb/btb_mgsc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,13 @@ BTBMGSC::tickStart()

/**
* Calculate perceptron sum from a table for a given PC
* perceptron sum is the sum of the (2*counter + 1) of the matching entries
* Counter range: [-2^(w-1), 2^(w-1)-1], e.g., [-32, 31] for w=6
* Percsum = sum of (2*counter + 1), transforms to odd numbers, e.g., [-63, 63] per entry
* @param table The table to search in
* @param tableIndices Indices to use for each table component
* @param numTables Number of tables to search
* @param pc PC to match against
* @return Calculated percsum value
* @return Calculated percsum value (positive=taken bias, negative=not-taken bias)
*/
int
BTBMGSC::calculatePercsum(const std::vector<std::vector<std::vector<int16_t>>> &table,
Expand All @@ -166,7 +167,7 @@ BTBMGSC::calculatePercsum(const std::vector<std::vector<std::vector<int16_t>>> &
for (unsigned int i = 0; i < numTables; ++i) {
auto [idx1, idx2] = posHash(pc, tableIndices[i]);
auto &entry = table[i][idx1][idx2];
percsum += (2 * entry + 1); // align to zero center
percsum += (2 * entry + 1); // transform to odd numbers, avoid zero
}
return percsum;
}
Expand All @@ -187,17 +188,11 @@ BTBMGSC::findWeight(const std::vector<int16_t> &weightTable, Addr pc)
return entry;
}

/**
* Calculate scaled percsum using weight
* weight range is [-32, 31], return value range is percsum or 2x percsum
* @param weight Weight value
* @param percsum Original percsum value
* @return Scaled percsum value
*/

int
BTBMGSC::calculateScaledPercsum(int weight, int percsum)
{
return ((weight + 64) / 32) * percsum;
return percsum; // disable weight scaling for test
}
Comment thread
jensen-yan marked this conversation as resolved.

/**
Expand Down Expand Up @@ -334,10 +329,11 @@ BTBMGSC::generateSinglePrediction(const BTBEntry &btb_entry, const Addr &startPC

DPRINTF(MGSC, "sc predict %#lx taken %d\n", btb_entry.pc, taken);

return MgscPrediction(btb_entry.pc, total_sum, use_sc_pred, taken, tage_info.tage_pred_taken, total_thres, bwIndex,
lIndex, iIndex, gIndex, pIndex, biasIndex, bw_weight_scale_diff, l_weight_scale_diff,
i_weight_scale_diff, g_weight_scale_diff, p_weight_scale_diff, bias_weight_scale_diff,
bw_percsum, l_percsum, i_percsum, g_percsum, p_percsum, bias_percsum);
return MgscPrediction(btb_entry.pc, total_sum, use_sc_pred, taken, tage_info.tage_pred_taken,
tage_info.tage_pred_conf_high, tage_info.tage_pred_conf_mid, tage_info.tage_pred_conf_low,
total_thres, bwIndex, lIndex, iIndex, gIndex, pIndex, biasIndex, bw_weight_scale_diff,
l_weight_scale_diff, i_weight_scale_diff, g_weight_scale_diff, p_weight_scale_diff,
bias_weight_scale_diff, bw_percsum, l_percsum, i_percsum, g_percsum, p_percsum, bias_percsum);
}

/**
Expand Down Expand Up @@ -544,6 +540,93 @@ BTBMGSC::updateGlobalThreshold(Addr pc, bool update_direction)
updateCounter(update_direction, updateThresholdWidth, updateThreshold);
}

void
BTBMGSC::recordPredictionStats(const MgscPrediction &pred, bool actual_taken, bool sc_pred_taken,
bool tage_pred_taken)
{
auto tage_conf_high = pred.tage_conf_high;
auto tage_conf_mid = pred.tage_conf_mid;
auto tage_conf_low = pred.tage_conf_low;

// SC vs TAGE outcomes
if (pred.use_mgsc) {
mgscStats.scUsed++;
if (sc_pred_taken == actual_taken && tage_pred_taken != actual_taken) {
mgscStats.scCorrectTageWrong++;
} else if (sc_pred_taken != actual_taken && tage_pred_taken == actual_taken) {
mgscStats.scWrongTageCorrect++;
} else if (sc_pred_taken == actual_taken && tage_pred_taken == actual_taken) {
mgscStats.scCorrectTageCorrect++;
} else if (sc_pred_taken != actual_taken && tage_pred_taken != actual_taken) {
mgscStats.scWrongTageWrong++;
}
} else {
mgscStats.scNotUsed++; // sc confidence is low
}

// Record raw percsum correctness and weight criticality for each table
auto recordPercsum = [&](int percsum, statistics::Scalar &correct, statistics::Scalar &wrong) {
if ((percsum >= 0) == actual_taken) {
correct++;
} else {
wrong++;
}
};
if (pred.bw_weight_scale_diff) {
mgscStats.bwWeightScaleDiff++;
}
recordPercsum(pred.bw_percsum, mgscStats.bwPercsumCorrect, mgscStats.bwPercsumWrong);

if (pred.l_weight_scale_diff) {
mgscStats.lWeightScaleDiff++;
}
recordPercsum(pred.l_percsum, mgscStats.lPercsumCorrect, mgscStats.lPercsumWrong);

if (pred.i_weight_scale_diff) {
mgscStats.iWeightScaleDiff++;
}
recordPercsum(pred.i_percsum, mgscStats.iPercsumCorrect, mgscStats.iPercsumWrong);

if (pred.g_weight_scale_diff) {
mgscStats.gWeightScaleDiff++;
}
recordPercsum(pred.g_percsum, mgscStats.gPercsumCorrect, mgscStats.gPercsumWrong);

if (pred.p_weight_scale_diff) {
mgscStats.pWeightScaleDiff++;
}
recordPercsum(pred.p_percsum, mgscStats.pPercsumCorrect, mgscStats.pPercsumWrong);

if (pred.bias_weight_scale_diff) {
mgscStats.biasWeightScaleDiff++;
}
recordPercsum(pred.bias_percsum, mgscStats.biasPercsumCorrect, mgscStats.biasPercsumWrong);

// SC usage under TAGE confidence buckets
auto recordConfOutcome = [&](bool conf_high, bool conf_mid, bool conf_low, bool use, bool correct) {
if (conf_high) {
if (use) {
correct ? mgscStats.scHighUseCorrect++ : mgscStats.scHighUseWrong++;
} else {
mgscStats.scHighBypass++;
}
} else if (conf_mid) {
if (use) {
correct ? mgscStats.scMidUseCorrect++ : mgscStats.scMidUseWrong++;
} else {
mgscStats.scMidBypass++;
}
} else if (conf_low) {
if (use) {
correct ? mgscStats.scLowUseCorrect++ : mgscStats.scLowUseWrong++;
} else {
mgscStats.scLowBypass++;
}
}
};
recordConfOutcome(tage_conf_high, tage_conf_mid, tage_conf_low, pred.use_mgsc, sc_pred_taken == actual_taken);
}

/**
* @brief Update predictor for a single entry and allocate new entries if needed
*
Expand All @@ -566,26 +649,20 @@ BTBMGSC::updateSinglePredictor(const BTBEntry &entry, bool actual_taken, const M
auto sc_pred_taken = total_sum >= 0;
auto tage_pred_taken = pred.taken_before_sc; // tage predictions

// Update statistics
if (use_mgsc) {
mgscStats.scUsed++;
if (sc_pred_taken == actual_taken && tage_pred_taken != actual_taken) {
mgscStats.scCorrectTageWrong++;
} else if (sc_pred_taken != actual_taken && tage_pred_taken == actual_taken) {
mgscStats.scWrongTageCorrect++;
} else if (sc_pred_taken == actual_taken && tage_pred_taken == actual_taken) {
mgscStats.scCorrectTageCorrect++;
} else if (sc_pred_taken != actual_taken && tage_pred_taken != actual_taken) {
mgscStats.scWrongTageWrong++;
}
} else {
mgscStats.scNotUsed++; // sc confidence is low
}
recordPredictionStats(pred, actual_taken, sc_pred_taken, tage_pred_taken);

// Only update tables if prediction was wrong or confidence was low
if (sc_pred_taken != actual_taken || abs(total_sum) < total_thres) {
// get weight table index from startPC
Addr weightTableIdx = getPcIndex(stream.startPC, weightTableIdxWidth);
bool threshold_inc = (sc_pred_taken != actual_taken);
if (threshold_inc) {
mgscStats.pcThresholdInc++;
mgscStats.globalThresholdInc++;
} else {
mgscStats.pcThresholdDec++;
mgscStats.globalThresholdDec++;
}

// Update BW tables
updatePredTable(bwTable, pred.bwIndex, bwTableNum, entry.pc, actual_taken);
Expand Down Expand Up @@ -1091,7 +1168,42 @@ BTBMGSC::MgscStats::MgscStats(statistics::Group *parent)
ADD_STAT(scPredMissTaken, statistics::units::Count::get(), "number of sc prediction miss taken"),
ADD_STAT(scPredMissNotTaken, statistics::units::Count::get(), "number of sc prediction miss not taken"),
ADD_STAT(scPredCorrectTageWrong, statistics::units::Count::get(),"number of sc prediction correct and tage wrong"),
ADD_STAT(scPredWrongTageCorrect, statistics::units::Count::get(),"number of sc prediction wrong and tage correct")
ADD_STAT(scPredWrongTageCorrect, statistics::units::Count::get(),"number of sc prediction wrong and tage correct"),

ADD_STAT(bwWeightScaleDiff, statistics::units::Count::get(), "bw table weight scaling decisive"),
ADD_STAT(lWeightScaleDiff, statistics::units::Count::get(), "l table weight scaling decisive"),
ADD_STAT(iWeightScaleDiff, statistics::units::Count::get(), "i table weight scaling decisive"),
ADD_STAT(gWeightScaleDiff, statistics::units::Count::get(), "g table weight scaling decisive"),
ADD_STAT(pWeightScaleDiff, statistics::units::Count::get(), "p table weight scaling decisive"),
ADD_STAT(biasWeightScaleDiff, statistics::units::Count::get(), "bias table weight scaling decisive"),

ADD_STAT(bwPercsumCorrect, statistics::units::Count::get(), "bw table raw percsum sign correct"),
ADD_STAT(bwPercsumWrong, statistics::units::Count::get(), "bw table raw percsum sign wrong"),
ADD_STAT(lPercsumCorrect, statistics::units::Count::get(), "l table raw percsum sign correct"),
ADD_STAT(lPercsumWrong, statistics::units::Count::get(), "l table raw percsum sign wrong"),
ADD_STAT(iPercsumCorrect, statistics::units::Count::get(), "i table raw percsum sign correct"),
ADD_STAT(iPercsumWrong, statistics::units::Count::get(), "i table raw percsum sign wrong"),
ADD_STAT(gPercsumCorrect, statistics::units::Count::get(), "g table raw percsum sign correct"),
ADD_STAT(gPercsumWrong, statistics::units::Count::get(), "g table raw percsum sign wrong"),
ADD_STAT(pPercsumCorrect, statistics::units::Count::get(), "p table raw percsum sign correct"),
ADD_STAT(pPercsumWrong, statistics::units::Count::get(), "p table raw percsum sign wrong"),
ADD_STAT(biasPercsumCorrect, statistics::units::Count::get(), "bias table raw percsum sign correct"),
ADD_STAT(biasPercsumWrong, statistics::units::Count::get(), "bias table raw percsum sign wrong"),

ADD_STAT(pcThresholdInc, statistics::units::Count::get(), "pc threshold increment"),
ADD_STAT(pcThresholdDec, statistics::units::Count::get(), "pc threshold decrement"),
ADD_STAT(globalThresholdInc, statistics::units::Count::get(), "global threshold increment"),
ADD_STAT(globalThresholdDec, statistics::units::Count::get(), "global threshold decrement"),

ADD_STAT(scHighUseCorrect, statistics::units::Count::get(), "tage high conf, sc used, correct"),
ADD_STAT(scHighUseWrong, statistics::units::Count::get(), "tage high conf, sc used, wrong"),
ADD_STAT(scMidUseCorrect, statistics::units::Count::get(), "tage mid conf, sc used, correct"),
ADD_STAT(scMidUseWrong, statistics::units::Count::get(), "tage mid conf, sc used, wrong"),
ADD_STAT(scLowUseCorrect, statistics::units::Count::get(), "tage low conf, sc used, correct"),
ADD_STAT(scLowUseWrong, statistics::units::Count::get(), "tage low conf, sc used, wrong"),
ADD_STAT(scHighBypass, statistics::units::Count::get(), "tage high conf, sc not used"),
ADD_STAT(scMidBypass, statistics::units::Count::get(), "tage mid conf, sc not used"),
ADD_STAT(scLowBypass, statistics::units::Count::get(), "tage low conf, sc not used")
{
}

Expand Down
Loading