.. _program_listing_file_src_tdm_delay_lib.h: Program Listing for File delay_lib.h ==================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/tdm/delay_lib.h``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #pragma once #include #include #include #include #include #include #include #include #include "json.hpp" enum class RatioLib { GIO, MGT }; enum class DelayScope { Default, Board, InterBoard, InterCluster, InterRack }; struct LinearModel { double a = 0.0, b = 0.0; bool valid = false; double eval(double x) const { return a * x + b; } }; struct PWLModel { std::vector xs; std::vector ys; std::vector slopes; bool valid = false; static PWLModel fromPoints(std::vector x, std::vector y) { PWLModel m; if (x.size() != y.size() || x.size() < 2) { m.valid = false; return m; } // sort & unique by x std::vector> kv; kv.reserve(x.size()); for (size_t i = 0; i < x.size(); ++i) kv.push_back({x[i], y[i]}); std::sort(kv.begin(), kv.end(), [](auto &a, auto &b) { return a.first < b.first; }); // 去重相同x,保留最后一个 std::vector X, Y; X.reserve(kv.size()); Y.reserve(kv.size()); for (size_t i = 0; i < kv.size(); ++i) { if (!X.empty() && std::abs(kv[i].first - X.back()) < 1e-12) { Y.back() = kv[i].second; // 覆盖 } else { X.push_back(kv[i].first); Y.push_back(kv[i].second); } } if (X.size() < 2) { m.valid = false; return m; } m.xs = std::move(X); m.ys = std::move(Y); m.slopes.resize(m.xs.size() - 1); for (size_t i = 0; i < m.slopes.size(); ++i) { double dx = m.xs[i + 1] - m.xs[i]; m.slopes[i] = (m.ys[i + 1] - m.ys[i]) / dx; } m.valid = true; return m; } double eval(double r) const { if (!valid) throw std::runtime_error("PWLModel not valid"); if (r <= xs.front()) { // 左端外推:用第一段斜率 return ys.front() + (r - xs.front()) * slopes.front(); } if (r >= xs.back()) { // 右端外推:用最后一段斜率 return ys.back() + (r - xs.back()) * slopes.back(); } // 中间插值:找到 r 所在的段 auto it = std::upper_bound(xs.begin(), xs.end(), r); size_t i = size_t(std::max(0, int(it - xs.begin()) - 1)); return ys[i] + (r - xs[i]) * slopes[i]; } double slopeAt(double r) const { if (!valid) throw std::runtime_error("PWLModel not valid"); if (r <= xs.front()) return slopes.front(); if (r >= xs.back()) return slopes.back(); auto it = std::upper_bound(xs.begin(), xs.end(), r); size_t i = size_t(std::max(0, int(it - xs.begin()) - 1)); return slopes[i]; } }; struct LibModel { bool has_bypass = false; double bypass_ratio = 2.0; double bypass_delay = 0.0; bool has_low_linear = false; double low_lo = 1.0, low_hi = 8.0; LinearModel low_linear; PWLModel pwl; LinearModel fallback; double delay(double r) const { // bypass 精确点 if (has_bypass && std::abs(r - bypass_ratio) < 1e-12) return bypass_delay; // 低范围线性段 if (has_low_linear && r >= low_lo && r <= low_hi && low_linear.valid) { return low_linear.eval(r); } // 主体 PWL if (pwl.valid) return pwl.eval(r); // 兜底 linear if (fallback.valid) return fallback.eval(r); throw std::runtime_error("LibModel has no valid model to evaluate delay()"); } double slope(double r) const { // bypass 是一个离散点,没有“段”的斜率;可返回: // - 若落在低范围线性:返回线性斜率 // - 否则用 PWL 的 slope或 fallback 的 a if (has_low_linear && r >= low_lo && r <= low_hi && low_linear.valid) { return low_linear.a; } if (pwl.valid) return pwl.slopeAt(r); if (fallback.valid) return fallback.a; throw std::runtime_error("LibModel has no valid model to evaluate slope()"); } }; struct LibMeta { double rmin = 1.0, rmax = 1e12; std::vector choices; bool has_bounds = false; }; struct ScopedLibModels { bool has_gio = false; bool has_mgt = false; LibModel gio; LibModel mgt; LibMeta gio_meta; LibMeta mgt_meta; }; struct DelayLibrary { LibModel gio, mgt; LibMeta gio_meta, mgt_meta; static constexpr int kDelayScopeCount = 5; std::array scoped_models; bool has_scoped_models = false; static inline LinearModel fitLinear(const std::vector &xs, const std::vector &ys) { LinearModel lm; if (xs.size() < 2 || xs.size() != ys.size()) return lm; double Sx = 0, Sy = 0, Sxx = 0, Sxy = 0; const size_t n = xs.size(); for (size_t i = 0; i < n; ++i) { Sx += xs[i]; Sy += ys[i]; Sxx += xs[i] * xs[i]; Sxy += xs[i] * ys[i]; } double denom = n * Sxx - Sx * Sx; if (std::abs(denom) < 1e-12) return lm; lm.a = (n * Sxy - Sx * Sy) / denom; lm.b = (Sy - lm.a * Sx) / n; lm.valid = true; return lm; } static inline bool parsePoints(const nlohmann::json &arr, std::vector &xs, std::vector &ys) { if (!arr.is_array() || arr.empty()) return false; xs.clear(); ys.clear(); xs.reserve(arr.size()); ys.reserve(arr.size()); for (const auto &p : arr) { if (p.is_array() && p.size() == 2) { xs.push_back(p[0].get()); ys.push_back(p[1].get()); } else if (p.is_object()) { if (!p.contains("ratio")) return false; double r = p["ratio"].get(); double d = 0.0; if (p.contains("delay")) d = p["delay"].get(); else if (p.contains("delay_ns")) d = p["delay_ns"].get(); else return false; xs.push_back(r); ys.push_back(d); } else return false; } return xs.size() >= 2; } static inline bool parseBypass(const nlohmann::json &b, double &r, std::optional &dopt) { if (!b.is_object() || !b.contains("ratio")) return false; r = b["ratio"].get(); if (b.contains("delay")) { dopt = b["delay"].get(); return true; } if (b.contains("delay_ns")) { dopt = b["delay_ns"].get(); return true; } // 只有 ratio:允许,delay 由外部推导 dopt = std::nullopt; return true; } static inline void parseMeta(const nlohmann::json &j, LibMeta &meta) { if (j.contains("min_ratio")) meta.rmin = j["min_ratio"].get(); if (j.contains("max_ratio")) meta.rmax = j["max_ratio"].get(); meta.has_bounds = j.contains("min_ratio") || j.contains("max_ratio"); if (j.contains("choices") && j["choices"].is_array()) { meta.choices.clear(); for (auto &c : j["choices"]) meta.choices.push_back(c.get()); } } static inline int scopeIndex(DelayScope scope) { return static_cast(scope); } static inline std::string normalizeName(std::string name) { for (char &ch : name) { unsigned char uch = static_cast(ch); if (std::isalnum(uch)) ch = static_cast(std::tolower(uch)); else ch = '_'; } return name; } static inline std::optional parseScopeName( const std::string &name) { const std::string n = normalizeName(name); if (n == "default" || n == "base") return DelayScope::Default; if (n == "board" || n == "local" || n == "board_local") return DelayScope::Board; if (n == "inter_board" || n == "ibm" || n == "cluster_local") return DelayScope::InterBoard; if (n == "inter_cluster" || n == "icm") return DelayScope::InterCluster; if (n == "inter_rack" || n == "irm") return DelayScope::InterRack; return std::nullopt; } static inline double parseOffsetNs(const nlohmann::json &j) { if (j.contains("offset_ns")) return j["offset_ns"].get(); if (j.contains("delay_offset_ns")) return j["delay_offset_ns"].get(); if (j.contains("add_delay_ns")) return j["add_delay_ns"].get(); return 0.0; } static inline void addDelayOffset(LibModel &model, double offset_ns) { if (std::abs(offset_ns) < 1e-12) return; if (model.has_bypass) model.bypass_delay += offset_ns; if (model.has_low_linear && model.low_linear.valid) model.low_linear.b += offset_ns; if (model.pwl.valid) { for (double &y : model.pwl.ys) y += offset_ns; } if (model.fallback.valid) model.fallback.b += offset_ns; } static inline bool modelUsable(const LibModel &model, RatioLib lib) { if (lib == RatioLib::GIO) { return model.pwl.valid || model.fallback.valid || model.has_low_linear || model.has_bypass; } return model.pwl.valid || model.fallback.valid; } static inline void inferBoundsFromPoints(const LibModel &model, LibMeta &meta) { if (model.pwl.valid && !meta.has_bounds) { meta.rmin = model.pwl.xs.front(); meta.rmax = model.pwl.xs.back(); meta.has_bounds = true; } } static inline void buildGioBypassAndLowLinear(const nlohmann::json &j, LibModel &model) { bool bypass_specified = false; if (j.contains("bypass")) { double br = 0.0; std::optional bdopt; if (parseBypass(j["bypass"], br, bdopt)) { bypass_specified = true; model.has_bypass = true; model.bypass_ratio = br; if (bdopt.has_value()) { model.bypass_delay = *bdopt; } else if (model.pwl.valid) { const double tol = 1e-9; auto it = std::lower_bound(model.pwl.xs.begin(), model.pwl.xs.end(), br); if (it != model.pwl.xs.end() && std::abs(*it - br) < tol) { size_t idx = size_t(it - model.pwl.xs.begin()); model.bypass_delay = model.pwl.ys[idx]; } else { model.bypass_delay = model.pwl.eval(br); } } } } if (!bypass_specified && model.pwl.valid && !model.pwl.xs.empty()) { model.has_bypass = true; model.bypass_ratio = model.pwl.xs.front(); model.bypass_delay = model.pwl.ys.front(); } if (model.has_bypass && model.pwl.valid) { double left_r = model.bypass_ratio, left_d = model.bypass_delay; double right_r = -1, right_d = 0; for (size_t i = 0; i < model.pwl.xs.size(); ++i) { if (model.pwl.xs[i] > left_r + 1e-12) { right_r = model.pwl.xs[i]; right_d = model.pwl.ys[i]; break; } } if (right_r > 0 && std::abs(right_r - left_r) > 1e-12) { model.low_linear.valid = true; model.low_linear.a = (right_d - left_d) / (right_r - left_r); model.low_linear.b = left_d - model.low_linear.a * left_r; model.has_low_linear = true; model.low_lo = left_r; model.low_hi = right_r; } } } static inline bool buildStandaloneModel(const nlohmann::json &j, RatioLib lib, LibModel &model, LibMeta &meta) { if (!j.is_object() || !j.contains("points")) return false; model = LibModel{}; meta = LibMeta{}; std::vector xs, ys; if (!parsePoints(j["points"], xs, ys)) return false; model.pwl = PWLModel::fromPoints(xs, ys); parseMeta(j, meta); inferBoundsFromPoints(model, meta); if (lib == RatioLib::GIO) { buildGioBypassAndLowLinear(j, model); if (!model.fallback.valid && model.pwl.valid) { std::vector fx, fy; for (size_t i = 0; i < model.pwl.xs.size(); ++i) { double r = model.pwl.xs[i], d = model.pwl.ys[i]; if (!model.has_bypass || r > model.bypass_ratio + 1e-12) { fx.push_back(r); fy.push_back(d); } } if (fx.size() >= 2) model.fallback = fitLinear(fx, fy); } } else if (!model.fallback.valid && model.pwl.valid) { model.fallback = fitLinear(model.pwl.xs, model.pwl.ys); } addDelayOffset(model, parseOffsetNs(j)); return modelUsable(model, lib); } static inline bool buildScopedModel(const nlohmann::json &j, RatioLib lib, const LibModel &base_model, const LibMeta &base_meta, LibModel &model, LibMeta &meta) { if (!j.is_object()) return false; if (j.contains("points")) return buildStandaloneModel(j, lib, model, meta); if (j.contains("inherit") || j.contains("offset_ns") || j.contains("delay_offset_ns") || j.contains("add_delay_ns")) { model = base_model; meta = base_meta; addDelayOffset(model, parseOffsetNs(j)); parseMeta(j, meta); return modelUsable(model, lib); } return false; } inline void initDefaultScopeModels() { auto &base = scoped_models[scopeIndex(DelayScope::Default)]; base.has_gio = true; base.has_mgt = true; base.gio = gio; base.mgt = mgt; base.gio_meta = gio_meta; base.mgt_meta = mgt_meta; } inline bool parseScopeModels(const nlohmann::json &j) { initDefaultScopeModels(); if (!j.contains("scopes")) return true; if (!j["scopes"].is_object()) return false; for (auto it = j["scopes"].begin(); it != j["scopes"].end(); ++it) { auto scope_opt = parseScopeName(it.key()); if (!scope_opt.has_value()) return false; const int idx = scopeIndex(*scope_opt); const auto &sj = it.value(); if (!sj.is_object()) return false; ScopedLibModels scoped; if (sj.contains("gio")) { if (!buildScopedModel(sj["gio"], RatioLib::GIO, gio, gio_meta, scoped.gio, scoped.gio_meta)) return false; scoped.has_gio = true; } if (sj.contains("mgt")) { if (!buildScopedModel(sj["mgt"], RatioLib::MGT, mgt, mgt_meta, scoped.mgt, scoped.mgt_meta)) return false; scoped.has_mgt = true; } if (!scoped.has_gio && !scoped.has_mgt) { scoped.gio = gio; scoped.mgt = mgt; scoped.gio_meta = gio_meta; scoped.mgt_meta = mgt_meta; addDelayOffset(scoped.gio, parseOffsetNs(sj)); addDelayOffset(scoped.mgt, parseOffsetNs(sj)); scoped.has_gio = true; scoped.has_mgt = true; } scoped_models[idx] = std::move(scoped); has_scoped_models = true; } return true; } bool buildFromJson(const nlohmann::json &j) { // 1) GIO if (!j.contains("gio") || !j["gio"].is_object()) return false; { const auto &gj = j["gio"]; std::vector xs, ys; if (gj.contains("points")) { if (!parsePoints(gj["points"], xs, ys)) return false; gio.pwl = PWLModel::fromPoints(xs, ys); } parseMeta(gj, gio_meta); // 若未显式给 rmin/rmax,则用 points 自动推断 if (gio.pwl.valid && !gio_meta.has_bounds) { gio_meta.rmin = gio.pwl.xs.front(); gio_meta.rmax = gio.pwl.xs.back(); gio_meta.has_bounds = true; } // bypass 单独挂在 root 或 gio 下都支持 // 同时支持:只给 ratio,不给 delay(delay 从 points/PWL 推导) auto apply_bypass = [&](const nlohmann::json &bj) -> bool { double br = 0.0; std::optional bdopt; if (!parseBypass(bj, br, bdopt)) return false; gio.has_bypass = true; gio.bypass_ratio = br; if (bdopt.has_value()) { // JSON 显式给了 bypass delay:原行为 gio.bypass_delay = *bdopt; return true; } // JSON 只给 ratio:delay 从 points/PWL 推导,避免重复定义 // 注意:这里不要调用 discreteDelay(),因为 has_bypass 已经置 true, // discreteDelay 会优先返回 bypass_delay(尚未赋值,可能是 0)。 if (gio.pwl.valid) { const double tol = 1e-9; auto it = std::lower_bound(gio.pwl.xs.begin(), gio.pwl.xs.end(), br); if (it != gio.pwl.xs.end() && std::abs(*it - br) < tol) { size_t idx = size_t(it - gio.pwl.xs.begin()); gio.bypass_delay = gio.pwl.ys[idx]; return true; } // 找不到离散点就用 PWL 估值 gio.bypass_delay = gio.pwl.eval(br); return true; } // 3) 再否则:没法推导 return false; }; bool bypass_specified = false; if (j.contains("bypass")) { bypass_specified = true; if (!apply_bypass(j["bypass"])) return false; } else if (gj.contains("bypass")) { bypass_specified = true; if (!apply_bypass(gj["bypass"])) return false; } // 如果 JSON 没写 bypass:默认把 points 的最小点当 bypass if (!bypass_specified) { if (gio.pwl.valid && gio.pwl.xs.size() >= 1) { gio.has_bypass = true; gio.bypass_ratio = gio.pwl.xs.front(); gio.bypass_delay = gio.pwl.ys.front(); } } // 自动构造 GIO 低段线性(bypass -> 第一段 ≥bypass 的点) if (gio.has_bypass && gio.pwl.valid) { // 找到第一个 >= bypass_ratio 的 pwl 点 double left_r = gio.bypass_ratio, left_d = gio.bypass_delay; double right_r = -1, right_d = 0; for (size_t i = 0; i < gio.pwl.xs.size(); ++i) { if (gio.pwl.xs[i] > left_r + 1e-12) { right_r = gio.pwl.xs[i]; right_d = gio.pwl.ys[i]; break; } } if (right_r > 0 && std::abs(right_r - left_r) > 1e-12) { gio.low_linear.valid = true; gio.low_linear.a = (right_d - left_d) / (right_r - left_r); gio.low_linear.b = left_d - gio.low_linear.a * left_r; gio.has_low_linear = true; gio.low_lo = left_r; gio.low_hi = right_r; // 例如 [1,8] } } // 线性回归 fallback(建议:GIO 用 r>=8 的点做回归,避免把直连混进去) if (!gio.fallback.valid && gio.pwl.valid) { std::vector fx, fy; for (size_t i = 0; i < gio.pwl.xs.size(); ++i) { double r = gio.pwl.xs[i], d = gio.pwl.ys[i]; if (!gio.has_bypass || r > gio.bypass_ratio + 1e-12) { fx.push_back(r); fy.push_back(d); } } if (fx.size() >= 2) gio.fallback = fitLinear(fx, fy); } } // 2) MGT if (!j.contains("mgt") || !j["mgt"].is_object()) return false; { const auto &mj = j["mgt"]; std::vector xs, ys; if (mj.contains("points")) { if (!parsePoints(mj["points"], xs, ys)) return false; mgt.pwl = PWLModel::fromPoints(xs, ys); } parseMeta(mj, mgt_meta); // 若未显式给 rmin/rmax,则用 points 自动推断 if (mgt.pwl.valid && !mgt_meta.has_bounds) { mgt_meta.rmin = mgt.pwl.xs.front(); mgt_meta.rmax = mgt.pwl.xs.back(); mgt_meta.has_bounds = true; } if (!mgt.fallback.valid && mgt.pwl.valid) { mgt.fallback = fitLinear(mgt.pwl.xs, mgt.pwl.ys); } } // 最少保证能评估 bool gio_ok = gio.pwl.valid || gio.fallback.valid || gio.has_low_linear || gio.has_bypass; bool mgt_ok = mgt.pwl.valid || mgt.fallback.valid; if (!(gio_ok && mgt_ok)) return false; return parseScopeModels(j); } inline double delayGio(double r) const { return gio.delay(r); } inline double delayMgt(double r) const { return mgt.delay(r); } inline double slopeGio(double r) const { return gio.slope(r); } inline double slopeMgt(double r) const { return mgt.slope(r); } inline double clampGio(double r) const { return std::max(gio_meta.rmin, std::min(r, gio_meta.rmax)); } inline double clampMgt(double r) const { return std::max(mgt_meta.rmin, std::min(r, mgt_meta.rmax)); } inline double delay(RatioLib lib, double r) const { return (lib == RatioLib::GIO) ? delayGio(r) : delayMgt(r); } inline double slope(RatioLib lib, double r) const { return (lib == RatioLib::GIO) ? slopeGio(r) : slopeMgt(r); } inline const LibModel &modelFor(DelayScope scope, RatioLib lib) const { const int idx = scopeIndex(scope); if (idx >= 0 && idx < kDelayScopeCount) { const auto &scoped = scoped_models[idx]; if (lib == RatioLib::GIO && scoped.has_gio) return scoped.gio; if (lib == RatioLib::MGT && scoped.has_mgt) return scoped.mgt; } return (lib == RatioLib::GIO) ? gio : mgt; } inline const LibMeta &metaFor(DelayScope scope, RatioLib lib) const { const int idx = scopeIndex(scope); if (idx >= 0 && idx < kDelayScopeCount) { const auto &scoped = scoped_models[idx]; if (lib == RatioLib::GIO && scoped.has_gio) return scoped.gio_meta; if (lib == RatioLib::MGT && scoped.has_mgt) return scoped.mgt_meta; } return (lib == RatioLib::GIO) ? gio_meta : mgt_meta; } inline double delay(DelayScope scope, RatioLib lib, double r) const { return modelFor(scope, lib).delay(r); } inline double slope(DelayScope scope, RatioLib lib, double r) const { return modelFor(scope, lib).slope(r); } inline double delayGio(DelayScope scope, double r) const { return delay(scope, RatioLib::GIO, r); } inline double delayMgt(DelayScope scope, double r) const { return delay(scope, RatioLib::MGT, r); } inline double slopeGio(DelayScope scope, double r) const { return slope(scope, RatioLib::GIO, r); } inline double slopeMgt(DelayScope scope, double r) const { return slope(scope, RatioLib::MGT, r); } inline double minRatio(RatioLib lib) const { return (lib == RatioLib::GIO) ? gio_meta.rmin : mgt_meta.rmin; } inline double maxRatio(RatioLib lib) const { return (lib == RatioLib::GIO) ? gio_meta.rmax : mgt_meta.rmax; } inline double minRatio(DelayScope scope, RatioLib lib) const { return metaFor(scope, lib).rmin; } inline double maxRatio(DelayScope scope, RatioLib lib) const { return metaFor(scope, lib).rmax; } inline std::vector choices(RatioLib lib) const { const LibMeta &meta = (lib == RatioLib::GIO) ? gio_meta : mgt_meta; const LibModel &mod = (lib == RatioLib::GIO) ? gio : mgt; if (!meta.choices.empty()) return meta.choices; // 没给 choices,则根据 PWL 的 xs 自动生成(并加入 bypass 点) std::vector out; if (mod.pwl.valid) { out.reserve(mod.pwl.xs.size() + 1); for (double r : mod.pwl.xs) out.push_back((int)std::llround(r)); } if (mod.has_bypass) out.push_back((int)std::llround(mod.bypass_ratio)); // 去重、排序、移除非正 std::sort(out.begin(), out.end()); out.erase(std::unique(out.begin(), out.end()), out.end()); out.erase( std::remove_if(out.begin(), out.end(), [](int x) { return x <= 0; }), out.end()); return out; } inline std::vector choices(DelayScope scope, RatioLib lib) const { const LibMeta &meta = metaFor(scope, lib); const LibModel &mod = modelFor(scope, lib); if (!meta.choices.empty()) return meta.choices; std::vector out; if (mod.pwl.valid) { out.reserve(mod.pwl.xs.size() + 1); for (double r : mod.pwl.xs) out.push_back((int)std::llround(r)); } if (mod.has_bypass) out.push_back((int)std::llround(mod.bypass_ratio)); std::sort(out.begin(), out.end()); out.erase(std::unique(out.begin(), out.end()), out.end()); out.erase( std::remove_if(out.begin(), out.end(), [](int x) { return x <= 0; }), out.end()); return out; } inline bool hasChoice(RatioLib lib, int r) const { auto cs = choices(lib); return std::find(cs.begin(), cs.end(), r) != cs.end(); } inline bool hasChoice(DelayScope scope, RatioLib lib, int r) const { auto cs = choices(scope, lib); return std::find(cs.begin(), cs.end(), r) != cs.end(); } inline std::optional discreteDelay(RatioLib lib, double r) const { return discreteDelay(DelayScope::Default, lib, r); } inline std::optional discreteDelay(DelayScope scope, RatioLib lib, double r) const { const LibModel &mod = modelFor(scope, lib); const double tol = 1e-9; // 先查 points(更客观) if (mod.pwl.valid) { auto it = std::lower_bound(mod.pwl.xs.begin(), mod.pwl.xs.end(), r); if (it != mod.pwl.xs.end() && std::abs(*it - r) < tol) { size_t idx = size_t(it - mod.pwl.xs.begin()); return mod.pwl.ys[idx]; } } // 再查 bypass(避免覆盖 points 的同 ratio) if (mod.has_bypass && std::abs(r - mod.bypass_ratio) < tol) return mod.bypass_delay; return std::nullopt; } };