← На главную

Mean обманул: кэш ускорил медиану, но p99 просел — размер ответа

29.07.2026 11:42 · hackernews

Автор пытался проверить ускорение сборки с lld на работе, но на живых дашбордах улучшения тонули в шуме. Коллега, которая сталкивалась с похожей проблемой при оценке скорости билдов, предложила строить кумулятивную функцию распределения — CDF. Это стало откровением. Вся статья построена вокруг одного синтетического датасета с фиксированным seed (воспроизвести всё можно через nix-shell и nix) и показывает, как разные визуализации одного и того же набора чисел рассказывают совершенно разные истории.

Сюжет: в типичный веб-сервис выкатывают новый кэширующий слой. Релиз длится неделю, и по итогам средняя задержка (mean) выросла со 112 мс до 122 мс — выглядит как регресс. Но если посмотреть на перцентили, картина разваливается. Медиана (p50) говорит, что типичный запрос стал почти вдвое быстрее. Зато p99 кричит о SEV — самые тяжёлые запросы замедлились более чем вдвое. Получается, что каждый прав, а одна сводка не тянет.

Дальше в ход идёт график плотности. «До» — один аккуратный горб, «после» — два горба. Так объясняется противоречие, но считывать перцентили с такого графика неудобно. Куда нагляднее работает CDF. Две кривые «до» и «после» пересекаются на отметке ~140 мс. Левее кривая «после» ушла вверх — больше запросов стали быстрее. Правее — кривая «после» выше, там запросы медленнее. Пересечение CDF — верный признак, что ни один перцентиль не расскажет всей правды. Чтобы оценить масштаб изменений, строят shift function — разницу задержки «после» минус «до» для каждого перцентиля.

Релиз был не мгновенным, а шёл день за днём. Если разложить распределения по дням, получится ridgeline: видно, как основной пик быстрых запросов съезжает влево, а второй пик медленных тихо растёт справа. На heatmap по дням тоже проступает новая популяция.

Механизм бимодальности вскрывается, когда запросы делят на cache hit и cache miss. CDF для каждой группы снова унимодальна: попадания в кэш быстрее старого базового уровня, промахи платят лишний hop и уезжают далеко вправо. Дальше — вопрос «почему». Поле «размер ответа» даёт ответ: мелкие объекты живут в кэше, крупные вытесняются или не влезают. Jointplot латентности против размера ответа, раскрашенный по попаданиям/промахам, чётко разделяет два кластера: мелкие-и-быстрые и крупные-и-медленные. Действовать теперь можно предметно: поднять максимальный размер объекта в кэше или разбить большие ответы.

Главный вывод — смотреть на данные, а не верить одному числу. Особенно впечатляет, как CDF передаёт всё распределение целиком. И как напоминает в конце цитата Черчилля: не доверяй статистике, которую не подделал сам.

Читать оригинал →