about:performance
1.48K subscribers
14 photos
2 files
86 links
Канал о performance engineering от Александра Лебедева (@alebsys).

Low latency, Linux, Observability.

▸ Обо мне: alebsys.github.io
▸ Консультации: alebsys.github.io/consulting
Download Telegram
Perf Weekly | №3
нструменты, системы и производительность)

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: как учиться, на чем фокусироваться, типичные ловушки и куча полезных ссылок.

А возникшие вопросы можно обсудить в треде;)
1🔥12👍62
Замечали "странные" проценты в скобках в выводе 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.
🔥172
Про L3 Cache (LLC)

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 тут.
1🔥2443👍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: тут.
2👍258
Когда мы приступаем к оптимизации системы (код, инфраструктура и т.п.), важно не торопиться и правильно наметить точки приложения усилий.

Промахнувшись с выбором, можно существенно ускорить отдельную часть системы, но почти не повлиять на общее время работы.

Эту идею хорошо иллюстрирует закон Амдала: итоговый выигрыш ограничен долей времени, которую занимал целевой участок.

Пример.
Общее время работы 100 мс, состоит из четырёх этапов:
A - 15 мс
Б - 5 мс
В - 10 мс
Д - 70 мс

Если ускорить Б в космические 10×, система в целом станет быстрее всего на 4.5%, тогда как оптимизация Д в 2 раза даст уже около 54% выигрыша.

И это при том, что затраченные усилия могли быть сопоставимы.

Поэтому начинать стоит с поиска того, что реально ограничивает систему. Иначе легко поддаться соблазну оптимизировать то, что просто лучше знаешь и в итоге потратить время впустую.

——

На эту тему было интересное обсуждение на LinkedIn, где подметили, что бутылочное горлышко системы не статично и со временем может менять форму и местоположение.

Поэтому цикл поиск узкого местаоптимизацияпоиск узкого места можно повторять бесконечно 🙂
1🔥246
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 как раз золотая середина.
🔥10👍1
Как CPU читает данные из памяти и о чем тут стоит подумать

Допустим, у нас есть указатель на счетчик (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
🔥2341
Добавим немного интерактива
и проверим кто как понимает взаимодействие уровня «процесс - данные».

Дано:
* Producer-процесс, пишет данные
* Consumer-процесс, читает данные
* Оба живут на отдельных изолированных ядрах, в рамках одного сокета. То есть делят общий L3 кеш.

Producer стартовал, поднимает данные из DRAM и вносит в них изменение (store).
Сразу же после этого, Сonsumer хочет прочитать эти данные (load). 

Вопросы:
👇
🔥6
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.

———

Теперь к длительностям:
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👍83😨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: возвращаемся к сложным разделам уже новым с контекстом и пониманием в голове.

Отдельно подчёркивается, что статьи зачастую являются продолжением предыдущих исследований, потому важно запастись терпением и быть готовым копнуть глубже.

Вспоминается чтение книги с кабанчиком, когда в какой-то момент понимаешь, что и без того не тоненькая книга лишь верхушка айсберга, а под водой десятки / сотни ссылок на исследования, которые надо осилить 🙂.

Делитесь своими подходами к обучению. Хорошего дня!
2👍15🔥61
О бенчмаркинге, часть 1

Бенчмаркинг занимает значительную часть моей повседневной работы, поэтому важно понимать его основы, типы и ограничения.

По сути, это способ оценить производительность системы под нагрузкой.

Брендан Грегг в своё время ввёл наглядную терминологию: 
* 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👍63
Вышла вторая часть серии про бенчмаркинг (первую ищи тут).

Ее центральная мысль: performance is a shape, not a number.

Каждый отдельный замер это всего лишь один сэмпл из полного распределения.

И чем лучше мы это распределение понимаем, тем полнее видим картину и следовательно сможем сделать более точные выводы.

В этой части:
* почему производительность это распределение, а не число
* что такое шум, откуда он берётся и каких бывает типов
* как бороться с каждым из них
* и почему борьба с ним не имеет конца

Полная версия появится здесь через 7 дней вот уже совсем скоро...
🔥101
Походил, подумал, не нужен в канале этот ранний доступ.

Добавил себе геморроя на ровном месте. Лучше откатить раньше, чем позже.

Поэтому реверт — возвращаюсь к исходному формату публикаций.
2👍1811🤝2💯1
О бенчмаркинге, часть 2

(первая часть тут)

Производительности в вакууме не бывает

Состояние одной и той же системы от запуска к запуску бенчмарков всегда разное. Даже если зафиксированы все статические параметры (настройки 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
🔥863
Channel name was changed to «about:performance»
Чего только не придумаешь в отпусках!

Например, новое название канала: about:performance

Теперь так.
1👍263🦄1
Упрощенно нарежем типичную функцию на две части:

1. подготовительная/служебная работа: платим каждый вызов
2. полезная работа: то, ради чего тут собрались

Мы хотим, чтобы первая часть была максимально дешёвой относительно второй (кэп).

А вот что менее очевидно: объём полезной работы далеко не всегда линейно влияет на длительность функции.

———

Наглядный пример: системный вызов sendto, для отправки UDP-датаграмм.

Зависимость длительности вызова от размера payload на скрине.

Интересно, что при минимальном объеме данных в 1 байт (плюс заголовки), время работы внутри sendto ~1.8мкс.

Но если увеличить payload до 512 байт, длительность вырастает всего в 1.25x. И дальше с ростом, почти не меняется. 

(цифры на других системах могут отличаться, перепроверяйте)

Бо́льшая часть времени внутри sendto уходит не на сами данные, а на сопутствующую работу: оформить UDP/IP-заголовки, прогнать через сетевой стек, поставить в очередь на отправку.

Всё это происходит вне зависимости того, один байт ты отправляешь или тысячу.

———

И дело не в sendto. Так ведут себя многие вызовы, на которых всё держится: фикса доминирует, а объём данных почти не влияет.

Такое поведение трудно угадать без знания внутренней механики — но из этих деталей и складывается грамотный дизайн системы.

Поэтому не угадывай, замеряй.
🔥104❤‍🔥1
LLM про корректность, не про производительность

пока что

Недавние исследования (раз, два) подсвечивают "особенность" современных 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🔥21
В продолжение темы про AI.

Хочу порекомендовать канал моего знакомого – Azalio_tech

Миша – топ по Kubernetes и активно интересуется применением AI, как в работе, так и в повседневной жизни.

Про это и пишет в канал: наблюдения, полезные инструменты и опыт использования.

Так что, если тема близка, подписывайтесь!

Хороший контент стоит того, чтобы его поддерживать.
🔥53👎2