Prepare Frontier code trace fidelity campaign

This commit is contained in:
2026-07-23 23:48:28 +08:00
parent cd7665d882
commit fbaa909723
5 changed files with 877 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
# Frontier code-trace campaign handoff
本目录已经准备好无 GPU 的 data preflight、512→16 参数化映射、prefill-only 转换和 `max_model_len` 显式适配。当前没有启动或探测 `dash1``dash4`
完整设计与 gate 见 [`experiment-card.md`](experiment-card.md)。
## 当前已知阻塞
本机 `/home/gahow/ali-trace/trace-glm5.1-formatted/` 不存在。仓库历史记录的远端路径是:
```text
/home/admin/cpfs/wjh/ali-trace/trace-glm5.1-formatted/
```
有机器后先确认用户给出的 `~/ali-trace/...` 是否解析到同一目录,再运行以下命令。
## 1. 审计所有 1h+ code source
在持有 trace 的机器、repo 根目录执行:
```bash
python3 runs/frontier-code-trace-v0/audit_code_trace.py \
--trace-root ~/ali-trace/trace-glm5.1-formatted \
--output runs/frontier-code-trace-v0/inputs/code-audit.json
```
如果目录里混有非 request JSONL先只读列举文件再用多个 `--source` 显式指定。审计输出必须满足:
```text
data_gate = PASS
selected.hash_contract.exact_source_block_size != null
max_model_len_recommendation != null
selected.selected_window_stats.max_model_len_coverage[推荐值].coverage = 1.0
```
旧记录预计 source block size 为 512、推荐 max model len 为 131072但禁止把这两个值写死为实验事实。
## 2. 物化稳定窗口
```bash
python3 runs/frontier-code-trace-v0/prepare_code_window.py \
--audit runs/frontier-code-trace-v0/inputs/code-audit.json \
--output-root runs/frontier-code-trace-v0/inputs/code-window
```
输出是 6075min `code-raw-window.jsonl` 和 manifest。source 文件不修改。
## 3. 生成 P+D paired trace
若没有 prompt sidecar先生成 shape/prefix-faithful synthetic prompts
```bash
python3 runs/frontier-s3-real-v0/remap_hash_blocks.py \
--input runs/frontier-code-trace-v0/inputs/code-window/code-raw-window.jsonl \
--output-root runs/frontier-code-trace-v0/inputs/code-pd-rho-max \
--source-block-size 512 \
--workload-mode prefill_decode \
--rho 1.0 \
--max-total-tokens 131072 \
--validate-parents
```
命令中的 `512``131072` 必须替换为 audit manifest 值。若存在对齐 prompt sidecar`--prompt``--tokenizer`,并要求 synthetic fallback 为 0。
正式 rho 不能直接用 1.0;先从最大 remap cache 按 session-coherent `sampling_u` 过滤,分别标定 low/mid/near-knee。
## 4. 生成 prefill-only paired trace
对 chat/code 使用同一个转换接口:
```bash
python3 runs/frontier-s3-real-v0/remap_hash_blocks.py \
--input INPUT_WINDOW.jsonl \
--output-root OUTPUT_ROOT \
--source-block-size SOURCE_BLOCK_SIZE \
--workload-mode prefill_only \
--rho RHO \
--max-total-tokens MAX_MODEL_LEN \
--validate-parents
```
该模式会同时把 Frontier `num_decode_tokens`、real request `min/max_tokens` 和 remapped row 的 `output_length` 固定为 1。
## 5. max-model-len 真机 gate
现有 real runner 新增了三个显式环境变量chat 默认行为不变:
```bash
MAX_MODEL_LEN=131072 \
TRACE_INPUT_ROOT=/absolute/path/to/materialized/code-cell \
ALLOW_SYNTHETIC_PROMPTS=true \
OUTPUT_ROOT=/absolute/path/to/new/output \
bash runs/frontier-s3-real-v0/run_full_real.sh RHO_LABEL tp4_mns16 1 PORT
```
- `MAX_MODEL_LEN` 必须等于 manifest 推荐值;
- `TRACE_INPUT_ROOT` 内必须有 `real_requests.jsonl``manifest.json`
- synthetic prompt 默认拒绝,只有在 experiment card 明确降级 claim 后才设为 `true`
- runner 会在启动前扫描 paired requests若任何 `ISL+OSL` 超 cap 立即失败。
正式 full job 前,先按 experiment card 的 G4 补 32k128k attention profile再做 TP4→TP2 的 p50/p99/max 单请求与 5min canary。
## 6. decode-only
当前 materializer 故意不提供 `decode_only` 选项。严格 decode-only 需要 initial-KV state而不是把 prompt 改短。只有 real `DecodeBenchConnector`(或等价能力)与 Frontier initial-KV contract 都通过 G7 后,才创建 decode-only jobs。
## 本地验证
```bash
python3 -m unittest -v \
runs/frontier-code-trace-v0/test_code_trace_preflight.py \
runs/frontier-s3-real-v0/test_remap_hash_blocks.py \
runs/frontier-s3-real-v0/test_select_chat_window.py
python3 -m py_compile \
runs/frontier-code-trace-v0/*.py \
runs/frontier-s3-real-v0/*.py
bash -n runs/frontier-s3-real-v0/run_full_real.sh
```

View File

@@ -0,0 +1,361 @@
#!/usr/bin/env python3
"""Audit long code traces before choosing a replay window and max model length."""
from __future__ import annotations
import argparse
import json
import math
import statistics
from collections import Counter
from pathlib import Path
from typing import Any, Iterable, Sequence
BLOCK_SIZE_CANDIDATES = (16, 32, 64, 128, 256, 512, 1024)
MAX_MODEL_LEN_CANDIDATES = (40960, 65536, 98304, 131072, 262144)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--trace-root", type=Path)
parser.add_argument("--source", type=Path, action="append")
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--min-minutes", type=int, default=60)
parser.add_argument("--max-minutes", type=int, default=75)
parser.add_argument("--bin-seconds", type=int, default=60)
parser.add_argument("--max-acceptable-gap-s", type=float, default=5.0)
parser.add_argument("--model-position-limit", type=int, default=262144)
return parser.parse_args()
def percentile(values: Sequence[int | float], fraction: float) -> float | None:
if not values:
return None
ordered = sorted(float(value) for value in values)
position = (len(ordered) - 1) * fraction
lower = math.floor(position)
upper = math.ceil(position)
if lower == upper:
return ordered[lower]
return ordered[lower] * (upper - position) + ordered[upper] * (position - lower)
def distribution(values: Sequence[int | float]) -> dict[str, int | float | None]:
return {
"count": len(values),
"min": min(values) if values else None,
"p50": percentile(values, 0.50),
"p90": percentile(values, 0.90),
"p95": percentile(values, 0.95),
"p99": percentile(values, 0.99),
"max": max(values) if values else None,
"mean": statistics.fmean(values) if values else None,
}
def parse_hash_ids(value: Any) -> list[Any]:
if isinstance(value, list):
return value
if isinstance(value, str):
stripped = value.strip()
if not stripped:
return []
if stripped.startswith("["):
decoded = json.loads(stripped)
if not isinstance(decoded, list):
raise ValueError("hash_ids JSON must decode to a list")
return decoded
delimiter = "|" if "|" in stripped else ","
return [part for part in stripped.split(delimiter) if part.strip()]
if value is None:
return []
return [value]
def iter_jsonl(path: Path) -> Iterable[tuple[int, dict[str, Any]]]:
with path.open() as stream:
for line_number, line in enumerate(stream, 1):
if not line.strip():
continue
row = json.loads(line)
if not isinstance(row, dict):
raise ValueError(f"{path}:{line_number}: row must be an object")
yield line_number, row
def choose_window(
*,
counts: Sequence[int],
max_gaps: Sequence[float],
first_timestamp: float,
min_minutes: int,
max_minutes: int,
bin_seconds: int,
max_acceptable_gap_s: float,
) -> dict[str, Any] | None:
candidates = []
for minutes in range(max_minutes, min_minutes - 1, -1):
bins = math.ceil(minutes * 60 / bin_seconds)
for start_bin in range(0, len(counts) - bins + 1):
selected = counts[start_bin : start_bin + bins]
mean = statistics.fmean(selected)
cv = statistics.pstdev(selected) / mean if mean else math.inf
max_gap = max(max_gaps[start_bin : start_bin + bins], default=0.0)
candidates.append(
{
"_score": (
max_gap > max_acceptable_gap_s,
cv,
max_gap,
-minutes,
start_bin,
),
"start_bin": start_bin,
"minutes": minutes,
"count_mean_per_bin": mean,
"count_cv": cv,
"count_min_per_bin": min(selected),
"count_max_per_bin": max(selected),
"max_gap_s": max_gap,
}
)
if not candidates:
return None
chosen = min(candidates, key=lambda item: item["_score"])
chosen.pop("_score")
chosen["start_timestamp"] = first_timestamp + chosen["start_bin"] * bin_seconds
chosen["end_timestamp"] = chosen["start_timestamp"] + chosen["minutes"] * 60
return chosen
def scan_source(path: Path, args: argparse.Namespace) -> dict[str, Any]:
rows = 0
first_timestamp = None
last_timestamp = None
previous_timestamp = None
counts: Counter[int] = Counter()
max_gaps: dict[int, float] = {}
input_lengths: list[int] = []
output_lengths: list[int] = []
total_lengths: list[int] = []
hash_rows = 0
hash_matches = Counter()
prompt_rows = 0
sampling_rows = 0
schema_keys: Counter[str] = Counter()
for line_number, row in iter_jsonl(path):
missing = [
key
for key in ("timestamp", "input_length", "output_length")
if key not in row
]
if missing:
raise ValueError(f"{path}:{line_number}: missing required fields {missing}")
timestamp = float(row["timestamp"])
if first_timestamp is None:
first_timestamp = timestamp
if previous_timestamp is not None and timestamp < previous_timestamp:
raise ValueError(
f"{path}:{line_number}: timestamp {timestamp} < {previous_timestamp}"
)
bin_index = math.floor((timestamp - first_timestamp) / args.bin_seconds)
counts[bin_index] += 1
if previous_timestamp is not None:
previous_bin = math.floor(
(previous_timestamp - first_timestamp) / args.bin_seconds
)
max_gaps[previous_bin] = max(
max_gaps.get(previous_bin, 0.0),
timestamp - previous_timestamp,
)
input_tokens = int(row["input_length"])
output_tokens = max(1, int(row["output_length"]))
if input_tokens <= 0:
raise ValueError(f"{path}:{line_number}: input_length must be positive")
input_lengths.append(input_tokens)
output_lengths.append(output_tokens)
total_lengths.append(input_tokens + output_tokens)
hashes = parse_hash_ids(row.get("hash_ids"))
if hashes:
hash_rows += 1
for block_size in BLOCK_SIZE_CANDIDATES:
if len(hashes) == math.ceil(input_tokens / block_size):
hash_matches[block_size] += 1
prompt_rows += int(
isinstance(row.get("prompt"), (str, list)) and bool(row.get("prompt"))
)
sampling_rows += int("sampling_u" in row)
schema_keys.update(row.keys())
rows += 1
previous_timestamp = timestamp
last_timestamp = timestamp
if not rows or first_timestamp is None or last_timestamp is None:
raise ValueError(f"{path}: empty trace")
total_bins = math.floor((last_timestamp - first_timestamp) / args.bin_seconds) + 1
chosen = choose_window(
counts=[counts[index] for index in range(total_bins)],
max_gaps=[max_gaps.get(index, 0.0) for index in range(total_bins)],
first_timestamp=first_timestamp,
min_minutes=args.min_minutes,
max_minutes=args.max_minutes,
bin_seconds=args.bin_seconds,
max_acceptable_gap_s=args.max_acceptable_gap_s,
)
return {
"source": str(path.resolve()),
"rows": rows,
"first_timestamp": first_timestamp,
"last_timestamp": last_timestamp,
"span_s": last_timestamp - first_timestamp,
"request_rate_per_s": rows / max(last_timestamp - first_timestamp, 1.0),
"input_length": distribution(input_lengths),
"output_length": distribution(output_lengths),
"total_length": distribution(total_lengths),
"over_max_model_len": {
str(limit): {
"requests": sum(value > limit for value in total_lengths),
"fraction": sum(value > limit for value in total_lengths) / rows,
}
for limit in MAX_MODEL_LEN_CANDIDATES
},
"hash_contract": {
"rows_with_hash_ids": hash_rows,
"candidate_exact_match_rows": {
str(size): hash_matches[size] for size in BLOCK_SIZE_CANDIDATES
},
"exact_source_block_size": next(
(
size
for size in BLOCK_SIZE_CANDIDATES
if hash_rows and hash_matches[size] == hash_rows
),
None,
),
},
"prompt_rows": prompt_rows,
"sampling_u_rows": sampling_rows,
"schema_field_counts": dict(sorted(schema_keys.items())),
"stable_window": chosen,
}
def scan_window(source: Path, window: dict[str, Any]) -> dict[str, Any]:
start = float(window["start_timestamp"])
end = float(window["end_timestamp"])
inputs: list[int] = []
outputs: list[int] = []
totals: list[int] = []
for _, row in iter_jsonl(source):
timestamp = float(row["timestamp"])
if timestamp < start:
continue
if timestamp >= end:
break
input_tokens = int(row["input_length"])
output_tokens = max(1, int(row["output_length"]))
inputs.append(input_tokens)
outputs.append(output_tokens)
totals.append(input_tokens + output_tokens)
return {
"requests": len(totals),
"input_length": distribution(inputs),
"output_length": distribution(outputs),
"total_length": distribution(totals),
"max_model_len_coverage": {
str(limit): {
"covered_requests": sum(value <= limit for value in totals),
"excluded_requests": sum(value > limit for value in totals),
"coverage": sum(value <= limit for value in totals) / len(totals),
}
for limit in MAX_MODEL_LEN_CANDIDATES
},
}
def resolve_sources(args: argparse.Namespace) -> list[Path]:
if args.source:
return [path.resolve() for path in args.source]
if args.trace_root is None:
raise ValueError("provide --trace-root or one or more --source")
sources = sorted(
path.resolve()
for path in args.trace_root.glob("*.jsonl")
if "prompt" not in path.stem.lower()
)
if not sources:
raise FileNotFoundError(f"no non-prompt JSONL files under {args.trace_root}")
return sources
def main() -> None:
args = parse_args()
if not 0 < args.min_minutes <= args.max_minutes:
raise ValueError("require 0 < min_minutes <= max_minutes")
sources = resolve_sources(args)
files = [scan_source(path, args) for path in sources]
eligible = [item for item in files if item["stable_window"] is not None]
if not eligible:
chosen = None
data_gate = "BLOCKED_NO_1H_WINDOW"
else:
chosen = min(
eligible,
key=lambda item: (
item["stable_window"]["max_gap_s"] > args.max_acceptable_gap_s,
item["stable_window"]["count_cv"],
-item["stable_window"]["minutes"],
item["source"],
),
)
chosen["selected_window_stats"] = scan_window(
Path(chosen["source"]), chosen["stable_window"]
)
exact_block_size = chosen["hash_contract"]["exact_source_block_size"]
max_total = chosen["selected_window_stats"]["total_length"]["max"]
data_gate = (
"PASS"
if exact_block_size is not None
and max_total is not None
and max_total <= args.model_position_limit
else "BLOCKED_HASH_OR_POSITION_CONTRACT"
)
recommendation = None
if chosen is not None:
maximum = chosen["selected_window_stats"]["total_length"]["max"]
recommendation = next(
(
limit
for limit in MAX_MODEL_LEN_CANDIDATES
if maximum <= limit <= args.model_position_limit
),
None,
)
payload = {
"schema": "frontier-code-trace-audit-v1",
"trace_root": str(args.trace_root.resolve()) if args.trace_root else None,
"sources": [str(path) for path in sources],
"window_policy": {
"min_minutes": args.min_minutes,
"max_minutes": args.max_minutes,
"bin_seconds": args.bin_seconds,
"max_acceptable_gap_s": args.max_acceptable_gap_s,
"selection": "lowest density CV after rejecting anomalous-gap windows",
},
"model_position_limit": args.model_position_limit,
"files": files,
"selected": chosen,
"max_model_len_recommendation": recommendation,
"data_gate": data_gate,
"runtime_gate": (
"PENDING: vLLM startup must prove enough KV blocks and nonzero "
"max concurrency at the recommended max_model_len for each TP"
),
}
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
print(json.dumps({"data_gate": data_gate, "output": str(args.output)}))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,204 @@
# EXP-CODE-TRACE从 chat 1h trace 扩展到 code 与 phase-separated replay
> **状态READY_FOR_DATA PREFLIGHT未启动 GPU。** 当前只完成本地适配与实验冻结;`dash1`--`dash4` 有整机空闲后按本文 gate 顺序推进。禁止使用 `dash0`。
## 目标与成功定义
当前 1h+ 证据只覆盖 Qwen3-30B-A3B 的生产 chat trace、prefill+decodeP+D和亚临界负载。本 campaign 分两步扩展:
1. **主任务:** 使用 `~/ali-trace/trace-glm5.1-formatted/` 中的 1h+ code trace先完成 P+D real-vs-Frontier 回放;
2. **后续 phase matrix** 对 chat/code 都补 prefill-only 和严格 decode-only。
本轮不是只看“能否跑完”。每个正式 cell 必须满足:同一 request vector、同一 arrival、同一 token shape、同一 prefix/initial-KV 合约、real 零失败、无持续 backlog并同时报告 TTFT/TPOT/E2E、queue/batch、KV/prefix state 与 5min 分窗漂移。
## 三种 workload mode 的冻结定义
| Mode | 保留 | 改写 | 主指标 | 明确不声称 |
|---|---|---|---|---|
| P+D | 原 ISL/OSL、arrival、session/prefix | 仅做 source block→16-token runtime block 映射 | TTFT、TPOT、E2E、hit ratio、batch/queue | 不代表 PD 分离 |
| prefill-only | 原 ISL、arrival、session/prefix | OSL 固定为 1real `min_tokens=max_tokens=1`sim decode tokens=1 | TTFT、prefill service/tokens/s、prefix hit、queue | TPOT 不定义1-token decode 只用于完成请求 |
| strict decode-only | 原 OSL、context length、arrival burst | arrival 定义为 **KV-ready time**;请求进入 decode 时已有 ISL 长度的 initial KV | TPOT、decode tokens/s、batch/queue、preemption | 不包含 prefill 与 KV transfer latency不把短 prompt proxy 称为 decode-only |
strict decode-only 必须同时具备:
- realvLLM `DecodeBenchConnector`(或等价、经验证的 initial-KV 注入);
- simFrontier request 在 admission 时已拥有相同长度/块布局的 computed KV
- 两侧都不在 decode critical path 重做 prefill
- arrival 以 KV-ready time 对齐。若只保留原 trace 的相对到达形状,结论限定为 decode engine compute/scheduling fidelity。
在该合约完成前,只允许跑并标注为 **decode-dominant proxy**,不能进入 strict decode-only 结果表。
## 为什么 code P+D 不能直接复用 chat 配置
已知历史探查显示 code trace ISL p90 约 81.9k,约 32.6% 请求超过旧 `40960` 上限;真实数值必须由本 campaign 重新审计。至少有四个独立适配面:
1. **Serving cap** `max_model_len` 必须覆盖 `ISL+OSL`,不能只看 ISL也不能静默丢掉超长请求
2. **KV capacity** Qwen3-30B 模型 position limit 为 262144但 TP1/2/4 在 H20 上是否有足够 KV blocks 是 runtime gate不由 config.json 自动保证;
3. **Prefix block** code source hash 预计为 512-token blockchat harness 原先固定 64→16
4. **Profile support** 当前修复后的 attention profile 只覆盖到约 32k KV context。即使 vLLM 能跑 128kFrontier 对 32k128k 仍会出 profile 支撑域;在补 long-context 网格前只能做诊断 replay不能做 fidelity claim。
## Hypotheses
- **H-code-generalizes** 在补齐 long-context profile 支撑域后code P+D 的 TTFT/TPOT/E2E 分布统计偏差仍处于当前 chat 量级,且 1h 残差不发散。
- **H-longctx-gap** code 的主要新增 gap 来自 32k 以上 KV-context 外推;补到 trace p99/max 对应的网格后TTFT bias 随 ISL 的二次项显著收敛。
- **H-phase-specific** prefill-only 主要暴露 long-context/profile gapstrict decode-only 主要暴露 batch-conditioned whole-layer service 与 scheduler fixed-point gap。二者不能用 P+D 的误差抵消来互相证明准确。
## Preflight gates按顺序任一失败即停止后续真机矩阵
### G0数据位置与 provenance
- 只读列举 `trace-glm5.1-formatted/*.jsonl`,记录文件大小与 SHA256
- 确认至少两个独立日期段:一个作为 development一个 held-out
- 本机当前没有该目录;仓库历史记录的远端位置为
`/home/admin/cpfs/wjh/ali-trace/trace-glm5.1-formatted/`。恢复机器后先确认 `~/ali-trace/...` 是否为同一路径/软链,不能假设。
### G11h window、schema 与 block contract
运行 `audit_code_trace.py`,要求:
- timestamp 单调,存在 6075min 连续稳定窗口;
- `timestamp/input_length/output_length` 全行存在;
- `hash_ids` 数量与某个 source block size 在全行严格满足
`ceil(ISL/source_block_size)`;预计值 512但以审计结果为准
- 记录 ISL/OSL/ISL+OSL 的 p50/p90/p95/p99/max、gap、request rate、prompt/sampling 字段覆盖。
选择窗口后用 `prepare_code_window.py` 物化只读派生文件,并按 session root 生成确定性的 `sampling_u`。另一日期段不参与 rho 与 profile 选择。
### G2`max_model_len` data gate
候选 cap 固定为 `40960/65536/98304/131072/262144`,选能 **100% 覆盖选中窗口 `ISL+OSL`** 的最小值。规则:
- 若 max≤131072主路径使用 131072 或更小的审计推荐值;
- 若存在 >131072 请求,不允许悄悄过滤。优先验证 262144若 runtime 不可行,必须预注册过滤比例,并把 claim 改为“≤131072 子群”;
- Frontier 的 trace max tokens、predictor max tokens/request、vLLM `--max-model-len` 三处使用同一个 manifest 值。
### G3prompt 与 prefix fidelity
优先级:
1. 有对齐 prompt text sidecar用 Qwen tokenizer 重分词,要求 token length 与 trace ISL 全行一致;
2. trace 内已有 prompt text/token IDs同样做长度与 hash relation 检查;
3. 两者都没有:允许用 source hash 确定性展开为 synthetic Qwen token IDs但结果降级为 **length/arrival/prefix-shape faithful**,不声称 prompt-content 或 MoE routing faithful。
不论走哪条路径source→16 映射冲突、runtime identity collision、parent prefix violation 都必须为 0。P+D/prefill-only 两侧 prefix caching 同开;先用 510min TP4/MNS16 做 hit-ratio audit。
### G4long-context profile support
现有 profile-v5 的 KV context 上界约 32k对 code 不足。根据 development window 的 uncached-ISL 分布生成 profile-v6-code-longctx
- full chunk`q8k`context 至少覆盖 32k/48k/64k/80k/96k/112k/120k
- tail chunk从真实 `ISL mod 8192` 的 p50/p90 选择 24k/46k 代表点;
- TP1/2/4 分开采集,复测 `q1ks8k/q8ks32k` anchor
- 每点至少两次 fresh-process repeatCV≤5%anchor drift≤10%
- profile max context 必须 ≥ development window p99正式 max claim 要求 ≥ max。若只覆盖 p99max 以上请求单独列为 out-of-support不进入总体准确度数字。
这是 code P+D 正式 fidelity 的硬 gate。可以先用旧 profile 跑 diagnostic sim 来估 load但不得与真机组成最终 gap。
### G5vLLM max-length/KV runtime gate
对每个候选 topology先 TP4再 TP2TP1 后置):
1. fresh server以 manifest cap 启动;
2. 记录 vLLM 版本、model config、GPU KV blocks、maximum concurrency、启动日志
3. 发 3 个单请求ISL p50、p99、maxOSL=1usage 必须逐 token 对齐;
4. 发 5min sampled P+D canary零 OOM/timeout/preemption storm
5. 只有 maximum concurrency>1 且 canary drain tail≤窗口时长 10% 才进入 rho calibration。
`max_model_len` 变大不等于每个请求都预占最大 KV但会改变启动合法性与可表达的单请求上界实际 KV 压力仍由并发 token state 决定。
### G6每种 mode 独立标定 rho
不能复用 P+D rho
- P+D 同时按 raw/prefix-adjusted prefill tokens/s 与 decode tokens/s 看 knee
- prefill-only 因 OSL=1重新按 prefill work 标定;
- strict decode-only 因无 prefill按 decode tokens/s 和 batch fixed point 标定。
每种 workload×mode 选择 `low/mid/near-knee` 三点;正式点必须亚临界:全请求完成、无持续 backlog、drain tail≤10%、waiting p99 不单调随时间增长。跨 knee 点若运行,只作为 overload boundary不支持“不发散”结论。
### G7strict decode-only capability gate
先在 10min synthetic trace 上验证:
- real connector 确认没有执行 prefill kernel
- Frontier ledger 第一个阶段就是 decodecomputed tokens=ISL
- 相同 context length 下两侧 KV block count 一致;
- connector preload/transfer 时间独立记账,不混入 TPOT
- decode batch telemetry 能覆盖 b1 到目标 batch。
若 vLLM 0.20 community stack 没有等价 connector严格 case 保持 BLOCKED可另跑 decode-dominant proxy但单独命名和汇报。
## 正式实验矩阵与推进顺序
### Phase Acode P+D第一优先级
1. **A0 CPU/data** G0G4
2. **A1 max-len smoke** TP4→TP2TP1 只在 KV gate 通过后加入;
3. **A2 paired 10min canary** TP4/MNS16low rhoreal+sim
4. **A3 calibration** 各 rho 只先跑 sim冻结 low/mid/near-knee
5. **A4 full** TP4/MNS16、TP2/MNS16 × 3 rho × 2 trial × 6075min
6. **A5 held-out** 只在 development window 判据冻结后,对第二日期段跑 TP4 的 mid/near-knee。
若某 topology 的 near-knee 过载,像现有 chat TP2/ρ0.01 一样排除,不为凑齐矩阵强跑。
### Phase Bchat/code prefill-only
- 复用各自已物化 window只把 OSL 改为 1
- primaryTP4/MNS16、TP2/MNS16 × 3 独立 rho × 2 trial
- 报 TTFT/CDF/quantiles、prefill tokens/s、prefix hit、waiting、chunk/context 分带 residual
- TPOT 记为 N/AE2E 仅作为“一 token completion”辅助值
- code 必须继续使用 profile-v6 long-contextchat 使用已验证 profile-v5。
### Phase Cchat/code strict decode-only
先做 batch-sensitive screening再决定是否铺满
- **C0 capability canary** 两 workload × TP4 × MNS{16,128}10min
- **C1 core full** TP{2,4} × MNS{16,128} × rho{low,near-knee} × 2 trial
- **C2 conditional expansion** 只有当 C1 的 batch 分布从 b≤8 跨到 b>8或 accuracy gap 随 MNS 改变>5pp才补 MNS{32,64} 与 mid rho。
decode profile/serving anchors 至少覆盖实际 batch p99。当前 whole-layer grid 只对少数 b≤8 有证据,且 b6 有长尾;在 MNS128 case 前必须补 b{1,2,4,8,16,32,64,128} 或实际访问 bucket不能把 b8 常数外推到 b128。
## 指标与判据
共同口径:
- 分布统计偏差:`(sim statistic-real statistic)/real statistic`,不是 per-request MAPE
- mean/p50/p90/p99 与 empirical CDF
- 5min 分窗,前 15min warmup 不进漂移 slope
- batch histogram、time-weighted running/waiting、drain tail、preemption
- 两 trial pooled 结果和 trial-to-trial noise floor 分开报告。
判据分两层:
1. **准确度:** primary latency mean/p90/p99 的 |bias|≤15% 为强通过1530% 为有界但需标注 correction>30% 立 bad case任何 topology 排序或 SLO feasibility 翻转都单独判 failure不能被平均值掩盖。
2. **长时稳定:** `|residual TheilSen slope|×12 / real noise floor < 1` 为 H-BOUNDED只适用于亚临界 cell。
mode-specific
- P+DTTFT/TPOT/E2E 全部 primary
- prefill-onlyTTFT primaryTPOT N/A
- strict decode-onlyTPOT primaryTTFT 仅表示 admission/connector overhead不进入 compute-fidelity gate。
## 成本与调度
- Phase A core12 个 6075min jobs2 topology×3 load×2 trial约 15 host-hours按 TP 加权约 45 H20-GPU-hours加 24 个 smoke/canary
- Phase B 两 workload24 个 full jobs按相同 75min 上界约 90 H20-GPU-hours
- Phase C 不一次铺满。C0 4 个 10min canaryC1 32 个 full jobsC2 按触发条件追加。
每个 job fresh server只在 `dash1``dash4` 全 8 卡 idle/healthy 时启动。即使 TP2/TP4 job 只用部分 GPU也不在同一 host 并跑,避免 fresh-server 空窗竞态。每一批使用新的 jobs TOML现有 dispatcher 非幂等。
## 预期产物
- `inputs/code-audit.json``inputs/code-window/window-manifest.json`
- P+D/prefill-only 的 paired `frontier.csv``real_requests.jsonl` 与 manifest
- profile-v6-code-longctx raw/merged profile 与 variance report
- 每 cell real/sim request metrics、server telemetry、stage ledger
- `results/code-pd-fidelity.md`
- 最终 `chat/code × P+D/prefill-only/decode-only` compatibility table。
## 已知边界
- code trace 来自 GLM5.1 业务serving model 是 Qwen3-30B若无原 prompt text测试只能保持 shape/prefix 结构,不能证明内容相关 routing fidelity
- `max_model_len=128k/256k` 解决的是接入上界,不自动解决 32k 以上 profile 外推;
- strict decode-only 只测 decode engine完整 PD 分离还需要单独建模 prefill、KV transfer、backpressure 与 KV-ready arrival。

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""Materialize the stable code window selected by audit_code_trace.py."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
from typing import Any
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--audit", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
parser.add_argument("--sample-seed", type=int, default=20260723)
return parser.parse_args()
def session_uniform(seed: int, window_id: str, session_root: Any) -> float:
payload = json.dumps(
{"seed": seed, "window_id": window_id, "session_root": session_root},
sort_keys=True,
separators=(",", ":"),
).encode()
return int.from_bytes(hashlib.blake2b(payload, digest_size=8).digest(), "big") / (
1 << 64
)
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1 << 20), b""):
digest.update(chunk)
return digest.hexdigest()
def main() -> None:
args = parse_args()
audit = json.loads(args.audit.read_text())
if audit["data_gate"] != "PASS":
raise ValueError(f"trace data gate is not PASS: {audit['data_gate']}")
selected = audit["selected"]
source = Path(selected["source"])
window = selected["stable_window"]
start = float(window["start_timestamp"])
end = float(window["end_timestamp"])
if args.output_root.exists():
raise ValueError(f"refusing to overwrite {args.output_root}")
args.output_root.mkdir(parents=True)
destination = args.output_root / "code-raw-window.jsonl"
root_of: dict[Any, Any] = {}
request_count = 0
with source.open() as input_stream, destination.open("w") as output_stream:
for source_index, line in enumerate(input_stream):
if not line.strip():
continue
row = json.loads(line)
timestamp = float(row["timestamp"])
if timestamp < start:
continue
if timestamp >= end:
break
chat = row.get("chat_id", source_index)
parent = row.get("parent_chat_id")
has_parent = parent not in (None, "", -1, "-1")
session_root = root_of.get(parent, parent) if has_parent else chat
root_of[chat] = session_root
materialized = {
**row,
"source_index": source_index,
"session_root": session_root,
"sampling_u": session_uniform(
args.sample_seed,
f"code-{start:.6f}-{end:.6f}",
session_root,
),
}
output_stream.write(
json.dumps(materialized, ensure_ascii=False, separators=(",", ":"))
+ "\n"
)
request_count += 1
expected = int(selected["selected_window_stats"]["requests"])
if request_count != expected:
raise ValueError(f"window request mismatch: materialized={request_count}, audit={expected}")
manifest = {
"schema": "frontier-code-window-v1",
"audit": str(args.audit.resolve()),
"audit_sha256": sha256(args.audit),
"source": str(source.resolve()),
"source_block_size": selected["hash_contract"]["exact_source_block_size"],
"target_block_size": 16,
"start_timestamp": start,
"end_timestamp": end,
"duration_s": end - start,
"requests": request_count,
"sample_seed": args.sample_seed,
"sampling_rule": "session-coherent deterministic sampling_u",
"max_model_len": audit["max_model_len_recommendation"],
"window_stats": selected["selected_window_stats"],
"raw_window": str(destination.resolve()),
"raw_window_sha256": sha256(destination),
}
(args.output_root / "window-manifest.json").write_text(
json.dumps(manifest, indent=2, sort_keys=True) + "\n"
)
print(json.dumps({"requests": request_count, "output_root": str(args.output_root)}))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,80 @@
#!/usr/bin/env python3
from __future__ import annotations
import json
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parent
class CodeTracePreflightTest(unittest.TestCase):
def test_audit_and_materialize_512_block_window(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
source = root / "051315-051317.jsonl"
with source.open("w") as stream:
for index in range(4501):
input_tokens = 513 if index % 2 else 512
stream.write(
json.dumps(
{
"chat_id": index,
"parent_chat_id": index - 1 if index % 2 else -1,
"timestamp": float(index),
"input_length": input_tokens,
"output_length": 32,
"hash_ids": [index // 2]
if input_tokens == 512
else [index // 2, 100000 + index],
}
)
+ "\n"
)
audit = root / "audit.json"
subprocess.run(
[
sys.executable,
str(ROOT / "audit_code_trace.py"),
"--source",
str(source),
"--output",
str(audit),
],
check=True,
)
payload = json.loads(audit.read_text())
self.assertEqual(payload["data_gate"], "PASS")
self.assertEqual(
payload["selected"]["hash_contract"]["exact_source_block_size"], 512
)
self.assertEqual(payload["max_model_len_recommendation"], 40960)
output = root / "window"
subprocess.run(
[
sys.executable,
str(ROOT / "prepare_code_window.py"),
"--audit",
str(audit),
"--output-root",
str(output),
],
check=True,
)
manifest = json.loads((output / "window-manifest.json").read_text())
rows = [
json.loads(line)
for line in (output / "code-raw-window.jsonl").read_text().splitlines()
]
self.assertEqual(manifest["requests"], len(rows))
self.assertGreaterEqual(manifest["duration_s"], 3600)
self.assertEqual(rows[0]["sampling_u"], rows[1]["sampling_u"])
if __name__ == "__main__":
unittest.main()