Perf Weekly | №3
(инструменты, системы и производительность)
Linux perf-top basics: understand the %
All Roads Lead to IPC: Rethinking CPU Performance Design
IPC как главная метрика CPU. Архитектура процессора, узкие места, latency vs occupation и как это всё применять, чтобы ускорить код. Дорогу осилит идущий 🙂.
How can I learn about performance optimization?
Большой тред о том, как расти в performance engineering: как учиться, на чем фокусироваться, типичные ловушки и куча полезных ссылок.
А возникшие вопросы можно обсудить в треде;)
(инструменты, системы и производительность)
Linux perf-top basics: understand the %
perf top базовая утилита, которая в три секунды покажет чем же занят процесс. Полезно делать это эффективно!All Roads Lead to IPC: Rethinking CPU Performance Design
IPC как главная метрика CPU. Архитектура процессора, узкие места, latency vs occupation и как это всё применять, чтобы ускорить код. Дорогу осилит идущий 🙂.
How can I learn about performance optimization?
Большой тред о том, как расти в performance engineering: как учиться, на чем фокусироваться, типичные ловушки и куча полезных ссылок.
dbi Blog
Linux perf-top basics: understand the %
By Franck Pachot . Linux kernel has a powerful instrumentation that can be accessed easily. When you want to drill down into your program functions to understand their CPU usage, “perf” is the easiest. It can attach to the processes, sample the CPU cycles…
1🔥12👍6❤2
Замечали "странные" проценты в скобках в выводе
Это multiplexing.
Аппаратных счётчиков в CPU может быть меньше, чем событий, которые мы хотим измерить. И perf выкручивается тем, что по очереди переключается между событиями и далее масштабирует результат.
Соответственно проценты в скобках это доля времени от всего замера, когда конкретное событие реально измерялось.
Если хочется разобраться глубже (а как иначе?), читаем статью PMU counters and profiling basics.
perf stat?$ perf stat -e cycles,instructions,LLC-loads,LLC-load-miss,... -C 1 -- sleep 5
Performance counter stats for 'CPU(s) 1':
15989951459 cycles (62.40%)
8367516992 instructions (74.96%)
6897420 LLC-loads (75.04%)
1258051 LLC-load-miss (75.05%)
1977812325 dTLB-loads (75.05%)
1059510 dTLB-load-misses (75.05%)
1957630660 mem_load_retired.l1_hit (49.91%)
24843220 mem_load_retired.l1_miss (49.91%)
Это multiplexing.
Аппаратных счётчиков в CPU может быть меньше, чем событий, которые мы хотим измерить. И perf выкручивается тем, что по очереди переключается между событиями и далее масштабирует результат.
Соответственно проценты в скобках это доля времени от всего замера, когда конкретное событие реально измерялось.
Если хочется разобраться глубже (а как иначе?), читаем статью PMU counters and profiling basics.
easyperf.net
PMU counters and profiling basics. | Easyperf
🔥17❤2
Про L3 Cache (LLC)
L3 последний из “быстрых” уровней памяти, он способен отдавать данные за 30-60 циклов процессора.
А дальше идет DRAM, где стоимость доступа Local / Remote может составлять до 150–300 cycles!
Потому если перформанс для нас важен, то хорошо когда
Осознав масштаб трагедии, идём смотреть, как чувствуют себя наши системы.
—
Node exporter любезно предоставляет набор метрик (через perf коллектор) для оценки обращений к L3:
В perf им соответствуют ивенты:
Там и подсчитаем:
Разница существенна, какой метрике будем доверять? :)
cache-references / cache-misses
Это самые “шумные” ивенты, включают в себя:
- запросы как на read так и на store;
- prefetch;
- instruction fetch;
- спекулятивные выполнения.
То есть по этим событиям сложно сделать вывод о реальном положении дел с L3.
LLC-loads / LLC-load-misses
Здесь детализации побольше: отслеживаются только запросы на read (для write есть отдельные
В целом это уже то, с чем можно работать и делать более менее обоснованные выводы из полученных цифр.
Но может есть что-то более точное?
mem_load_retired.l3_*
Для процессоров Intel доступны события
А еще они precise (PEBS)! Но и об этом в другой раз:)
Итоги
1. если хочется быстро оценить ситуацию, то пользуйся
2. если хочется точнее разобраться откуда идут промахи, то
Гудлак 😊
—
p.s. поддержать плюсом на linkedin тут.
L3 последний из “быстрых” уровней памяти, он способен отдавать данные за 30-60 циклов процессора.
А дальше идет DRAM, где стоимость доступа Local / Remote может составлять до 150–300 cycles!
Потому если перформанс для нас важен, то хорошо когда
L3 Hit Rate большой, а L3 Miss Rate маленький ;)Осознав масштаб трагедии, идём смотреть, как чувствуют себя наши системы.
—
Node exporter любезно предоставляет набор метрик (через perf коллектор) для оценки обращений к L3:
- node_perf_cache_refs_total
- node_perf_cache_misses_total
- node_perf_cache_ll_read_hits_total
- node_perf_cache_ll_read_misses_total
В perf им соответствуют ивенты:
- cache-references
- cache-misses
- LLC-loads
- LLC-load-miss
Там и подсчитаем:
$ perf stat -e cache-references,cache-misses \
-e LLC-loads,LLC-load-misses \
-- sleep 5
166680 cache-references
38376 cache-misses # 23.0% miss rate
65552 LLC-loads
8997 LLC-load-misses # 13.7% miss rate
Разница существенна, какой метрике будем доверять? :)
cache-references / cache-misses
Это самые “шумные” ивенты, включают в себя:
- запросы как на read так и на store;
- prefetch;
- instruction fetch;
- спекулятивные выполнения.
Спекулятивное выполнение это когда процессор исполняет инструкции заранее, предполагая, что выбранный путь выполнения окажется верным.
То есть по этим событиям сложно сделать вывод о реальном положении дел с L3.
LLC-loads / LLC-load-misses
Здесь детализации побольше: отслеживаются только запросы на read (для write есть отдельные
LLC-store* события) + спекулятивные обращения.В целом это уже то, с чем можно работать и делать более менее обоснованные выводы из полученных цифр.
Но может есть что-то более точное?
mem_load_retired.l3_*
Для процессоров Intel доступны события
mem_load_retired.l3_hit и mem_load_retired.l3_miss. Они учитывают только те загрузки, которые были retired (реально выполнены и завершены процессором), то есть действительно повлияли на ход выполнения программы:$ perf stat -e cache-references,cache-misses \
-e LLC-loads,LLC-load-misses \
-e mem_load_retired.l3_hit,mem_load_retired.l3_miss \
-- sleep 5
199082 cache-references
55283 cache-misses # 27.7% miss rate
65554 LLC-loads
8796 LLC-load-misses # 13.4% miss rate
16790 mem_load_retired.l3_hit
3694 mem_load_retired.l3_miss # 22.0% miss rate
А еще они precise (PEBS)! Но и об этом в другой раз:)
Итоги
1. если хочется быстро оценить ситуацию, то пользуйся
LLC-load* (или соответствующие им метрики в Node exporter)2. если хочется точнее разобраться откуда идут промахи, то
mem_load_retired.l3_* в помощь.Гудлак 😊
—
p.s. поддержать плюсом на linkedin тут.
Linkedin
Про L3 Cache (LLC)
L3 последний из “быстрых” уровней памяти, он способен отдавать данные за 30-60 циклов процессора. А дальше идет DRAM, где стоимость доступа Local / Remote может составлять до 150–300 cycles! Потому если перформанс для нас важен, то хорошо когда L3 Hit Rate…
1🔥24✍4❤3👍1
Сегодня у нас экскурсия по заводу! А устроен он так:
- первый цех подготавливает сырье
- второй обрабатывает его до готового вида
- отдел приёмки выпускает в свет только то, что пришло без брака.
Когда всё работает слажено, то и продукция сходит с ленты быстро и качественно.
Но стоит одному цеху притормозить (станок сломался) и конвейер встаёт: пробка с одной стороны, простой с другой.
Выходит, что скорость всего завода определяется самым медленным цехом.
—
Вот и процессор устроен так же.
Упрощенно его работу можно разделить на три последовательных этапа:
- Frontend: достать инструкцию и подготовить её к исполнению (цех №1)
- Backend: выполнить поступившую инструкцию (цех №2)
- Retiring: зафиксировать инструкцию как выполненную, если она не выброшена из-за ошибки предсказания (приемка)
А объём готовой продукции это количество инструкций, завершённых за цикл. Что и есть IPC (instructions per cycle).
—
Проблема IPC в том, что это очень верхнеуровневая метрика-симптом. Она может показать, что скорость просела, но не говорит где именно и почему.
Чтобы ответить на эти вопросы, современные процессоры любезно предоставляют сотни ивентов и столько же метрик (ознакомиться). Разбираться в них безусловно интересно, но оочень долго.
А нам нужно побыстрее понять что к чему, и приступить к исправлению проблем.
Хорошо, что умные ребята уже подумали за нас и предложили методологию
Top-Down Microarchitecture Analysis.
—
Если продолжить аналогию с заводом, TMA не измеряет скорость каждого отдельного станка. Она показывает, в каком цеху завод чаще всего теряет время:
- Frontend Bound: первый цех не успевает подготавливать инструкции.
- Backend Bound: второй цех не успевает выполнять поступающие инструкции.
- Bad Speculation: часть работы уходит в брак, например из-за ошибки предсказания перехода.
- Retiring: инструкции успешно проходят все этапы и фиксируются как выполненные.
Наша естественная цель: максимизировать Retiring и минимизировать остальные категории.
Для этого каждую из них можно разложить глубже и понять, из-за чего именно замедляется процессор!
—
Подробнее:
1. описание методологии от её автора @Ahmad Yasin
2. практическо-теоретический гайд от @dendibakh (можно и начать отсюда)
3. тулинг для анализа TMA
Удачи!
—
p.s. поддержать лайком на linkedin: тут.
- первый цех подготавливает сырье
- второй обрабатывает его до готового вида
- отдел приёмки выпускает в свет только то, что пришло без брака.
Когда всё работает слажено, то и продукция сходит с ленты быстро и качественно.
Но стоит одному цеху притормозить (станок сломался) и конвейер встаёт: пробка с одной стороны, простой с другой.
Выходит, что скорость всего завода определяется самым медленным цехом.
—
Вот и процессор устроен так же.
Упрощенно его работу можно разделить на три последовательных этапа:
- Frontend: достать инструкцию и подготовить её к исполнению (цех №1)
- Backend: выполнить поступившую инструкцию (цех №2)
- Retiring: зафиксировать инструкцию как выполненную, если она не выброшена из-за ошибки предсказания (приемка)
А объём готовой продукции это количество инструкций, завершённых за цикл. Что и есть IPC (instructions per cycle).
—
Проблема IPC в том, что это очень верхнеуровневая метрика-симптом. Она может показать, что скорость просела, но не говорит где именно и почему.
Чтобы ответить на эти вопросы, современные процессоры любезно предоставляют сотни ивентов и столько же метрик (ознакомиться). Разбираться в них безусловно интересно, но оочень долго.
А нам нужно побыстрее понять что к чему, и приступить к исправлению проблем.
Хорошо, что умные ребята уже подумали за нас и предложили методологию
Top-Down Microarchitecture Analysis.
—
Если продолжить аналогию с заводом, TMA не измеряет скорость каждого отдельного станка. Она показывает, в каком цеху завод чаще всего теряет время:
- Frontend Bound: первый цех не успевает подготавливать инструкции.
- Backend Bound: второй цех не успевает выполнять поступающие инструкции.
- Bad Speculation: часть работы уходит в брак, например из-за ошибки предсказания перехода.
- Retiring: инструкции успешно проходят все этапы и фиксируются как выполненные.
Наша естественная цель: максимизировать Retiring и минимизировать остальные категории.
Для этого каждую из них можно разложить глубже и понять, из-за чего именно замедляется процессор!
—
Подробнее:
1. описание методологии от её автора @Ahmad Yasin
2. практическо-теоретический гайд от @dendibakh (можно и начать отсюда)
3. тулинг для анализа TMA
Удачи!
—
p.s. поддержать лайком на linkedin: тут.
2👍25❤8
Когда мы приступаем к оптимизации системы (код, инфраструктура и т.п.), важно не торопиться и правильно наметить точки приложения усилий.
Промахнувшись с выбором, можно существенно ускорить отдельную часть системы, но почти не повлиять на общее время работы.
Эту идею хорошо иллюстрирует закон Амдала: итоговый выигрыш ограничен долей времени, которую занимал целевой участок.
Пример.
Общее время работы 100 мс, состоит из четырёх этапов:
A - 15 мс
Б - 5 мс
В - 10 мс
Д - 70 мс
Если ускорить Б в космические 10×, система в целом станет быстрее всего на 4.5%, тогда как оптимизация Д в 2 раза даст уже около 54% выигрыша.
И это при том, что затраченные усилия могли быть сопоставимы.
Поэтому начинать стоит с поиска того, что реально ограничивает систему. Иначе легко поддаться соблазну оптимизировать то, что просто лучше знаешь и в итоге потратить время впустую.
——
На эту тему было интересное обсуждение на LinkedIn, где подметили, что бутылочное горлышко системы не статично и со временем может менять форму и местоположение.
Поэтому цикл поиск узкого места → оптимизация → поиск узкого места можно повторять бесконечно 🙂
Промахнувшись с выбором, можно существенно ускорить отдельную часть системы, но почти не повлиять на общее время работы.
Эту идею хорошо иллюстрирует закон Амдала: итоговый выигрыш ограничен долей времени, которую занимал целевой участок.
Пример.
Общее время работы 100 мс, состоит из четырёх этапов:
A - 15 мс
Б - 5 мс
В - 10 мс
Д - 70 мс
Если ускорить Б в космические 10×, система в целом станет быстрее всего на 4.5%, тогда как оптимизация Д в 2 раза даст уже около 54% выигрыша.
И это при том, что затраченные усилия могли быть сопоставимы.
Поэтому начинать стоит с поиска того, что реально ограничивает систему. Иначе легко поддаться соблазну оптимизировать то, что просто лучше знаешь и в итоге потратить время впустую.
——
На эту тему было интересное обсуждение на LinkedIn, где подметили, что бутылочное горлышко системы не статично и со временем может менять форму и местоположение.
Поэтому цикл поиск узкого места → оптимизация → поиск узкого места можно повторять бесконечно 🙂
1🔥24❤6
Perf Weekly | №4
(инструменты, системы и производительность)
Modern X86 Assembly Language Programming (podcast)
Чем больше занимаешься производительностью, тем глубже приходится закапываться, вплоть до уровня инструкций процессора. И неплохо бы понимать заранее, что это такое и как работает.
Подкаст может стать хорошим введением в тему.
Locks Aren't Slow; Lock Contention Is
Статья предлагает посмотреть на locks под другим углом, подсвечивая, что проблема не в наличии блокировки (взятие лока это пара десятков наносекунд), а в уровне конкуренции за него.
Вот на этом и стоит сосредоточиться: проектировать так, чтобы contention был минимальным, если конечно это оправдано:)
Красивые картинки прилагаются.
5 Reasons Why Box Plots are the Better Default Choice for Visualizing Performance
В продолжении темы про картинки.
Автор убедительно поясняет, почему для визуализации производительности нужно перестать пользоваться Bar charts в пользу Box plots.
Основной довод - performance это про распределение, а не число.
И визуализация должна:
a) это отображать
b) быть информативной
c) не быть перегруженной.
На взгляд автора, Box plots как раз золотая середина.
(инструменты, системы и производительность)
Modern X86 Assembly Language Programming (podcast)
Чем больше занимаешься производительностью, тем глубже приходится закапываться, вплоть до уровня инструкций процессора. И неплохо бы понимать заранее, что это такое и как работает.
Подкаст может стать хорошим введением в тему.
Locks Aren't Slow; Lock Contention Is
Статья предлагает посмотреть на locks под другим углом, подсвечивая, что проблема не в наличии блокировки (взятие лока это пара десятков наносекунд), а в уровне конкуренции за него.
Вот на этом и стоит сосредоточиться: проектировать так, чтобы contention был минимальным, если конечно это оправдано:)
Красивые картинки прилагаются.
5 Reasons Why Box Plots are the Better Default Choice for Visualizing Performance
В продолжении темы про картинки.
Автор убедительно поясняет, почему для визуализации производительности нужно перестать пользоваться Bar charts в пользу Box plots.
Основной довод - performance это про распределение, а не число.
И визуализация должна:
a) это отображать
b) быть информативной
c) не быть перегруженной.
На взгляд автора, Box plots как раз золотая середина.
🔥10👍1
Как CPU читает данные из памяти и о чем тут стоит подумать
Допустим, у нас есть указатель на счетчик (
То же самое на ассемблере:
1.
2.
Если после этого программа завершается, то с точки зрения производительности, нас интересует только скорость доставки данных (считаем что инкремент выполняется за константное время, в 1 цикл).
Типичные тайминги:
Но если впереди у программы есть другая работа, картина усложняется - появляется фактор пропускной способности. Даже в рамках одного ядра (читаем про out-of-order execution, ссылки в конце).
———
Максимальную пропускную способность для одного ядра можно грубо подсчитать по формуле:
Где:
-
-
-
Получается, что одно ядро может потреблять порядка 10 GB/s пропускной способности памяти, при том что один канал DDR5 выдает ~38 GB/s.
И раз переутилизация каналов DRAM часто не наш кейс, все что остается это сокращать задержку доступа к данным (Latency) и/или увеличивать эффективное число загрузок
Из вариантов:
* сокращать working set (рабочий набор), чтобы больше данных помещались в быстрые кеши процессора
* избегать pointer chasing, особенно в list-подобных структурах, которые ограничивают
* продумывать layout данных: группировать поля структур по паттернам доступа, чтобы они подтягивались в рамках одной кеш-линии
* использовать software prefetch, Huge Pages и прочие техники.
———
Ссылки по теме:
- Taras Tsugrii о memory-bound
- A whirlwind introduction to dataflow graphs (pointer chasing)
- Про Line Fill Buffer (LFB)
- Out-of-order execution
- Performance Analysis and Tuning on Modern CPUs
Допустим, у нас есть указатель на счетчик (
uint32_t), значение которого нужно увеличить:uint32_t inc(uint32_t *p) {
return *p + 1;
}То же самое на ассемблере:
inc:
mov eax, dword ptr [rdi]
inc eax
ret
1.
mov загружает в регистр eax 4 байта, при промахе в L1 целая кеш-линия (64 байта) подтягивается со следующих уровней;2.
inc увеличивает значение в eax на 1.Если после этого программа завершается, то с точки зрения производительности, нас интересует только скорость доставки данных (считаем что инкремент выполняется за константное время, в 1 цикл).
Типичные тайминги:
Типичные тайминги:
| Level | Cycles | Latency (~3 GHz )
|------------------|--------|------------------
| L1D | ~4-5 | ~1.5 ns
| L2 | ~12-15 | ~4-5 ns
| L3 (LLC, local) | ~40-70 | ~14-23 ns
| DRAM (local NUMA)| — | ~80-120 ns
| DRAM (remote) | — | ~140-200 ns
Но если впереди у программы есть другая работа, картина усложняется - появляется фактор пропускной способности. Даже в рамках одного ядра (читаем про out-of-order execution, ссылки в конце).
———
Максимальную пропускную способность для одного ядра можно грубо подсчитать по формуле:
Bandwidth = N_max × line_size / miss_latencyГде:
-
N_max: число одновременных загрузок из памяти при L1 miss (memory-level parallelism), в Intel ограничено компонентом Line Fill Buffer (LFB), ~12-16 единиц;-
line_size: размер кеш-линии, 64 байта;-
miss_latency: задержка доступа к данным, пусть в 100ns. Получается, что одно ядро может потреблять порядка 10 GB/s пропускной способности памяти, при том что один канал DDR5 выдает ~38 GB/s.
И раз переутилизация каналов DRAM часто не наш кейс, все что остается это сокращать задержку доступа к данным (Latency) и/или увеличивать эффективное число загрузок
N (Throughput). Из вариантов:
* сокращать working set (рабочий набор), чтобы больше данных помещались в быстрые кеши процессора
* избегать pointer chasing, особенно в list-подобных структурах, которые ограничивают
N* продумывать layout данных: группировать поля структур по паттернам доступа, чтобы они подтягивались в рамках одной кеш-линии
* использовать software prefetch, Huge Pages и прочие техники.
———
Ссылки по теме:
- Taras Tsugrii о memory-bound
- A whirlwind introduction to dataflow graphs (pointer chasing)
- Про Line Fill Buffer (LFB)
- Out-of-order execution
- Performance Analysis and Tuning on Modern CPUs
🔥23✍4❤1
Добавим немного интерактива
и проверим кто как понимает взаимодействие уровня «процесс - данные».
Дано:
* Producer-процесс, пишет данные
* Consumer-процесс, читает данные
* Оба живут на отдельных изолированных ядрах, в рамках одного сокета. То есть делят общий L3 кеш.
Producer стартовал, поднимает данные из DRAM и вносит в них изменение (store).
Сразу же после этого, Сonsumer хочет прочитать эти данные (load).
Вопросы:
👇
и проверим кто как понимает взаимодействие уровня «процесс - данные».
Дано:
* Producer-процесс, пишет данные
* Consumer-процесс, читает данные
* Оба живут на отдельных изолированных ядрах, в рамках одного сокета. То есть делят общий L3 кеш.
Producer стартовал, поднимает данные из DRAM и вносит в них изменение (store).
Сразу же после этого, Сonsumer хочет прочитать эти данные (load).
Вопросы:
👇
🔥6
about:performance
Добавим немного интерактива и проверим кто как понимает взаимодействие уровня «процесс - данные». Дано: * Producer-процесс, пишет данные * Consumer-процесс, читает данные * Оба живут на отдельных изолированных ядрах, в рамках одного сокета. То есть делят…
Please open Telegram to view this post
VIEW IN TELEGRAM
about:performance
Добавим немного интерактива и проверим кто как понимает взаимодействие уровня «процесс - данные». Дано: * Producer-процесс, пишет данные * Consumer-процесс, читает данные * Оба живут на отдельных изолированных ядрах, в рамках одного сокета. То есть делят…
Please open Telegram to view this post
VIEW IN TELEGRAM
about:performance
Добавим немного интерактива и проверим кто как понимает взаимодействие уровня «процесс - данные». Дано: * Producer-процесс, пишет данные * Consumer-процесс, читает данные * Оба живут на отдельных изолированных ядрах, в рамках одного сокета. То есть делят…
Голосовалка завершилась, спасибо всем за участие ;)
С явным отрывом первое место берёт "общий L3 cache с ~30ns latency на загрузку".
Поздравляем победителей 🥳🥳🥳
———
А теперь опишу, как я понимаю происходящее.
Если бы на дворе был, скажем, 2015 год, то Consumer действительно пошёл бы в L3 и нашёл там данные.
Во всяком случае, в серверных процессорах Intel того времени использовался так называемый inclusive L3 cache.
Кеш-линия при изменении должна была не только находиться в приватных кешах ядра (L1/L2), но и поддерживаться в синхронизированном состоянии в общем L3.
Где ее и находили бы все желающие.
———
Но начиная со Skylake-SP (2017) и по сей день используется non-inclusive L3 cache.
И дата флоу выглядит примерно так:
1. Producer загружает данные из DRAM, и они сразу попадают в приватные L2/L1 кеши. Копия в L3 при этом не создаётся.
2. Producer меняет данные (Modified), и в этот момент они находятся только в его L1/L2 кешах.
3. Consumer, живущий на другом ядре, запрашивает ту же кеш-строку и получает каскад промахов: L1 → L2 → L3.
Самих данных в L3 нет, но рядом работает Snoop Filter. Он хранит информацию в кешах каких ядер какие кеш-линии находятся.
В приватном кеше соседнего ядра мы и найдем искомые данные, потянув их напрямую в свой кеш, опять игнорируя запись в L3.
Такое событие называется HITM: линия найдена в Modified-состоянии в чужом приватном кеше.
Таким образом, данные попадают в L3 в основном при вытеснении из приватных кешей (eviction) и не синхронизируются ни при первоначальной загрузке из DRAM, ни при последующих изменениях.
———
Для контраста можно привести механизм TLB: при промахе Page Walker может дойти до DRAM, а затем сопоставление "
PSC → STLB → dTLB.
———
Теперь к длительностям:
Выходит, что ответ в ~30ns выглядит немного оптимистично, а вот в 60ns вполне ок.
Почитать тут и тут.
———
Критика и уточнения приветствуются, хорошего дня!
С явным отрывом первое место берёт "общий L3 cache с ~30ns latency на загрузку".
Поздравляем победителей 🥳🥳🥳
———
А теперь опишу, как я понимаю происходящее.
Если бы на дворе был, скажем, 2015 год, то Consumer действительно пошёл бы в L3 и нашёл там данные.
Во всяком случае, в серверных процессорах Intel того времени использовался так называемый inclusive L3 cache.
Кеш-линия при изменении должна была не только находиться в приватных кешах ядра (L1/L2), но и поддерживаться в синхронизированном состоянии в общем L3.
Где ее и находили бы все желающие.
———
Но начиная со Skylake-SP (2017) и по сей день используется non-inclusive L3 cache.
И дата флоу выглядит примерно так:
1. Producer загружает данные из DRAM, и они сразу попадают в приватные L2/L1 кеши. Копия в L3 при этом не создаётся.
2. Producer меняет данные (Modified), и в этот момент они находятся только в его L1/L2 кешах.
3. Consumer, живущий на другом ядре, запрашивает ту же кеш-строку и получает каскад промахов: L1 → L2 → L3.
Самих данных в L3 нет, но рядом работает Snoop Filter. Он хранит информацию в кешах каких ядер какие кеш-линии находятся.
В приватном кеше соседнего ядра мы и найдем искомые данные, потянув их напрямую в свой кеш, опять игнорируя запись в L3.
Такое событие называется HITM: линия найдена в Modified-состоянии в чужом приватном кеше.
Таким образом, данные попадают в L3 в основном при вытеснении из приватных кешей (eviction) и не синхронизируются ни при первоначальной загрузке из DRAM, ни при последующих изменениях.
———
Для контраста можно привести механизм TLB: при промахе Page Walker может дойти до DRAM, а затем сопоставление "
физ.адрес - вирт.адрес" каскадно заполняет все вышележащие уровни:PSC → STLB → dTLB.
———
Теперь к длительностям:
L1 hit: ~1ns
L2 hit: ~3-4ns
L3 hit: ~30-50ns
HITM: ~50-80ns
DRAM Local: ~80-100ns
DRAM Remote: 140-180ns
...
Выходит, что ответ в ~30ns выглядит немного оптимистично, а вот в 60ns вполне ок.
Почитать тут и тут.
———
Критика и уточнения приветствуются, хорошего дня!
🔥14👍8❤3😨3
Читать научные публикации это не только полезно, но бывает и чертовски сложно. Нередко закапываешься в деталях и останавливаешься по середине.
Для меня это актуальная проблема и я постоянно ищу как оптимизировать процесс обучения.
Один из фреймворков работы с white paper описан в статье A Software Engineer's Guide to Reading Research Papers.
Вкратце алгоритм, в зависимости от целей, разбивается на 4 этапа:
1. Big picture: пробегаемся по abstract + intro + conclusion.
На этом шаге будет понятно насколько исследование релевантно нашему запросу (если нет, то проходим мимо) и требуется ли погружаться подробнее. Подозреваю, что где-то тут и пролегают те самые "80 / 20"
2. Deep dive: если решили капать дальше, то читаем пропущенные раннее разделы, выписываем все что непонятно + интересные ссылки. Цели понять всё досконально на этом этапе нет, все впереди)
3. Background: гуглим что было непонятно, обсуждаем с AI, пробегаемся по ссылкам (часто хватит abstract)
4. Connect dots: возвращаемся к сложным разделам уже новым с контекстом и пониманием в голове.
Отдельно подчёркивается, что статьи зачастую являются продолжением предыдущих исследований, потому важно запастись терпением и быть готовым копнуть глубже.
Вспоминается чтение книги с кабанчиком, когда в какой-то момент понимаешь, что и без того не тоненькая книга лишь верхушка айсберга, а под водой десятки / сотни ссылок на исследования, которые надо осилить 🙂.
Делитесь своими подходами к обучению. Хорошего дня!
Для меня это актуальная проблема и я постоянно ищу как оптимизировать процесс обучения.
Один из фреймворков работы с white paper описан в статье A Software Engineer's Guide to Reading Research Papers.
Вкратце алгоритм, в зависимости от целей, разбивается на 4 этапа:
1. Big picture: пробегаемся по abstract + intro + conclusion.
На этом шаге будет понятно насколько исследование релевантно нашему запросу (если нет, то проходим мимо) и требуется ли погружаться подробнее. Подозреваю, что где-то тут и пролегают те самые "80 / 20"
2. Deep dive: если решили капать дальше, то читаем пропущенные раннее разделы, выписываем все что непонятно + интересные ссылки. Цели понять всё досконально на этом этапе нет, все впереди)
3. Background: гуглим что было непонятно, обсуждаем с AI, пробегаемся по ссылкам (часто хватит abstract)
4. Connect dots: возвращаемся к сложным разделам уже новым с контекстом и пониманием в голове.
Отдельно подчёркивается, что статьи зачастую являются продолжением предыдущих исследований, потому важно запастись терпением и быть готовым копнуть глубже.
Вспоминается чтение книги с кабанчиком, когда в какой-то момент понимаешь, что и без того не тоненькая книга лишь верхушка айсберга, а под водой десятки / сотни ссылок на исследования, которые надо осилить 🙂.
Делитесь своими подходами к обучению. Хорошего дня!
Codingconfessions
A Software Engineer's Guide to Reading Research Papers
My personal framework for reading research papers.
2👍15🔥6❤1
О бенчмаркинге, часть 1
Бенчмаркинг занимает значительную часть моей повседневной работы, поэтому важно понимать его основы, типы и ограничения.
По сути, это способ оценить производительность системы под нагрузкой.
Брендан Грегг в своё время ввёл наглядную терминологию:
* Passive Benchmarking
* Active Benchmarking
———
Passive Benchmarking простой и распространённый подход: настраиваешь окружение, запускаешь нагрузку, получаешь на выходе цифры и в дальнейшем ими руководствуешься.
Но это как раз тот случай, когда «просто» не значит «лучше».
Проблемы таких замеров:
* риск измерить не то, что планировалось изначально
* непонятно, что именно ограничивает производительность
* нельзя отличить систематическое отклонение от шума (об этом позже)
* остаемся без ответа, почему получены именно такие результаты
* сами бенчмарки могут содержать баги, что останется от нас скрыто
В итоге решения на основе таких данных могут оказаться даже хуже, чем если бы данных не было вовсе.
———
В противовес ему стоит Active Benchmarking (AB).
Помимо настройки окружения требуется активное наблюдение за системой во время прогона.
Задача понять:
* то ли мы меряем, что планировали
* что ограничивает производительность
* согласуется ли наблюдаемое поведение с нашей моделью системы
* что нужно изменить, чтобы улучшить результат
AB способен дать более надёжный результат.
Цена этого: более высокие требования к проведению эксперимента. Нужно не только уметь настроить окружение, но и верно интерпретировать наблюдаемое.
Алгоритм:
1. Собрать данные о работе системы, тулинг в помощь (perf, bcc, iostat, bpftrace, tcpdump, ...)
2. Интерпретировать, как реагирует система (методологии USE, RED, off-CPU, TSA, ...)
3. Применять в цикле:
Каждый пункт по отдельности даёт ограниченный эффект, зато вместе позволяет быстрее и увереннее продвигаться вперёд.
———
Вывод
Сырые цифры от Passive Benchmarking могут выглядеть правдоподобно и при этом вести к неверным и дорогим решениям.
Слепо доверяясь им, мы фактически надеемся, что угадали с сетапом с первого раза и учли все нюансы.
Не похоже на надёжную стратегию
Active Benchmarking напротив, позволяет избежать ловушки «бенчмаркаешь A, измеряешь B, делаешь выводы о C».
Цифры, полученные таким методом, поддаются объяснению, их можно оспорить и воспроизвести.
И на них уже можно опираться при принятии инженерных решений.
———
Что почитать
- Active Benchmarking
- CPU Benchmarks and Bad Tinder Dates
- Performance Methodologies
- Producing Wrong Data Without Doing Anything Obviously Wrong (тут может помочь заметка о чтении white paper)
To be continued...
———
Поддержать лайком на Linkedin.
Бенчмаркинг занимает значительную часть моей повседневной работы, поэтому важно понимать его основы, типы и ограничения.
По сути, это способ оценить производительность системы под нагрузкой.
Брендан Грегг в своё время ввёл наглядную терминологию:
* Passive Benchmarking
* Active Benchmarking
———
Passive Benchmarking простой и распространённый подход: настраиваешь окружение, запускаешь нагрузку, получаешь на выходе цифры и в дальнейшем ими руководствуешься.
Но это как раз тот случай, когда «просто» не значит «лучше».
Проблемы таких замеров:
* риск измерить не то, что планировалось изначально
* непонятно, что именно ограничивает производительность
* нельзя отличить систематическое отклонение от шума (об этом позже)
* остаемся без ответа, почему получены именно такие результаты
* сами бенчмарки могут содержать баги, что останется от нас скрыто
«Бенчмаркаешь A, на самом деле измеряешь B, а выводы делаешь о C». ©
В итоге решения на основе таких данных могут оказаться даже хуже, чем если бы данных не было вовсе.
———
В противовес ему стоит Active Benchmarking (AB).
Помимо настройки окружения требуется активное наблюдение за системой во время прогона.
Задача понять:
* то ли мы меряем, что планировали
* что ограничивает производительность
* согласуется ли наблюдаемое поведение с нашей моделью системы
* что нужно изменить, чтобы улучшить результат
AB способен дать более надёжный результат.
Цена этого: более высокие требования к проведению эксперимента. Нужно не только уметь настроить окружение, но и верно интерпретировать наблюдаемое.
Алгоритм:
1. Собрать данные о работе системы, тулинг в помощь (perf, bcc, iostat, bpftrace, tcpdump, ...)
2. Интерпретировать, как реагирует система (методологии USE, RED, off-CPU, TSA, ...)
3. Применять в цикле:
запустил
└─ пронаблюдал
└─ сформулировал гипотезу во что упираемся
└─ проверил в следующем прогоне
└─ повторил
Каждый пункт по отдельности даёт ограниченный эффект, зато вместе позволяет быстрее и увереннее продвигаться вперёд.
———
Вывод
Сырые цифры от Passive Benchmarking могут выглядеть правдоподобно и при этом вести к неверным и дорогим решениям.
Слепо доверяясь им, мы фактически надеемся, что угадали с сетапом с первого раза и учли все нюансы.
Не похоже на надёжную стратегию
Active Benchmarking напротив, позволяет избежать ловушки «бенчмаркаешь A, измеряешь B, делаешь выводы о C».
Цифры, полученные таким методом, поддаются объяснению, их можно оспорить и воспроизвести.
И на них уже можно опираться при принятии инженерных решений.
———
Что почитать
- Active Benchmarking
- CPU Benchmarks and Bad Tinder Dates
- Performance Methodologies
- Producing Wrong Data Without Doing Anything Obviously Wrong (тут может помочь заметка о чтении white paper)
To be continued...
———
Поддержать лайком на Linkedin.
🔥12👍6❤3
Вышла вторая часть серии про бенчмаркинг (первую ищи тут).
Ее центральная мысль: performance is a shape, not a number.
Каждый отдельный замер это всего лишь один сэмпл из полного распределения.
И чем лучше мы это распределение понимаем, тем полнее видим картину и следовательно сможем сделать более точные выводы.
В этой части:
* почему производительность это распределение, а не число
* что такое шум, откуда он берётся и каких бывает типов
* как бороться с каждым из них
* и почему борьба с ним не имеет конца
Полная версия появится здесьчерез 7 дней вот уже совсем скоро...
Ее центральная мысль: performance is a shape, not a number.
Каждый отдельный замер это всего лишь один сэмпл из полного распределения.
И чем лучше мы это распределение понимаем, тем полнее видим картину и следовательно сможем сделать более точные выводы.
В этой части:
* почему производительность это распределение, а не число
* что такое шум, откуда он берётся и каких бывает типов
* как бороться с каждым из них
* и почему борьба с ним не имеет конца
Полная версия появится здесь
🔥10❤1
Походил, подумал, не нужен в канале этот ранний доступ.
Добавил себе геморроя на ровном месте. Лучше откатить раньше, чем позже.
Поэтому реверт — возвращаюсь к исходному формату публикаций.
Добавил себе геморроя на ровном месте. Лучше откатить раньше, чем позже.
Поэтому реверт — возвращаюсь к исходному формату публикаций.
2👍18❤11🤝2💯1
О бенчмаркинге, часть 2
(первая часть тут)
Производительности в вакууме не бывает
Состояние одной и той же системы от запуска к запуску бенчмарков всегда разное. Даже если зафиксированы все статические параметры (настройки OS и рантайма, CPU affinity, etc), то температура процессоров, состояние кешей, layout кода в памяти всё равно плавают. Что уж говорить про внешне схожие окружения - там различий может быть еще больше.
Следовательно результаты на выходе всегда различаются.
Так какой из них нам взять за основу?
———
Помогает формулировка от Ben Sigelman: «Performance is a shape, not a number».
Производительность это всегда распределение, а не одно конкретное число. Не существует одного единственного, правильного значения.
И каждый отдельный прогон бенчмарка это просто один сэмпл из всего распределения, полученный при конкретных условиях.
Если углубить, то и внутри одного запуска мы всегда получаем распределение:
———
Задача перформанс-инженера сводится к двум вещам:
1. сужать это распределение
2. объяснять, почему оно именно такое
На ширину распределения влияет шум: переключение контекста, прерывания, соседи, и т.д. И чем шум больше, тем шире хвосты распределения.
Важный момент: шум может быть случайным, а может быть систематическим. Бороться с ними нужно по-разному.
———
Случайный шум (random noise) это то, что возникает непредсказуемо, без видимой системы. Например, фоновый процесс случайно попал на наше ядро в одних прогонах и не попал в других. Или соседнее ядро внезапно дало всплеск нагрузки на общий L3.
Лечится количеством прогонов: чем их больше, тем сильнее усредняем случайные отклонения, и тем точнее итоговая оценка.
Систематический шум (systematic bias), уже другое явление. Какой-то фактор стабильно сдвигает измерения в одну и туже сторону. Например IRQ, прибитый к нашему ядру, просыпается через равные промежутки. Такой тип шумов (или отклонений) не лечится количеством прогонов, из раза в раз будем получать искаженный результат.
В "Producing Wrong Data Without Doing Anything Obviously Wrong", авторы показали, что даже размер переменных окружения может вносить существенные искажения в итоговый результат.
С систематическим шумом борются наблюдением и рандомизацией факторов, см. Stabilizer.
———
Это объясняет, почему Passive Benchmarking плох, даже при тысяче прогонов: он не отличает один тип шума от другого. Active Benchmarking, помимо прочего, как раз про то, чтобы такие смещения находить.
Ранее мы говорили о цикле в Active Benchmarking: запустил → пронаблюдал → сформулировал гипотезу → проверил → повторил.
Это как раз про то, что борьба с шумом процесс бесконечный: зафиксировали частоту, получили распределение уже, в следующем прогоне в топ выходят прерывания, вынесли их с ядра, получили еще более стабильный результат.
Далее поднимают голову соседи по L3 кешу...
По своей природе это похоже на дрейфующее бутылочное горлышко в производительности: всегда что-то будет в топ-1.
Следующим возникает естественный вопрос: "где и когда остановиться?" To be continued.
———
Выводы
- Производительность это распределение, а не число
- Шум есть всегда, и работать с ним нужно исходя из его типа
- Борьба с шумом бесконечный процесс - главное знать, где остановиться.
———
Поставить лайк на Linkedin
(первая часть тут)
Производительности в вакууме не бывает
Состояние одной и той же системы от запуска к запуску бенчмарков всегда разное. Даже если зафиксированы все статические параметры (настройки OS и рантайма, CPU affinity, etc), то температура процессоров, состояние кешей, layout кода в памяти всё равно плавают. Что уж говорить про внешне схожие окружения - там различий может быть еще больше.
Следовательно результаты на выходе всегда различаются.
Так какой из них нам взять за основу?
———
Помогает формулировка от Ben Sigelman: «Performance is a shape, not a number».
Производительность это всегда распределение, а не одно конкретное число. Не существует одного единственного, правильного значения.
И каждый отдельный прогон бенчмарка это просто один сэмпл из всего распределения, полученный при конкретных условиях.
Если углубить, то и внутри одного запуска мы всегда получаем распределение:
$ funclatency-bpfcc __sys_sendto -d 10 -u
usecs : count distribution
0 -> 1 : 12433 |************ |
2 -> 3 : 20394 |******************* |
4 -> 7 : 20540 |********************|
8 -> 15 : 1962 |** |
16 -> 31 : 237 | |
32 -> 63 : 19 | |
64 -> 127 : 8500 |******** |
128 -> 255 : 12 | |
256 -> 511 : 0 | |
512 -> 1023 : 4 | |
1024 -> 2047 : 5 | |
2048 -> 4095 : 1 | |
4096 -> 8191 : 2 | |
———
Задача перформанс-инженера сводится к двум вещам:
1. сужать это распределение
2. объяснять, почему оно именно такое
На ширину распределения влияет шум: переключение контекста, прерывания, соседи, и т.д. И чем шум больше, тем шире хвосты распределения.
Важный момент: шум может быть случайным, а может быть систематическим. Бороться с ними нужно по-разному.
———
Случайный шум (random noise) это то, что возникает непредсказуемо, без видимой системы. Например, фоновый процесс случайно попал на наше ядро в одних прогонах и не попал в других. Или соседнее ядро внезапно дало всплеск нагрузки на общий L3.
Лечится количеством прогонов: чем их больше, тем сильнее усредняем случайные отклонения, и тем точнее итоговая оценка.
Систематический шум (systematic bias), уже другое явление. Какой-то фактор стабильно сдвигает измерения в одну и туже сторону. Например IRQ, прибитый к нашему ядру, просыпается через равные промежутки. Такой тип шумов (или отклонений) не лечится количеством прогонов, из раза в раз будем получать искаженный результат.
В "Producing Wrong Data Without Doing Anything Obviously Wrong", авторы показали, что даже размер переменных окружения может вносить существенные искажения в итоговый результат.
С систематическим шумом борются наблюдением и рандомизацией факторов, см. Stabilizer.
———
Это объясняет, почему Passive Benchmarking плох, даже при тысяче прогонов: он не отличает один тип шума от другого. Active Benchmarking, помимо прочего, как раз про то, чтобы такие смещения находить.
Ранее мы говорили о цикле в Active Benchmarking: запустил → пронаблюдал → сформулировал гипотезу → проверил → повторил.
Это как раз про то, что борьба с шумом процесс бесконечный: зафиксировали частоту, получили распределение уже, в следующем прогоне в топ выходят прерывания, вынесли их с ядра, получили еще более стабильный результат.
Далее поднимают голову соседи по L3 кешу...
По своей природе это похоже на дрейфующее бутылочное горлышко в производительности: всегда что-то будет в топ-1.
Следующим возникает естественный вопрос: "где и когда остановиться?" To be continued.
———
Выводы
- Производительность это распределение, а не число
- Шум есть всегда, и работать с ним нужно исходя из его типа
- Борьба с шумом бесконечный процесс - главное знать, где остановиться.
———
Поставить лайк на Linkedin
Telegram
about:performance
О бенчмаркинге, часть 1
Бенчмаркинг занимает значительную часть моей повседневной работы, поэтому важно понимать его основы, типы и ограничения.
По сути, это способ оценить производительность системы под нагрузкой.
Брендан Грегг в своё время ввёл наглядную…
Бенчмаркинг занимает значительную часть моей повседневной работы, поэтому важно понимать его основы, типы и ограничения.
По сути, это способ оценить производительность системы под нагрузкой.
Брендан Грегг в своё время ввёл наглядную…
🔥8❤6⚡3
Чего только не придумаешь в отпусках!
Например, новое название канала:
Теперь так.
Например, новое название канала:
about:performanceТеперь так.
1👍26❤3🦄1
Упрощенно нарежем типичную функцию на две части:
1. подготовительная/служебная работа: платим каждый вызов
2. полезная работа: то, ради чего тут собрались
Мы хотим, чтобы первая часть была максимально дешёвой относительно второй (кэп).
А вот что менее очевидно: объём полезной работы далеко не всегда линейно влияет на длительность функции.
———
Наглядный пример: системный вызов sendto, для отправки UDP-датаграмм.
Зависимость длительности вызова от размера payload на скрине.
Интересно, что при минимальном объеме данных в 1 байт (плюс заголовки), время работы внутри sendto ~1.8мкс.
Но если увеличить payload до 512 байт, длительность вырастает всего в 1.25x. И дальше с ростом, почти не меняется.
(цифры на других системах могут отличаться, перепроверяйте)
Бо́льшая часть времени внутри sendto уходит не на сами данные, а на сопутствующую работу: оформить UDP/IP-заголовки, прогнать через сетевой стек, поставить в очередь на отправку.
Всё это происходит вне зависимости того, один байт ты отправляешь или тысячу.
———
И дело не в sendto. Так ведут себя многие вызовы, на которых всё держится: фикса доминирует, а объём данных почти не влияет.
Такое поведение трудно угадать без знания внутренней механики — но из этих деталей и складывается грамотный дизайн системы.
Поэтому не угадывай, замеряй.
1. подготовительная/служебная работа: платим каждый вызов
2. полезная работа: то, ради чего тут собрались
Мы хотим, чтобы первая часть была максимально дешёвой относительно второй (кэп).
А вот что менее очевидно: объём полезной работы далеко не всегда линейно влияет на длительность функции.
———
Наглядный пример: системный вызов sendto, для отправки UDP-датаграмм.
Зависимость длительности вызова от размера payload на скрине.
Интересно, что при минимальном объеме данных в 1 байт (плюс заголовки), время работы внутри sendto ~1.8мкс.
Но если увеличить payload до 512 байт, длительность вырастает всего в 1.25x. И дальше с ростом, почти не меняется.
(цифры на других системах могут отличаться, перепроверяйте)
Бо́льшая часть времени внутри sendto уходит не на сами данные, а на сопутствующую работу: оформить UDP/IP-заголовки, прогнать через сетевой стек, поставить в очередь на отправку.
Всё это происходит вне зависимости того, один байт ты отправляешь или тысячу.
———
И дело не в sendto. Так ведут себя многие вызовы, на которых всё держится: фикса доминирует, а объём данных почти не влияет.
Такое поведение трудно угадать без знания внутренней механики — но из этих деталей и складывается грамотный дизайн системы.
Поэтому не угадывай, замеряй.
🔥10❤4❤🔥1
LLM про корректность, не про производительность
пока что
Недавние исследования (раз, два) подсвечивают "особенность" современных LLM:
Причина не в том, что модели "не могут" в перформанс, а в том, как их обучают и оценивают.
Основные бенчмарки (HumanEval, MBPP, SWE-bench) делают упор на корректность генерируемого кода (pass@k). Метрики эффективности (eff@k) существуют, но пока не стали стандартом.
То есть "перформанс" сейчас не входит в KPI моделей.
———
Первая работа проверяла, насколько модели способны ускорять код: найти узкое место, внести фикс, попутно ничего не сломав.
За
Таким образом, лучшая модель (GPT-5) достигла лишь 15% от ускорения, которое показал человек-эксперт.
А в среднем модели показывали результаты на уровне погрешности.
———
Во второй работе проверяли, можно ли снизить регрессию производительности промптингом. Сравнивали два подхода:
1)
2)
За 100% взят уровень регрессий без промпта. Если меньше, то промпт помог, если больше, то навредил. (скрин в комментах)
Как нетрудно догадаться, когда LLM дают примеры эффективного кода (мы же так и делаем, верно?🙂), результат стабильно лучше, чем когда мы просто просим её "хорошо подумать".
———
Среди основных слабостей LLM отмечают:
1) неверно идентифицируют узкое место
2) бросают на полпути, найдя любое ускорение
3) используют костыли вместо системных решений
4) подгоняют решение под тесты, периодически ломая корректность
———
В любом случае, на сегодня "человек с perf'ом" в руках остается важнейшим звеном в вопросах производительности.
А с растущим объёмом генерируемого кода эта роль только усиливается.
———
Хотя вопросы появляются:
1) 15% от "экспертного" уровня это много или мало? И сколько покажет обычный, пусть и опытный, разработчик?
2) Как скоро эффективность кода станет для LLM полноценным KPI?
Недавние исследования (раз, два) подсвечивают "особенность" современных LLM:
«... модели хорошо чинят баги и закрывают задачи, но это умение не переносится на оптимизацию производительности. Современные LLM заточены под корректный код, а не под быстрый.»
Причина не в том, что модели "не могут" в перформанс, а в том, как их обучают и оценивают.
Основные бенчмарки (HumanEval, MBPP, SWE-bench) делают упор на корректность генерируемого кода (pass@k). Метрики эффективности (eff@k) существуют, но пока не стали стандартом.
То есть "перформанс" сейчас не входит в KPI моделей.
———
Первая работа проверяла, насколько модели способны ускорять код: найти узкое место, внести фикс, попутно ничего не сломав.
За
1.0× брали результат человека-эксперта по кодовой базе:Top-Expert 1.000×
GPT-5 0.150×
Claude 4.1 Opus 0.098×
Qwen3 Coder Plus 0.064×
Claude 3.7 Sonnet 0.047×
Claude 4.5 Sonnet 0.041×
GLM-4.6 0.026×
GPT-5 Mini 0.019×
Kimi K2-0905 0.008×
Gemini 2.5 Flash 0.008×
DeepSeek V3.1 0.007×
Gemini 2.5 Pro 0.007×
Таким образом, лучшая модель (GPT-5) достигла лишь 15% от ускорения, которое показал человек-эксперт.
А в среднем модели показывали результаты на уровне погрешности.
———
Во второй работе проверяли, можно ли снизить регрессию производительности промптингом. Сравнивали два подхода:
1)
few-shot: даём примеры эффективного кода2)
chain-of-thought: просим рассуждать пошаговоЗа 100% взят уровень регрессий без промпта. Если меньше, то промпт помог, если больше, то навредил. (скрин в комментах)
few-shot почти всегда уводит регрессии ниже 100%, а результаты chain-of-thought неоднозначны: где-то помогает, а где-то делает только хуже.Как нетрудно догадаться, когда LLM дают примеры эффективного кода (мы же так и делаем, верно?🙂), результат стабильно лучше, чем когда мы просто просим её "хорошо подумать".
———
Среди основных слабостей LLM отмечают:
1) неверно идентифицируют узкое место
2) бросают на полпути, найдя любое ускорение
3) используют костыли вместо системных решений
4) подгоняют решение под тесты, периодически ломая корректность
———
В любом случае, на сегодня "человек с perf'ом" в руках остается важнейшим звеном в вопросах производительности.
А с растущим объёмом генерируемого кода эта роль только усиливается.
———
Хотя вопросы появляются:
1) 15% от "экспертного" уровня это много или мало? И сколько покажет обычный, пусть и опытный, разработчик?
2) Как скоро эффективность кода станет для LLM полноценным KPI?
1👍13🔥2❤1
В продолжение темы про AI.
Хочу порекомендовать канал моего знакомого – Azalio_tech
Миша – топ по Kubernetes и активно интересуется применением AI, как в работе, так и в повседневной жизни.
Про это и пишет в канал: наблюдения, полезные инструменты и опыт использования.
Так что, если тема близка, подписывайтесь!
Хороший контент стоит того, чтобы его поддерживать.
Хочу порекомендовать канал моего знакомого – Azalio_tech
Миша – топ по Kubernetes и активно интересуется применением AI, как в работе, так и в повседневной жизни.
Про это и пишет в канал: наблюдения, полезные инструменты и опыт использования.
Так что, если тема близка, подписывайтесь!
Хороший контент стоит того, чтобы его поддерживать.
Telegram
Azalio_tech
Разные заметки о kubernetes, linux и AI
https://www.linkedin.com/in/azalio/
https://www.linkedin.com/in/azalio/
🔥5❤3👎2