about:performance
1.43K subscribers
14 photos
2 files
85 links
Канал про SRE, Linux и производительность от Александра Лебедева (@alebsys).

Разбираю сбои, ускоряю системы, делюсь опытом.

🔹 Обо мне: alebsys.github.io/about
🧑‍💻 Консультации: alebsys.github.io/mentoring/
💰 Поддержать: https://boosty.to/troubleperf
Download Telegram
Не знаю как и зачем пишутся статьи на 40 минут чтения, но факт есть:

The case of the vanishing CPU: A Linux kernel debugging story

Ещё не погружался, но все ключевые слова на месте: CPU throttling, ebpf, perf, kernel bug.

Будем разбирать.
👍16👎1
В умных книгах пишут, что к анализу производительности стоит подходить в несколько этапов:

1. определить сколько времени должна занимать работа;
2. измерить сколько работа занимает на самом деле;
3. выдвинуть гипотезу почему есть различия и что фиксить;
4. внести изменения в систему с целью привести (2) к (1);
5. GOTO (2)

Если первый этап обычно стабилен и редко меняется, то основное внимание уделяется последующим шагам, при этом пункты с измерениями выглядят наиболее значимыми. И (возможно) самыми сложными.

———————————

Пример анализа потребления CPU
(очень упрощенно)

* замерь общее потребление CPU на машине (top, vmstat);
* определи долю целевого процесса и её распределение на user/system mode (top, pidstat);
* найди самые "горячие" функции/методы/системные вызовы (perf, profile);
* изучи код, чтобы понять, что именно «бьет» по производительности.

Когда узкое место найдено, принимай решение: оптимизировать логику, менять зависимости или что-то еще. И не забудь про мантры производительности.

Теперь внеси изменения и замерь их аффект.

А вот тут нас могут караулить неприятности: точно ли измерили то, что ожидали измерить?

———————————

В главе Measuring CPUs книги Understanding Software Dynamics разбирается случай измерения времени выполнения инструкции add в тактах процессора.

В качестве решения "в лоб" автор приводит:
start = RDTSC();
for (int n = 0; n < 5000; ++n) {
sum += 1;
}
delta = RDTSC() - start;


Здесь фиксируется начальное время (start), затем выполняется инкремент (add) в цикле, после чего рассчитывается разница между началом и концом операции (delta).

Разделив полученную дельту на число итераций, автор получил 6.76 тактов процессора на один проход, что довольно дорого для такой "элементарной инструкции".

(и это среднее значение, а значит, разброс по перцентилям может быть значительным)

На этом можно было бы остановиться: "померяли же!", но если копнуть глубже (куда уж глубже🙂), то окажется, что на ассемблере цикл for{} раскладывается в:
cmpl $999999999, -44(%rbp) # сравнение i с константой
jg .L3 # условный переход, если i > константа
addq $1, -40(%rbp) # sum += 1; значение sum хранится в памяти по адресу -40(%rbp)
addl $1, -44(%rbp) # ++i; значение i хранится в памяти по адресу -44(%rbp)
jmp .L4 # переход к началу цикла


Прямая речь:
[прим. alebsys: loop for{}] has five instructions, three of which access memory by three reads (cmpl, addq, addl) and two writes (addq, addl). So most of what we are measuring is in fact memory accesses, specifically to the L1 data cache.


Далее автор рассматривает способы минимизировать оверхед от цикла и выходит на почти "чистый" замер add в 1.06 такта на инструкцию.

А казалось бы "че там мерить, зашел вышел на пять минут"')

———————————

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

Например, можно думать, что измеряем задержки на сети, хотя узкое место в локальной очереди на машине.
Или радоваться быстрым дискам, хотя по факту запись шла асинхронно через файловую систему.

P.S. Товарищ, будь бдителен!
👍13🔥5👎1🎉1
CPU Isolation: исследование в шести частях о применение техник CPU Isolation для задержкочувствительных нагрузок.

Недостаточно просто выгнать все процессы, кроме целевого, с ядра с помощью cpuset и привязать его к CPU через taskset. Надо не забыть и о фоновых задачах ядра, т.н. housekeeping work.

Housekeeping work – это совокупность фоновых операций, которые ядро Linux выполняет для поддержания своей внутренней инфраструктуры. Эти задачи включают обработку таймеров, обновление системного времени, управление очередями отложенной работы (workqueues), обработку прерываний, очистку ресурсов и прочее. Несмотря на то, что они обычно незаметны для пользователя, именно эти операции обеспечивают стабильность и корректное функционирование всей системы.


Оборотной стороной housekeeping work является то, что она привносит непредсказуемые задержки (jitter), прерывая выполнение пользовательских задач.

Борьба с этими задержками и есть центральная тема цикла.
👍7🔥5👎1
Профилируя процессы в Linux хорошо бы представлять оверхед от инструментов и выбирать подходящий под задачу.

На примере работы утилиты dd сравним накладные расходы strace, perf и bpftrace (eBPF).

Для начала запустим dd без оберток:
# dd if=/dev/zero of=/dev/null bs=512 count=100k
102400+0 records in
102400+0 records out
52428800 bytes (52 MB, 50 MiB) copied, 0.0229283 s, 2.3 GB/s


Скорость в 2.3GB/s будет эталонной, с которой и будем все сравнивать.

strace

# strace -c dd if=/dev/zero of=/dev/null bs=512 count=100k
102400+0 records in
102400+0 records out
52428800 bytes (52 MB, 50 MiB) copied, 1.73851 s, 30.2 MB/s
...


Падение скорости в ~76 раз, неплохо поработали!

perf

# perf stat -e 'syscalls:sys_enter_*' dd if=/dev/zero of=/dev/null bs=512 count=100k
102400+0 records in
102400+0 records out
52428800 bytes (52 MB, 50 MiB) copied, 0.0287921 s, 1.8 GB/s


Замедление на треть уже и не выглядит чем-то страшным:)

bpftrace

# bpftrace -e 'tracepoint:syscalls:sys_enter_* /comm == "dd"/
{ @[probe] = count(); }' -c '/usr/bin/dd if=/dev/zero of=/dev/null bs=512 count=100k'
102400+0 records in
102400+0 records out
52428800 bytes (52 MB, 50 MiB) copied, 0.0475401 s, 1.1 GB/s


Хваленный eBPF дал оверхеда более чем в 2 раза! А говорили, что "eBPF это про скорость" :(

——————————————————

С strace всё ясно: он через ptrace приостанавливает dd на каждом syscall, с переходом в kernel mode и обратно.

Но в чем eBPF не справился? Давайте обсудим!

Важно помнить: dd генерирует много системных вызовов, поэтому такой большой оверхед у `strace`. Цифры выше это скорее утрированный пример, но суть отражают верно.
👍24🔥3👎1
Об IPC
(конспект по книге Performance Analysis and Tuning on Modern CPUs)

Я уже писал о показателе Instructions Per Cycle (IPC) (например тут и тут). Сейчас разберём детали глубже.

Instruction Per Cycle (IPC) — это среднее количество инструкций, завершённых за один такт процессора:

IPC = Retired Instructions / Core Cycles

Определим ключевые понятия.

Инструкции делятся на executed и retired.

- Executed инструкции уже выполнены, но результат ещё не записан в память. Они могут выполняться вне порядка (out of order) и быть отменены, например, из-за miss branch prediction;

- Retired инструкции полностью завершены, то есть и выполнены и их результаты записаны (committed). Отменить их уже нельзя.

Executed напрямую не отслеживаются, а для retired есть отдельный счётчик:
perf stat -e instructions -- ./a.exe

2173414 instructions # 0.80 insn per cycle


Cycles (циклы) процессора бывают двух видов:
- core;
- reference.

Разница важна при динамическом изменении частоты процессора:
1. если CPU работает на штатной частоте: core = reference.
2. если CPU разогнан: core > reference.

Core Cycles отражают реальную текущую, когда Reference Cycles базовую (по паспорту) частоту процессора.
perf stat -e cycles,ref-cycles -- ./a.exe

43340884632 cycles # 3.97 GHz <= Core Cycles
37028245322 ref-cycles # 3.39 GHz <= Reference Cycles


Следовательно IPC показывает единицу A) выполненной B) полезной работы в текущий момент.

IPC не зависит от изменения тактовой частоты, так как всегда рассчитывается на один цикл.


Факторы, ограничивающие IPC
(перечислены в случайном порядке, список неполный):

- скорость памяти и cache misses;
- архитектура процессора: скалярность, загрузка слотов пайплайна;
- тип и сложность инструкций;
- branch misprediction (пенальти на ошибку по 10–25 ns);
- ...

Скалярность ограничивает количество инструкций, которые процессор может обработать за один такт, и задаёт теоретический максимум IPC.

На практике этот максимум недостижим: процессор может одновременно выполнять только определённые типы инструкций. Например, в 6-wide архитектуре за такт можно провести четыре операции сложения/вычитания, одну загрузку и одну запись, но не шесть загрузок одновременно.

to be continued...

#cpu #theory
👍18🔥10👎1
Продолжаем!
1👍13🔥8👎1
Perf Weekly | №1
нструменты, системы и производительность)

Troubleshooting Slow TCP Transfers: A Stack-Level Approach

Большинство проблем в production решаются без сложных инструментов. Обычно достаточно стандартных утилит, доступных из любого дистрибутива. Главное понимать, как и где их использовать. Например ss и nstat.

Why strong engineers are rarely blocked
Заблокированными могут быть не только треды, но и инженеры. По ссылке о важности менеджмента своих активностей, чтобы двигаться быстрее.

Can We Know Whether a Profiler is Accurate?
Профайлеры, как и любой инструмент, имеют погрешность измерений. В статье разбирается любопытный способ оценивать их точность через контролируемое замедление программы.
1🔥15👍9
Perf Weekly | №2
нструменты, системы и производительность)

Good Performance for Bad Days
Часто перф анализ проводят при нормальной/обычной нагрузке (задержки низкие, пропускная способность высокая, поведение предсказуемо).

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

Introduce tseries() For Capturing Time Series Data #3838
В v0.24.0 версии bpftrace наряду с привычными hist / lhist появился ещё один способ визуализации: tseries.

Режим позволяет увидеть, как значения меняются во времени. И это уже не просто циферки в терминале, а настоящие time series!

NUMA Cost & Performance
Статья о том, почему многосокетные (NUMA) серверы часто оказываются дороже, сложнее в настройке и зачастую медленнее, чем односокетные системы, и что с этим делать.
👍9🔥81
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👍247
Когда мы приступаем к оптимизации системы (код, инфраструктура и т.п.), важно не торопиться и правильно наметить точки приложения усилий.

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

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

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

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

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

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

——

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

Поэтому цикл поиск узкого местаоптимизацияпоиск узкого места можно повторять бесконечно 🙂
1🔥236
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
🔥2241
Добавим немного интерактива
и проверим кто как понимает взаимодействие уровня «процесс - данные».

Дано:
* 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