Add OpProf campaign: protocols, results, patches, run evidence (P0-P6)
Workload-conditioned operator profiling on patched vLLM 0.24.0 + Qwen3-30B-A3B/H20. H1b PASS (irregular patterns carry +23-45pp R64 raggedness, 8-45% token-efficiency loss vs rectangular controls); mechanism decomposition kills the padding narrative and finds the arrival-uniformization artifact (-12.9%); cross-version churn surface shows TP2/MNS64 -29.4% across vLLM 0.20->0.24 while the argmax held. Raw Layer-1 JSONL streams (507 MB) stay on disk, git-ignored; footer sidecars and metrics are tracked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
242
runs/opprof-phase5/opprof_phase5_client.py
Normal file
242
runs/opprof-phase5/opprof_phase5_client.py
Normal file
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Phase-5 private-manifest transforms and timestamp-scheduled P3 client wrapper."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
import math
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
|
||||
import opprof_phase3_client as p3
|
||||
|
||||
|
||||
def _numeric(values: list[float | int]) -> dict[str, Any]:
|
||||
finite = [float(value) for value in values if math.isfinite(float(value))]
|
||||
return {
|
||||
"n": len(values),
|
||||
"finite_n": len(finite),
|
||||
"missing_n": len(values) - len(finite),
|
||||
"min": min(finite) if finite else None,
|
||||
"max": max(finite) if finite else None,
|
||||
"distinct_n": len(set(finite)),
|
||||
"sum": sum(finite),
|
||||
}
|
||||
|
||||
|
||||
def _r16(rows: list[dict[str, Any]]) -> float:
|
||||
groups = [rows[index : index + 16] for index in range(0, len(rows) - 15, 16)]
|
||||
useful = sum(sum(int(row["input_tokens"]) for row in group) for group in groups)
|
||||
rectangular = sum(16 * max(int(row["input_tokens"]) for row in group) for group in groups)
|
||||
return 1.0 - useful / rectangular
|
||||
|
||||
|
||||
def _source_timestamps(path: Path, indices: set[int], field: str) -> dict[int, float]:
|
||||
result: dict[int, float] = {}
|
||||
maximum = max(indices)
|
||||
with path.open(encoding="utf-8") as source:
|
||||
for index, line in enumerate(source):
|
||||
if index in indices:
|
||||
value = float(json.loads(line)[field])
|
||||
if not math.isfinite(value):
|
||||
raise ValueError(f"non-finite source timestamp at {index}")
|
||||
result[index] = value
|
||||
if index >= maximum:
|
||||
break
|
||||
if set(result) != indices:
|
||||
raise ValueError("timestamp source did not cover all source_index values")
|
||||
return result
|
||||
|
||||
|
||||
def transform(args: argparse.Namespace) -> dict[str, Any]:
|
||||
rows = p3.load_manifest(Path(args.input))[: args.take_first]
|
||||
if len(rows) != args.take_first:
|
||||
raise ValueError(f"requested {args.take_first} rows, found {len(rows)}")
|
||||
indices = [int(row[args.join_key]) for row in rows]
|
||||
timestamps = _source_timestamps(
|
||||
Path(args.timestamp_source), set(indices), args.timestamp_field
|
||||
)
|
||||
source_times = [timestamps[index] for index in indices]
|
||||
if any(right < left for left, right in zip(source_times, source_times[1:])):
|
||||
raise ValueError("selected timestamps are not nondecreasing")
|
||||
if source_times[-1] <= source_times[0]:
|
||||
raise ValueError("selected timestamp span is not positive")
|
||||
end_s = (len(rows) - 1) / args.target_rate
|
||||
scale = end_s / (source_times[-1] - source_times[0])
|
||||
recorded_slots = [(value - source_times[0]) * scale for value in source_times]
|
||||
uniform_slots = [index / args.target_rate for index in range(len(rows))]
|
||||
slots = recorded_slots if args.arrival == "recorded-scaled" else uniform_slots
|
||||
|
||||
for index, row in enumerate(rows):
|
||||
row["arrival"] = args.arrival
|
||||
row["arrival_s"] = slots[index]
|
||||
row["original_index"] = index
|
||||
row["source_timestamp"] = source_times[index]
|
||||
|
||||
original = list(rows)
|
||||
max_added_delay = 0.0
|
||||
if args.service_order == "length-binned":
|
||||
edges = [int(item) for item in args.length_bin_edges.split(",")]
|
||||
|
||||
def bin_id(row: dict[str, Any]) -> int:
|
||||
length = int(row["input_tokens"])
|
||||
for index, edge in enumerate(edges):
|
||||
if length <= edge:
|
||||
return index
|
||||
raise ValueError(f"input length {length} exceeds final edge")
|
||||
|
||||
reordered: list[dict[str, Any]] = []
|
||||
for offset in range(0, len(rows), args.reorder_block_size):
|
||||
block = rows[offset : offset + args.reorder_block_size]
|
||||
ordered = sorted(
|
||||
block,
|
||||
key=lambda row: (
|
||||
bin_id(row),
|
||||
int(row["input_tokens"]),
|
||||
int(row["original_index"]),
|
||||
),
|
||||
)
|
||||
block_slots = slots[offset : offset + len(block)]
|
||||
for position, row in enumerate(ordered):
|
||||
added = max(0.0, block_slots[position] - float(row["arrival_s"]))
|
||||
max_added_delay = max(max_added_delay, added)
|
||||
row["arrival_s"] = block_slots[position]
|
||||
reordered.extend(ordered)
|
||||
rows = reordered
|
||||
if max_added_delay > args.max_added_delay_seconds + 1e-9:
|
||||
raise ValueError(
|
||||
f"fairness cap exceeded: {max_added_delay} > {args.max_added_delay_seconds}"
|
||||
)
|
||||
elif args.service_order != "original":
|
||||
raise ValueError(f"unsupported service order: {args.service_order}")
|
||||
|
||||
if sorted(row["request_id"] for row in rows) != sorted(
|
||||
row["request_id"] for row in original
|
||||
):
|
||||
raise AssertionError("request identity changed")
|
||||
for key in ("input_tokens", "output_tokens"):
|
||||
if sum(int(row[key]) for row in rows) != sum(int(row[key]) for row in original):
|
||||
raise AssertionError(f"{key} total changed")
|
||||
arrival_values = [float(row["arrival_s"]) for row in rows]
|
||||
if any(right < left for left, right in zip(arrival_values, arrival_values[1:])):
|
||||
raise AssertionError("assigned arrival slots are not nondecreasing")
|
||||
|
||||
output = Path(args.out)
|
||||
p3.atomic_jsonl(output, rows, mode=0o600)
|
||||
summary = {
|
||||
"schema": 1,
|
||||
"path": str(output),
|
||||
"sha256": p3.sha256_file(output),
|
||||
"rows": len(rows),
|
||||
"arrival": args.arrival,
|
||||
"service_order": args.service_order,
|
||||
"target_rate": args.target_rate,
|
||||
"input_tokens": _numeric([int(row["input_tokens"]) for row in rows]),
|
||||
"output_tokens": _numeric([int(row["output_tokens"]) for row in rows]),
|
||||
"arrival_s": _numeric(arrival_values),
|
||||
"source_timestamp": _numeric(source_times),
|
||||
"r16": _r16(rows),
|
||||
"max_added_delay_seconds": max_added_delay,
|
||||
"request_id_set_sha256": p3.hashlib.sha256(
|
||||
"\n".join(sorted(str(row["request_id"]) for row in rows)).encode()
|
||||
).hexdigest(),
|
||||
"invariants": {
|
||||
"same_request_ids": True,
|
||||
"same_input_tokens": True,
|
||||
"same_output_tokens": True,
|
||||
"arrival_nondecreasing": True,
|
||||
"fairness_cap": max_added_delay <= args.max_added_delay_seconds + 1e-9,
|
||||
"no_prompt_in_summary": True,
|
||||
},
|
||||
}
|
||||
p3.atomic_json(output.with_suffix(output.suffix + ".summary.json"), summary, mode=0o600)
|
||||
print(json.dumps(summary, sort_keys=True))
|
||||
return summary
|
||||
|
||||
|
||||
async def finite_timestamp_load(
|
||||
ctx: p3.RunContext, session: aiohttp.ClientSession, rate: float
|
||||
) -> list[dict[str, Any]]:
|
||||
if "arrival_s" not in ctx.rows[0]:
|
||||
return await _ORIGINAL_FINITE_LOAD(ctx, session, rate)
|
||||
sem = asyncio.Semaphore(ctx.args.max_concurrency)
|
||||
tasks: list[asyncio.Task[dict[str, Any]]] = []
|
||||
|
||||
async def limited(row: dict[str, Any], scheduled: float) -> dict[str, Any]:
|
||||
async with sem:
|
||||
return await p3.request_one(ctx, session, row, scheduled)
|
||||
|
||||
for expected in ctx.rows:
|
||||
scheduled = ctx.t0 + float(expected["arrival_s"])
|
||||
delay = scheduled - asyncio.get_running_loop().time()
|
||||
if delay > 0:
|
||||
try:
|
||||
await asyncio.wait_for(ctx.stop_event.wait(), timeout=delay)
|
||||
break
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
if ctx.stop_event.is_set():
|
||||
break
|
||||
row = await ctx.next_row()
|
||||
if row["request_id"] != expected["request_id"]:
|
||||
raise AssertionError("timestamp scheduler row drift")
|
||||
tasks.append(asyncio.create_task(limited(row, scheduled)))
|
||||
return await asyncio.gather(*tasks) if tasks else []
|
||||
|
||||
|
||||
_ORIGINAL_FINITE_LOAD = p3.finite_load
|
||||
p3.finite_load = finite_timestamp_load
|
||||
|
||||
|
||||
def build_transform_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--in", dest="input", required=True)
|
||||
parser.add_argument("--take-first", type=int, required=True)
|
||||
parser.add_argument("--timestamp-source", required=True)
|
||||
parser.add_argument("--join-key", default="source_index")
|
||||
parser.add_argument("--timestamp-field", default="timestamp")
|
||||
parser.add_argument("--arrival", choices=("recorded-scaled", "uniform"), required=True)
|
||||
parser.add_argument("--target-rate", type=float, required=True)
|
||||
parser.add_argument("--service-order", choices=("original", "length-binned"), required=True)
|
||||
parser.add_argument("--reorder-block-size", type=int, default=32)
|
||||
parser.add_argument("--analysis-cohort-size", type=int, default=16)
|
||||
parser.add_argument("--length-bin-edges", default="512,1024,2048,4096,8192,16384,32768")
|
||||
parser.add_argument("--max-added-delay-seconds", type=float, default=64)
|
||||
parser.add_argument("--out", required=True)
|
||||
return parser
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "transform":
|
||||
transform(build_transform_parser().parse_args(sys.argv[2:]))
|
||||
return
|
||||
fixed_rate = None
|
||||
if "--fixed-request-rate" in sys.argv:
|
||||
index = sys.argv.index("--fixed-request-rate")
|
||||
fixed_rate = float(sys.argv[index + 1])
|
||||
del sys.argv[index : index + 2]
|
||||
args = p3.build_parser().parse_args()
|
||||
if args.command != "run":
|
||||
p3.main()
|
||||
return
|
||||
if fixed_rate is not None:
|
||||
if args.load_point != "moderate" or fixed_rate <= 0 or not math.isfinite(fixed_rate):
|
||||
raise ValueError("--fixed-request-rate requires positive finite moderate rate")
|
||||
result_dir = Path(args.result_dir)
|
||||
result_dir.mkdir(parents=True, exist_ok=True)
|
||||
source = result_dir / "fixed-rate-source.json"
|
||||
p3.atomic_json(source, {"clean": {"completed_throughput_rps": fixed_rate}})
|
||||
args.saturation_result = str(source)
|
||||
args.rate_fraction = 1.0
|
||||
if args.profile_after_clean and not args.profile_trace_dir:
|
||||
raise ValueError("--profile-after-clean requires --profile-trace-dir")
|
||||
print(json.dumps(asyncio.run(p3.run_load(args)), sort_keys=True))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user