Ddavidich commited on
Commit
cd29a1f
·
verified ·
1 Parent(s): f2dce28

Таблица префилла переснята: модель быстрее, чем утверждала карточка

Browse files

У прежней строки 2496 / 1799 / 1140 / 648 не было квитанции в репозитории —
сплошная сверка чисел пошла её искать и не нашла. Сняты они были до правок
арены, плана позиций и распределителя.

131 072: 2496 -> 3840 т/с (34 с)
262 144: 1799 -> 2400 т/с (109 с)
524 288: 1140 -> 1375 т/с (381 с)
1 010 000: 648 -> 768 т/с (22 мин)

Ловушка замера, вынесенная в карточку: первый запрос в свежем процессе
компилирует ядра Triton на ходу. Холодные 131 072 дают 2277 т/с, прогретые
3840 — та же работа, разница 41%. Опубликовано прогретое, потому что его и
выдаёт работающий сервер.

Плюс квитанция на арифметику карты позиций: 447 941 и 251 333 проверены
счётом, и названа опущенная тонкость — пары 15-21 кладут последний токен
на 415 173, а не туда же, куда остальные.

README.md CHANGED
@@ -305,15 +305,29 @@ at stock clocks, `turboquant_3bit_nc`, one sequence.
305
 
306
  | context | read time | tokens/s |
307
  |---:|---:|---:|
308
- | 131,072 | 52 s | 2496 |
309
- | 262,144 | 146 s | 1799 |
310
- | 524,288 | 460 s (7.7 min) | 1140 |
311
- | 1,010,000 | **26 min** | 648 |
312
 
313
  RTX 4090: 32,768 tokens in 38 s (861 tok/s).
314
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  The slowdown with length is attention's quadratic cost rather than an
316
- implementation flaw: 460 s at 524,288 is *faster* than a pure quadratic
317
  extrapolation from 262,144.
318
 
319
  **The second question about the same text is nearly free.** Prefix caching gives
@@ -608,6 +622,14 @@ answers. It is not that the attention layers are ignored — they clearly respon
608
  It is that whatever they contribute at this length is not what decides whether
609
  the needle is found.
610
 
 
 
 
 
 
 
 
 
611
  None of this changes the shipped choice, and the reason it was chosen never
612
  depended on the million: below 262,144 the map is the identity and therefore
613
  cannot spoil anything inside the trained window, which no other candidate can
@@ -984,15 +1006,29 @@ RTX 5090, заводские частоты, кодек `turboquant_3bit_nc`, о
984
 
985
  | контекст | сколько читается | токенов в секунду |
986
  |---:|---:|---:|
987
- | 131 072 | 52 с | 2496 |
988
- | 262 144 | 146 с | 1799 |
989
- | 524 288 | 460 с (7.7 мин) | 1140 |
990
- | 1 010 000 | **26 мин** | 648 |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
991
 
992
  RTX 4090: 32 768 токенов за 38 с (861 т/с).
993
 
994
  Спад с ростом контекста — квадратичная стоимость внимания, а не изъян
995
- реализации: 460 с на 524 288 токенах **быстрее** чистой квадратичной
996
  экстраполяции от 262 144.
997
 
998
  **Второй вопрос по тому же тексту почти бесплатен.** Кэш префикса даёт
@@ -1282,6 +1318,13 @@ YaRN делает две вещи, и каждая стоила примерно
1282
  они явно откликаются. Дело в том, что их вклад на такой длине не решает, будет
1283
  закладка найдена или нет.
1284
 
 
 
 
 
 
 
 
1285
  Релизный выбор от этого не меняется, и его причина никогда не опиралась на
1286
  миллион: до 262 144 карта тождественна и потому ничего не может испортить внутри
1287
  обученного окна, чего не может заявить ни один другой вариант. А выше ни одна из
 
305
 
306
  | context | read time | tokens/s |
307
  |---:|---:|---:|
308
+ | 131,072 | 34 s | **3840** |
309
+ | 262,144 | 109 s | **2400** |
310
+ | 524,288 | 381 s (6.4 min) | **1375** |
311
+ | 1,010,000 | **22 min** | **768** |
312
 
313
  RTX 4090: 32,768 tokens in 38 s (861 tok/s).
314
 
315
+ > **Re-measured 2026-07-26, and the old row was slower than the model.** This
316
+ > table used to read 2496 / 1799 / 1140 / 648. Those numbers had **no receipt in
317
+ > this repository** — an audit of every figure in the card went looking and found
318
+ > none — and they were taken before the arena, the position map and the allocator
319
+ > settings changed. Re-measured on the release configuration with `max_tokens=1`,
320
+ > the model is 18–54% faster than the card claimed
321
+ > (`receipts/prefill_table.json`, reproduced by `prefill_table.py`).
322
+ >
323
+ > One measurement trap worth passing on: the **first** request in a fresh process
324
+ > compiles Triton kernels while it runs. Measured cold, 131,072 tokens gives
325
+ > 2277 tok/s; measured after one warm-up request, 3840 — the same work, 41%
326
+ > apart. The table above is the warm figure, which is what a running server
327
+ > delivers. The first request a user makes will be the slow one.
328
+
329
  The slowdown with length is attention's quadratic cost rather than an
330
+ implementation flaw: 381 s at 524,288 is *faster* than a pure quadratic
331
  extrapolation from 262,144.
332
 
333
  **The second question about the same text is nearly free.** Prefix caching gives
 
622
  It is that whatever they contribute at this length is not what decides whether
623
  the needle is found.
624
 
625
+ Those two positions are arithmetic, and checkable:
626
+ `251,333 = 1,005,333 // 4` for bare division, `447,941 = 262,144 + (1,005,333 −
627
+ 262,144) // 4` for the faithful prefix. One refinement the sentence above
628
+ glosses over: the shipped map does not place the last token at a single
629
+ position. Pairs 15–21 carry the earlier stage too and land at **415,173**; the
630
+ other twenty-six pairs land at 447,941
631
+ (`receipts/POSITION_MAP_ARITHMETIC_RECEIPT.json`).
632
+
633
  None of this changes the shipped choice, and the reason it was chosen never
634
  depended on the million: below 262,144 the map is the identity and therefore
635
  cannot spoil anything inside the trained window, which no other candidate can
 
1006
 
1007
  | контекст | сколько читается | токенов в секунду |
1008
  |---:|---:|---:|
1009
+ | 131 072 | 34 с | **3840** |
1010
+ | 262 144 | 109 с | **2400** |
1011
+ | 524 288 | 381 с (6.4 мин) | **1375** |
1012
+ | 1 010 000 | **22 мин** | **768** |
1013
+
1014
+ > **Переснято 2026-07-26, и прежняя строка была медленнее самой модели.** Здесь
1015
+ > стояло 2496 / 1799 / 1140 / 648. У этих чисел **не было квитанции в
1016
+ > репозитории** — сплошная сверка всех цифр карточки пошла её искать и не нашла,
1017
+ > — и сняты они были до правок арены, плана позиций и распределителя. Замер на
1018
+ > релизной сборке с `max_tokens=1` показал, что модель на 18–54% быстрее, чем
1019
+ > утверждала карточка (`receipts/prefill_table.json`, воспроизводится
1020
+ > `prefill_table.py`).
1021
+ >
1022
+ > Одну ловушку замера стоит передать дальше: **первый** запрос в свежем процессе
1023
+ > компилирует ядра Triton прямо на ходу. На холодную 131 072 токена дают
1024
+ > 2277 т/с, после одного прогревочного запроса — 3840. Та же работа, разница 41%.
1025
+ > В таблице стоит прогретое число, потому что именно его выдаёт работающий
1026
+ > сервер. Первый запрос пользователя будет медленным.
1027
 
1028
  RTX 4090: 32 768 токенов за 38 с (861 т/с).
1029
 
1030
  Спад с ростом контекста — квадратичная стоимость внимания, а не изъян
1031
+ реализации: 381 с на 524 288 токенах **быстрее** чистой квадратичной
1032
  экстраполяции от 262 144.
1033
 
1034
  **Второй вопрос по тому же тексту почти бесплатен.** Кэш префикса даёт
 
1318
  они явно откликаются. Дело в том, что их вклад на такой длине не решает, будет
1319
  закладка найдена или нет.
1320
 
1321
+ Обе позиции — арифметика, и её можно проверить: `251 333 = 1 005 333 // 4` для
1322
+ голого деления, `447 941 = 262 144 + (1 005 333 − 262 144) // 4` для карты с
1323
+ верным началом. Одну тонкость фраза выше сглаживает: отгружаемая карта кладёт
1324
+ последний токен не в одну точку. Пары 15–21 несут ещё и первую ступень и
1325
+ попадают на **415 173**, прочие двадцать шесть — на 447 941
1326
+ (`receipts/POSITION_MAP_ARITHMETIC_RECEIPT.json`).
1327
+
1328
  Релизный выбор от этого не меняется, и его причина никогда не опиралась на
1329
  миллион: до 262 144 карта тождественна и потому ничего не может испортить внутри
1330
  обученного окна, чего не может заявить ни один другой вариант. А выше ни одна из
prefill_table.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Таблица префилла на релизной сборке. Замер, а не наследство.
3
+
4
+ Зачем. В карточке таблица 131К 2496 т/с, 262К 1799, 524К 1140, 1M 648 подана как
5
+ замеренная, но квитанции у неё в репозитории нет — сплошная сверка чисел этого не
6
+ нашла. Карточка при этом обещает: «каждое число ниже замерено на нашем железе и
7
+ имеет квитанцию». Значит либо квитанция, либо число уходит.
8
+
9
+ Кроме того числа расходятся с сегодняшними прогонами процентов на семнадцать —
10
+ конфигурация с тех пор менялась (арена, план позиций, распределитель), так что
11
+ старые значения могли просто устареть.
12
+
13
+ Как мерится. `max_tokens=1`, чтобы декод не размазывал итог: одна ступень декода
14
+ стоит около 150 мс, и на коротких длинах это заметная доля. Отдельно пишется
15
+ время до первого токена, которое движок сообщает сам, — из него и считается
16
+ скорость префилла. Промпт подаётся идентификаторами: построение текстом
17
+ повторами даёт не ту длину, на которой думаешь мерить.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import argparse, json, os, time
22
+
23
+
24
+ def main() -> int:
25
+ ap = argparse.ArgumentParser()
26
+ ap.add_argument("--model", required=True)
27
+ ap.add_argument("--out", required=True)
28
+ ap.add_argument("--lengths", default="131072,262144,524288,1010000")
29
+ args = ap.parse_args()
30
+ lengths = [int(x) for x in args.lengths.split(",")]
31
+
32
+ from vllm import LLM, SamplingParams
33
+
34
+ t0 = time.time()
35
+ llm = LLM(model=args.model) # без флагов: профиль решает всё
36
+ load_s = round(time.time() - t0, 1)
37
+ cfg = llm.llm_engine.vllm_config
38
+ tok = llm.get_tokenizer()
39
+
40
+ unit = ("Полярная станция ведёт наблюдения за дрейфом льда, и записи "
41
+ "хранятся в общем журнале смены. ")
42
+ per = len(tok(unit).input_ids)
43
+
44
+ rows = []
45
+ for n in lengths:
46
+ text = unit * (n // max(per - 1, 1) + 64)
47
+ ids = tok(text).input_ids[:n]
48
+ if len(ids) < n:
49
+ rows.append({"target": n, "error": "корпус короче цели"})
50
+ continue
51
+ t1 = time.time()
52
+ out = llm.generate([{"prompt_token_ids": ids}],
53
+ SamplingParams(temperature=0.0, max_tokens=1))
54
+ wall = time.time() - t1
55
+ m = out[0].metrics
56
+ ttft = None
57
+ if m is not None and getattr(m, "first_token_time", None) and \
58
+ getattr(m, "first_scheduled_time", None):
59
+ ttft = m.first_token_time - m.first_scheduled_time
60
+ rows.append({
61
+ "tokens": len(ids),
62
+ "wall_seconds": round(wall, 2),
63
+ "wall_tokens_per_second": round(len(ids) / wall, 1),
64
+ "time_to_first_token_seconds": round(ttft, 2) if ttft else None,
65
+ "prefill_tokens_per_second": round(len(ids) / ttft, 1) if ttft else None,
66
+ })
67
+ print(json.dumps(rows[-1], ensure_ascii=False), flush=True)
68
+
69
+ receipt = {
70
+ "schema": "lomonosov_zenit_prefill_table_v2",
71
+ "what_this_shows": "скорость префилла на релизной сборке, max_tokens=1",
72
+ "supersedes": "таблица карточки 131К 2496 / 262К 1799 / 524К 1140 / 1M 648 — "
73
+ "без квитанции в репозитории и снятая до правок арены, плана "
74
+ "позиций и распределителя",
75
+ "load_seconds": load_s,
76
+ "max_model_len": int(cfg.model_config.max_model_len),
77
+ "kv_cache_dtype": str(cfg.cache_config.cache_dtype),
78
+ "arena_bytes": getattr(cfg.cache_config, "kv_cache_memory_bytes", None),
79
+ "chunk": int(cfg.scheduler_config.max_num_batched_tokens),
80
+ "enforce_eager": bool(cfg.model_config.enforce_eager),
81
+ "pytorch_cuda_alloc_conf": os.environ.get("PYTORCH_CUDA_ALLOC_CONF"),
82
+ "position_stages": os.environ.get("ZENIT_POSITION_STAGES"),
83
+ "clocks": "заводские, не фиксировались",
84
+ "rows": rows,
85
+ }
86
+ with open(args.out, "w", encoding="utf-8") as fh:
87
+ json.dump(receipt, fh, ensure_ascii=False, indent=1)
88
+ print(json.dumps(receipt, ensure_ascii=False, indent=1))
89
+ return 0
90
+
91
+
92
+ if __name__ == "__main__":
93
+ raise SystemExit(main())
receipts/POSITION_MAP_ARITHMETIC_RECEIPT.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "lomonosov_zenit_position_map_arithmetic_v1",
3
+ "what_this_shows": "куда три карты кладут последний токен промпта на 1 005 334 токена",
4
+ "why": "числа 447 941 и 251 333 стоят в карточке как замеренные; это арифметика, и она обязана быть проверяемой, а не приниматься на слово",
5
+ "last_token_position": 1005333,
6
+ "stages_from_config": [
7
+ {
8
+ "from": 196608,
9
+ "group": 2,
10
+ "first_pair": 15,
11
+ "last_pair": 21
12
+ },
13
+ {
14
+ "from": 262144,
15
+ "group": 4,
16
+ "first_pair": 0,
17
+ "last_pair": 31
18
+ }
19
+ ],
20
+ "bare_floor_div_4": 251333,
21
+ "faithful_prefix_then_div_4": 447941,
22
+ "staged_map_pairs_15_21": 415173,
23
+ "staged_map_other_pairs": 447941,
24
+ "card_figures": {
25
+ "447941": true,
26
+ "251333": true
27
+ },
28
+ "note": "отгружаемая ступенчатая карта кладёт последний токен НЕ в одну точку: пары 15-21 попадают на 415173, прочие 26 пар — на 447941. Прежняя формулировка карточки называла только вторую цифру."
29
+ }
receipts/prefill_table.json ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "lomonosov_zenit_prefill_table_v2",
3
+ "what_this_shows": "скорость префилла на релизной сборке, max_tokens=1",
4
+ "supersedes": "таблица карточки 131К 2496 / 262К 1799 / 524К 1140 / 1M 648 — без квитанции в репозитории и снятая до правок арены, плана позиций и распределителя",
5
+ "load_seconds": 50.3,
6
+ "max_model_len": 1010001,
7
+ "kv_cache_dtype": "turboquant_3bit_nc",
8
+ "arena_bytes": 13000000000,
9
+ "chunk": 4096,
10
+ "enforce_eager": true,
11
+ "pytorch_cuda_alloc_conf": "expandable_segments:True",
12
+ "position_stages": "[{\"from\":196608,\"group\":2,\"first_pair\":15,\"last_pair\":21},{\"from\":262144,\"group\":4,\"first_pair\":0,\"last_pair\":31}]",
13
+ "clocks": "заводские, не фиксировались",
14
+ "rows": [
15
+ {
16
+ "tokens": 131072,
17
+ "wall_seconds": 57.57,
18
+ "wall_tokens_per_second": 2276.9,
19
+ "time_to_first_token_seconds": null,
20
+ "prefill_tokens_per_second": null
21
+ },
22
+ {
23
+ "tokens": 262144,
24
+ "wall_seconds": 109.22,
25
+ "wall_tokens_per_second": 2400.1,
26
+ "time_to_first_token_seconds": null,
27
+ "prefill_tokens_per_second": null
28
+ },
29
+ {
30
+ "tokens": 524288,
31
+ "wall_seconds": 381.32,
32
+ "wall_tokens_per_second": 1374.9,
33
+ "time_to_first_token_seconds": null,
34
+ "prefill_tokens_per_second": null
35
+ },
36
+ {
37
+ "tokens": 1010000,
38
+ "wall_seconds": 1315.46,
39
+ "wall_tokens_per_second": 767.8,
40
+ "time_to_first_token_seconds": null,
41
+ "prefill_tokens_per_second": null
42
+ }
43
+ ],
44
+ "cold_start_note": "точка 131 072 в rows снята ПЕРВОЙ в процессе и потому платит компиляцию ядер Triton на ходу: 2276.9 т/с. После прогревочного запроса на 8 192 та же длина даёт 3839.7 т/с — разница 41%. В карточке стоит прогретое число.",
45
+ "warm_131072_tokens_per_second": 3839.7,
46
+ "warm_run_receipt": "prefill_warm131k.json",
47
+ "published_table": {
48
+ "131072": 3839.7,
49
+ "262144": 2400.1,
50
+ "524288": 1374.9,
51
+ "1010000": 767.8
52
+ }
53
+ }
receipts/prefill_warm131k.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "lomonosov_zenit_prefill_table_v2",
3
+ "what_this_shows": "скорость префилла на релизной сборке, max_tokens=1",
4
+ "supersedes": "таблица карточки 131К 2496 / 262К 1799 / 524К 1140 / 1M 648 — без квитанции в репозитории и снятая до правок арены, плана позиций и распределителя",
5
+ "load_seconds": 50.4,
6
+ "max_model_len": 1010001,
7
+ "kv_cache_dtype": "turboquant_3bit_nc",
8
+ "arena_bytes": 13000000000,
9
+ "chunk": 4096,
10
+ "enforce_eager": true,
11
+ "pytorch_cuda_alloc_conf": "expandable_segments:True",
12
+ "position_stages": "[{\"from\":196608,\"group\":2,\"first_pair\":15,\"last_pair\":21},{\"from\":262144,\"group\":4,\"first_pair\":0,\"last_pair\":31}]",
13
+ "clocks": "заводские, не фиксировались",
14
+ "rows": [
15
+ {
16
+ "tokens": 8192,
17
+ "wall_seconds": 24.19,
18
+ "wall_tokens_per_second": 338.7,
19
+ "time_to_first_token_seconds": null,
20
+ "prefill_tokens_per_second": null
21
+ },
22
+ {
23
+ "tokens": 131072,
24
+ "wall_seconds": 34.14,
25
+ "wall_tokens_per_second": 3839.7,
26
+ "time_to_first_token_seconds": null,
27
+ "prefill_tokens_per_second": null
28
+ }
29
+ ]
30
+ }