fix(perf): 公式映射导致确认/适应视图/拖拽排序/组复选框四处卡死根治
四次卡顿是**同一个根因**:公式信号(不保序)的幅值范围与包络带各写了一份 「逐点扫描整条信号」,且都没有缓存。2580 万采样时实测 1589ms + 1481ms, 而幅值范围指纹包含公式文本 / 信号顺序 / 可见性 / lane.autoRange ⇒ 确认编辑、适应视图、拖拽排序、组复选框每一个都会让它失效、每次都重付 3.1 秒。 修法: - 新增 DataSet::FormulaTransformCache / FormulaTransformFor:一次扫描同时算出 绘制值域与逐分组包络,缓存到数据层(std::deque,指针稳定)。 - TabConfig::ComputeLaneRange 的公式分支改为查该缓存(保留非法公式的逐点回退)。 - PlotView::AcquireFormulaEnvelope 改为直接复用数据层同一条目, 删除 PlotView 自有的 FormulaEnvelopeEntry / m_formulaEnvelopes 与两个重复常量。 - FormulaEval 新增 FormulaProgram(编译一次、批量求值):always_inline、 常量折叠(2/1.25 => 1.6)、幂次改写(x^2 => x*x、x^3、x^0.5 => sqrt)、 按分组两级循环(去掉每点一次整数除法)。 - EvaluateFormula 复用 thread_local FormulaProgram(按内容缓存),两条路径语义一致。 实测(2580 万采样,纯控制台探针):确认公式 3070ms => 379ms;改 x^2 1.9s => 0.19s; 适应视图 / 拖拽排序 / 组复选框 1589ms => 0.0015ms(命中缓存)。 回归:缓存命中/失效/值域、14 组公式语义与逐位一致性、非法/空公式回退全部 PASS; release/debug 零警告;Release TargetCheck.exe 890,880 B(< 1MB)。 未做:跨组移动共享时间轴仍深拷贝 197MB(55-66ms,值语义决定,风险大于收益); 首次 379ms 仍同步阻塞(后台化的复杂度暂不划算)。
This commit is contained in:
1 parent
8b9c1e3de3
commit
266ab657be
9 files changed
+725
-259
No files matched your search
@@ -946,28 +946,44 @@ namespace jdi
|
||||
{
|
||||
if (style->HasFormula())
|
||||
{
|
||||
// ⚠️ 「公式映射」不保证单调(`x*x` / `1/x`)⇒ 端点映射会算错,
|
||||
// 只能逐点扫描(只在自适应重算时发生,不是每帧路径)。
|
||||
double formulaLow = std::numeric_limits<double>::infinity();
|
||||
double formulaHigh = -std::numeric_limits<double>::infinity();
|
||||
for (const double raw : signal->values)
|
||||
// ⚠️⚠️ 「公式映射」不保证单调(`x*x` / `1/x`)⇒ 端点映射会算错。
|
||||
// 逐点扫描整条信号(2580 万采样 ≈ 1.6 秒)**必须**由数据层的
|
||||
// `FormulaTransformFor` 缓存承担 —— 它在第一次建立时同时算出
|
||||
// 「绘制值域」与「逐分组包络」,之后(改视界 / 缩放 / 适应视图 /
|
||||
// 拖拽排序 / 勾选组)全部是 O(1)。旧实现在这里逐点重扫,导致
|
||||
// 「确认公式」「适应视图」「拖拽信号」「组复选框」都整个应用卡死
|
||||
//(用户 2026-11-04 报告)。
|
||||
const DataSet::FormulaTransformCache* transform =
|
||||
dataSet.FormulaTransformFor(*signal, *style);
|
||||
if (transform != nullptr && transform->ready)
|
||||
{
|
||||
if (!std::isfinite(raw))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const double mapped = ApplyDrawTransform(*style, raw);
|
||||
if (!std::isfinite(mapped))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
formulaLow = std::min(formulaLow, mapped);
|
||||
formulaHigh = std::max(formulaHigh, mapped);
|
||||
signalLow = transform->low;
|
||||
signalHigh = transform->high;
|
||||
}
|
||||
if (std::isfinite(formulaLow) && std::isfinite(formulaHigh))
|
||||
else
|
||||
{
|
||||
signalLow = formulaLow;
|
||||
signalHigh = formulaHigh;
|
||||
// 回退(公式非法 / 缓存不可用):保持原逐点语义,绝不改变显示结果。
|
||||
double formulaLow = std::numeric_limits<double>::infinity();
|
||||
double formulaHigh = -std::numeric_limits<double>::infinity();
|
||||
for (const double raw : signal->values)
|
||||
{
|
||||
if (!std::isfinite(raw))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const double mapped = ApplyDrawTransform(*style, raw);
|
||||
if (!std::isfinite(mapped))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
formulaLow = std::min(formulaLow, mapped);
|
||||
formulaHigh = std::max(formulaHigh, mapped);
|
||||
}
|
||||
if (std::isfinite(formulaLow) && std::isfinite(formulaHigh))
|
||||
{
|
||||
signalLow = formulaLow;
|
||||
signalHigh = formulaHigh;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
Reference in new issue
Block a user