Откат арены отдаёт куски системе: пик −36…61 % при времени ±1 %
Довод «удерживать куски выгодно, потому что арена остаётся горячей в кэше» стоял в fl_arena_rollback незамеренным. Замерили — довод верен по знаку и ничтожен по величине: отдача кусков системе стоит от −1,8 % до +1,2 % времени, а пик роняет на треть-две трети.
Отдаётся весь хвост цепочки за отметкой, кроме первых FL_ARENA_KEEP (= 4) кусков: их откат оставляет себе, потому что сразу после него fl_region_close кладёт копию результата обратно в арену, и без запаса эта копия идёт к malloc.
Чем подтверждено. Ветка b/arena-release над стволом 58ac015f. Обе колонки — ОДИН двоичный: perf на машине закрыт, а две сборки сравнивать нечестно, поэтому счётчики вшиты в тот же двоичный и включаются переменной среды. Прогоны подряд, пик через /usr/bin/time -v, загрузка машины названа у каждой пары.
flang check flang/self/types.flang --proof, загрузка 14,7–15,2:
| что делает откат | время | пик | покупок у malloc |
|---|---|---|---|
| держит весь хвост (как было) | 1:34,92 | 6 236 536 КБ | 94 022 |
| отдаёт весь хвост | 1:36,06 | 3 952 772 КБ | 1 303 680 |
| отдаёт, оставив 1 кусок | 1:35,50 | 3 950 176 КБ | 1 104 760 |
| отдаёт, оставив 4 куска | 1:34,99 | 3 974 552 КБ | 657 256 |
flang check flang/self/parser.flang --proof, загрузка 15,5–16,7:
| что делает откат | время | пик |
|---|---|---|
| держит весь хвост | 1:42,92 | 3 022 784 КБ |
| отдаёт, оставив 4 | 1:43,76 | 1 172 216 КБ |
То есть память −36,3 % и −61,2 %, время +0,07 % и +0,8 % при разбросе повторов 0,45 % по времени и 4,5 % по пику. Ведомость types.flang побайтово та же: 1 635 строк, одна сумма md5 на обе колонки.
И то же самое двумя обычными сборками, без счётчиков — счётчики стоят около процента сами по себе. Ствол против этой правки, подряд, загрузка 15,8:
| прогон | ствол | правка |
|---|---|---|
check flang/self/types.flang --proof | 1:33,91 / 6 235 232 КБ | 1:33,37 / 3 965 748 КБ |
check flang/self/parser.flang --proof | 1:42,92 / 3 022 784 КБ | 1:43,76 / 1 172 216 КБ |
check flang/stdlib/strings.flang | 0:03,77 / 211 152 КБ | 0:04,03 / 89 064 КБ |
Вывод у всех трёх побайтово тот же, коды возврата те же. На самом тяжёлом прогоне правка даже быстрее ствола на 0,6 %; дороже она только на мелком, и там это 0,26 с.
Пик — это МНОГО МЕЛКИХ кусков, а не несколько огромных. Счётчик по размерам на types.flang: из 94 022 купленных кусков 93 756 (94 % байт) — минимального размера FL_CHUNK_MIN = 64 КиБ. Отсюда и работал приём: отдавать надо было не редкие громадины, а поток обычных кусков.
Ещё два числа из того же счётчика. «Держим сейчас» в конце прогона совпадало с «куплено» до байта — то есть до этой правки арена не отдавала системе НИЧЕГО за весь прогон. И из 66 009 433 закрытий области 65 053 562 (98,6 %) отказывались по порогу «мало наросло», 731 972 — по обмеру, а откатов было 223 899: пик держат не отказы, а именно те двести тысяч откатов, после которых память оставалась у арены.
Чем ограничено. Мерили flang check на исходниках самого компилятора, а не перепечатку семени: перепечатка в этот час шла у соседней учётной записи, а второй такой машина не выдержала бы. Устройство арены у обеих команд одно, но доля отката в пике у перепечатки не измерена и может быть другой: по замеру 21 августа память печати лежит в глубокой нехвостовой рекурсии, где область отказывается от отката ЗАКОННО (результат больше четверти наросшего), а где отката нет — там и отдавать нечего.
Чего правка не трогает. fl_arena_reset по-прежнему оставляет куски себе: он зовётся между запросами оболочки, и там куски забирает тот же запрос сразу же.
Связано: arena-never-releases, arena-makes-memory-not-time-the-limit-of-a-long-computation, subgraphs-below-the-rollback-mark-need-no-copy, memory-peak-scales-with-obligations-not-file-size