From cef894376cb136d34f32bf17d3e80d95cf5130db Mon Sep 17 00:00:00 2001 From: jack Date: Fri, 28 Aug 2026 16:19:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9A=E4=BD=8D=20inner=5Fms=20=E7=9A=84=203?= =?UTF-8?q?.3=20=E5=80=8D=EF=BC=9A=E6=98=AF=E4=BA=89=E6=8A=A2=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=E7=AE=97=E6=B3=95=E6=A1=A3=EF=BC=8C=E6=92=A4=E5=9B=9E?= =?UTF-8?q?=2022/86=20=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 服务端跑 probe_inner.py 与本机对表:分档占比一致(TF_DF 两腿 68~74%, 信号链 16~21ms),此前报的「chan 构建 22ms / 信号链 86ms」是无效减法 ——孤立环境的 1m append 减在场十币的 inner_ms,还漏了 5m 腿。 真实构成:inner_ms 100ms = 约 30ms 计算 × 3.3 倍争抢。逐个排除 _rebuild(1.6ms)、币种差异、周期重建(21.5%/1.5x)、批内次序(相关-0.083) 后,用外生的到达密集度确认(67→117ms),并以注入合成负载复现 (2 个满载进程 2.68x,3 个 2.86x,在场实测 2.24~3.34x)。 心跳判定不再推荐「继续压算法」,改为加核或减币。 第一版用 t_signal-inner_ms 反推并发区间得相关 0.533,是循环构造, 已废弃并在 §5.72 记下这个坑。 Co-authored-by: Cursor --- research/HANDOFF.md | 74 ++++++++++++++++++++++++++++++++++++-- research/live/shadow_hb.py | 15 +++++--- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/research/HANDOFF.md b/research/HANDOFF.md index 4fb273f..c58d27b 100644 --- a/research/HANDOFF.md +++ b/research/HANDOFF.md @@ -1581,9 +1581,14 @@ python step46_engine_parity.py --check --rows 200000 --out step46_baseline_big.j **十币 / 2 核的效果:** 清空 560→247ms,排队 92→10ms,纯计算 219→108ms, 内存 597→598MiB(20 条缓存流的开销在噪声里)。 -**瓶颈已经换位置了:** `inner_ms` 108ms 里 chan 构建只占约 22ms,其余 86ms -是 `build_htf_zones` / `find_fast_bsp3` / `attach_htf_context`。再压 chan -构建收益有限,要继续压得看下游那三个。 +~~**瓶颈已经换位置了:** `inner_ms` 108ms 里 chan 构建只占约 22ms,其余 86ms +是 `build_htf_zones` / `find_fast_bsp3` / `attach_htf_context`。~~ + +⚠️ **上面这段是错的,2026-08-28 撤回,见 §5.72。** 那个 86ms 不是测出来的, +是减出来的:22ms 是孤立环境下 1m 腿的 `append_bar`,108ms 是跑着的十币采集器 +里的 `inner_ms`。一个无争抢一个有争抢,还漏了 5m 腿,这个减法不成立。 +实测信号链合计只有 16~21ms,`build_htf_zones` 是其中最大的一项(13ms), +`find_fast_bsp3` / attach 合计不到 2%。 #### 5.71 第二轮引擎优化(2026-08-28,服务端反馈驱动) @@ -1635,6 +1640,69 @@ EMA/MACD/ATR 是递推的、BB/SMA/量比是窗口的,理论上都能 O(1) 更 **曾怀疑是 payload 反序列化,实测 `_rebuild` 只有 1.0ms,假设不成立。** 两边跑同一个探针对分档表,才能定位那 86ms。 +**→ 已定位,见 §5.72:那 86ms 不存在,是服务端的减法错了。分档两边一致。** + +#### 5.72 那 86ms 不存在:`inner_ms` 的 3.3 倍是争抢,不是某个算法档 + +服务端跑了同一个 `probe_inner.py`(2 核,采集器十币在跑)。**分档占比两边 +对得上,四倍差距是伪命题:** + +| | 本机 | 服务端 | +|---|---|---| +| `TF_DF` 两条腿 | 70% | **68~74%** | +| `build_htf_zones` | 13% | 10~12% | +| `htf_fx_timeline` | 6% | 5% | +| `find_fast_bsp3`+ladder+attach | <2% | **<2%** | +| 信号链合计 | — | **16~21ms**(不是 86ms) | + +服务端那个 22/86 是无效减法(见 §5.7 的撤回批注)。 + +**但对完表冒出一个真问题:** 探针里全量 `TF_DF` 合计 106~126ms,而采集器走 +增量报的 `inner_ms` 也是 100ms 中位——增量该快得多,两个数却一样。差额去哪了? +排查顺序与结论: + +| 假设 | 实测 | 判定 | +|---|---|---| +| `_rebuild` 反序列化贵 | 两腿合计 **1.6ms** | ✗ | +| 某些币结构复杂 | 十币中位 92~105ms,几乎一致 | ✗ | +| 周期性全量重建拉高中位 | 占 21.5%,且只贵 1.5x(133 vs 91ms) | 部分 | +| 批内后段被拖慢 | `inner_ms` 对批内次序相关 **−0.083**,是平的 | ✗ | +| **CPU 争抢** | 见下 | **✓ 主因** | + +「批内平坦」一度被我读成争抢的反证,其实反了:**两个 worker 在整段突发期都 +满载时,批内每个币被拖慢的程度一样,平坦正是均匀争抢的表现。** + +用外生指标(到达密集度,仅由 `t_data_ms` 决定,不含 `inner_ms`): + +| ±250ms 内到达币数 | 1 | 2 | 4 | 6 | 8 | +|---|---|---|---|---|---| +| `inner_ms` 中位 | 67ms | 80ms | 94ms | 105ms | 117ms | + +⚠️ **第一版用 `beg = t_signal_ms − inner_ms` 反推并发区间,得到相关 0.533, +这个数是废的**——`inner_ms` 越大区间越宽、重叠自然越多,相关性有一部分是构造 +出来的。换外生指标后相关降到 0.271。**凡是用被解释变量去构造解释变量的,先 +怀疑循环。** + +注入合成负载复现(离线同一窗口,2 核): + +| 背景 | `compute` 增量 | 放大 | +|---|---|---| +| 空闲 | **29.9ms** | 1.00x | +| 1 个满载进程 | 41.7ms | 1.39x | +| 2 个满载进程 | 80.2ms | 2.68x | +| 3 个满载进程 | 85.4ms | 2.86x | + +采集器实测:密集度=1 时 67ms(2.24x)、总体中位 100ms(3.34x)。落在「1~2 个 +满载进程」到「2 个以上」之间,正是 **2 核上跑 主进程 + 2 worker** 该有的样子 +(主进程常驻负载不小:10 路 K 线 WS + 10 路盘口 + 10Hz 采样)。 + +**结论与代价:** `inner_ms` 100ms = **约 30ms 真实计算 × 3.3 倍争抢**。 +剩下的延迟不是算法性质的,继续压引擎收益有限(把 30ms 砍一半只换回 15ms×3.3 +≈ 50ms,且越往下越难)。真正的杠杆是**核数**或**减币**。 + +⚠️ 这和 §5.7 心跳里「币数 > 核数时加 worker 无用」不矛盾但要分清:**加 worker +确实无用(worker 已等于核数),加核有用。** 两者在原判定里被混成一句了。 + --- ## 6. 接下来要做的事(按优先级) diff --git a/research/live/shadow_hb.py b/research/live/shadow_hb.py index 55e9cd7..1f1a6a7 100644 --- a/research/live/shadow_hb.py +++ b/research/live/shadow_hb.py @@ -856,7 +856,9 @@ class Shadow: 144ms 抬到 192ms,反而超过 queue 135ms,于是判定落到「量级已低、 无需优化」,而此时最后一个币已经落在 1376ms。 - 所以币数超过核数时,唯一的出路是压单币耗时(增量计算),不是加 worker。 + 所以币数超过核数时,加 worker 不增吞吐。出路有两级:先上增量把真实计算 + 压下来;增量之后剩的是争抢放大(实测 3.3 倍,§5.72),那一级只能加核或 + 减币,继续改算法收益有限。 """ if not np.isfinite(clear): return "样本不足,暂不判定" @@ -872,10 +874,13 @@ class Shadow: if not INCR_ON: return (f"币数 {len(SYMS)} > 核数 {CORES},加 worker 无用(CPU 密集)" f"。压单币耗时 → 开 SHADOW_INCR=1 走增量(实测 3.56x)") - return (f"币数 {len(SYMS)} > 核数 {CORES},且增量已生效。单币 {i:.0f}ms " - f"里 chan 构建仅约 22ms,其余是 build_htf_zones / " - f"find_fast_bsp3 / attach_htf_context —— 要继续压得改这三个," - f"或减币") + # 增量已生效时,单币耗时的大头不再是算法而是争抢:离线同一窗口 30ms, + # 在场 100ms,注入合成负载能复现这个倍数(HANDOFF §5.72)。所以这里不能 + # 再推荐「继续改算法」——那是按 3.3 倍虚高的数去优化 30ms 的真实计算 + return (f"币数 {len(SYMS)} > 核数 {CORES},增量已生效,加 worker 无用" + f"(worker 已等于核数)。单币 {i:.0f}ms 里真实计算约 30ms," + f"其余是 {CORES} 核上争抢的放大 —— 杠杆是加核或减币," + f"不是继续压算法") async def run(self) -> None: await self.start()