LOMONOSOV-ZENIT-27B-1M-INDEV / prefill_table.py
Ddavidich's picture
Таблица префилла переснята: модель быстрее, чем утверждала карточка
cd29a1f verified
Raw
History Blame Contribute Delete
4.99 kB
#!/usr/bin/env python3
"""Таблица префилла на релизной сборке. Замер, а не наследство.
Зачем. В карточке таблица 131К 2496 т/с, 262К 1799, 524К 1140, 1M 648 подана как
замеренная, но квитанции у неё в репозитории нет — сплошная сверка чисел этого не
нашла. Карточка при этом обещает: «каждое число ниже замерено на нашем железе и
имеет квитанцию». Значит либо квитанция, либо число уходит.
Кроме того числа расходятся с сегодняшними прогонами процентов на семнадцать —
конфигурация с тех пор менялась (арена, план позиций, распределитель), так что
старые значения могли просто устареть.
Как мерится. `max_tokens=1`, чтобы декод не размазывал итог: одна ступень декода
стоит около 150 мс, и на коротких длинах это заметная доля. Отдельно пишется
время до первого токена, которое движок сообщает сам, — из него и считается
скорость префилла. Промпт подаётся идентификаторами: построение текстом
повторами даёт не ту длину, на которой думаешь мерить.
"""
from __future__ import annotations
import argparse, json, os, time
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--model", required=True)
ap.add_argument("--out", required=True)
ap.add_argument("--lengths", default="131072,262144,524288,1010000")
args = ap.parse_args()
lengths = [int(x) for x in args.lengths.split(",")]
from vllm import LLM, SamplingParams
t0 = time.time()
llm = LLM(model=args.model) # без флагов: профиль решает всё
load_s = round(time.time() - t0, 1)
cfg = llm.llm_engine.vllm_config
tok = llm.get_tokenizer()
unit = ("Полярная станция ведёт наблюдения за дрейфом льда, и записи "
"хранятся в общем журнале смены. ")
per = len(tok(unit).input_ids)
rows = []
for n in lengths:
text = unit * (n // max(per - 1, 1) + 64)
ids = tok(text).input_ids[:n]
if len(ids) < n:
rows.append({"target": n, "error": "корпус короче цели"})
continue
t1 = time.time()
out = llm.generate([{"prompt_token_ids": ids}],
SamplingParams(temperature=0.0, max_tokens=1))
wall = time.time() - t1
m = out[0].metrics
ttft = None
if m is not None and getattr(m, "first_token_time", None) and \
getattr(m, "first_scheduled_time", None):
ttft = m.first_token_time - m.first_scheduled_time
rows.append({
"tokens": len(ids),
"wall_seconds": round(wall, 2),
"wall_tokens_per_second": round(len(ids) / wall, 1),
"time_to_first_token_seconds": round(ttft, 2) if ttft else None,
"prefill_tokens_per_second": round(len(ids) / ttft, 1) if ttft else None,
})
print(json.dumps(rows[-1], ensure_ascii=False), flush=True)
receipt = {
"schema": "lomonosov_zenit_prefill_table_v2",
"what_this_shows": "скорость префилла на релизной сборке, max_tokens=1",
"supersedes": "таблица карточки 131К 2496 / 262К 1799 / 524К 1140 / 1M 648 — "
"без квитанции в репозитории и снятая до правок арены, плана "
"позиций и распределителя",
"load_seconds": load_s,
"max_model_len": int(cfg.model_config.max_model_len),
"kv_cache_dtype": str(cfg.cache_config.cache_dtype),
"arena_bytes": getattr(cfg.cache_config, "kv_cache_memory_bytes", None),
"chunk": int(cfg.scheduler_config.max_num_batched_tokens),
"enforce_eager": bool(cfg.model_config.enforce_eager),
"pytorch_cuda_alloc_conf": os.environ.get("PYTORCH_CUDA_ALLOC_CONF"),
"position_stages": os.environ.get("ZENIT_POSITION_STAGES"),
"clocks": "заводские, не фиксировались",
"rows": rows,
}
with open(args.out, "w", encoding="utf-8") as fh:
json.dump(receipt, fh, ensure_ascii=False, indent=1)
print(json.dumps(receipt, ensure_ascii=False, indent=1))
return 0
if __name__ == "__main__":
raise SystemExit(main())