awesome-everything EN
↑ Обратно к восхождению

Производительность

Горячие пути: чтение кода и счётчиков

Суть Читай реальные Go-сниппеты, блок perf stat и горячий путь N-API; предскажи форму hotspot'а по уликам перед тобой и выбери фикс с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Форма hotspot’а живёт в коде и счётчиках, а не в ширине фрейма на flame graph. Читай каждый сниппет, классифицируй его в одну из форм юнита и выбирай фикс, который senior-инженер сделал бы первым — прежде чем трогать ручку тюнинга или библиотеку.

Цель

Отработай цикл, который ты запускаешь в каждом инциденте: прочитай горячий путь, реши, какая это форма, по уликам перед тобой (код, perf stat, профиль), и тянись к подходящему семейству фиксов вместо угадывания.

Сниппет 1 — построение по строкам

func renderCSV(rows []Row) string {
    out := ""                                 // пустая строка
    for _, r := range rows {
        out += fmt.Sprintf("%d,%s\n", r.ID, r.Name)  // Sprintf + конкатенация на каждую строку
    }
    return out
}
Викторина

renderCSV показывает 30% self-time на 200k строк, и рядом широки GC-фреймы (mallocgc). Какая это форма и каков единственный фикс с наибольшим рычагом?

Сниппет 2 — общая линия счётчиков

type Stats struct {
    hits   uint64
    misses uint64   // соседнее поле — та же 64-байтная кеш-линия, что и hits
}
var s Stats

// горутина A, горячий цикл:   atomic.AddUint64(&s.hits, 1)
// горутина B, горячий цикл:   atomic.AddUint64(&s.misses, 1)
Викторина

Под нагрузкой оба atomic add показываются широко в CPU-профиле с IPC ~0.4 и cache-miss rate 70%+, и хуже с ростом числа ядер. Что происходит и каков фикс?

Сниппет 3 — блок perf stat

# perf stat -e cycles,instructions,cache-misses,LLC-load-misses ./svc --bench score
   8,400,000,000  cycles
   3,360,000,000  instructions     #  0.40 insns per cycle (IPC)
     900,000,000  cache-misses     # 10.7% от всех обращений к памяти
     700,000,000  LLC-load-misses  # 78% cache miss'ов также мимо L3 → DRAM
# горячий лист с flame graph: score_embeddings()  — 42% self-time
Викторина

Читая этот блок perf stat для score_embeddings, какое утверждение верно?

Сниппет 4 — нативный мост

// Node-сервис, вызывается ~10 000 раз/секунду
function hashAll(items) {
  return items.map(item => nativeHmac(item))  // одно пересечение N-API на элемент
}
// nativeHmac (Rust): ~40 нс реальной работы
// накладные N-API stub: ~160 нс на пересечение
// CPU-профиль: 40% в napi_call_function, 8% в Rust HMAC
Викторина

Нативный HMAC быстр (40 нс), но N-API stub (160 нс) доминирует в CPU-профиле. Каков диагноз и правильный фикс?

Итог

Каждая форма читается по уликам перед тобой, а не только по ширине фрейма: широкие GC-фреймы + Sprintf/конкатенация на итерацию — это allocation-bound (строй в преаллоцированный буфер); IPC ~0.4 с высоким miss rate плюс ухудшение с ядрами на соседних atomic-полях — это false sharing (разнеси на разные кеш-линии); IPC 0.4 с DRAM-доминирующими miss’ами — это memory-bound (раскладка данных, AoS→SoA); а stub моста шире своего дешёвого нативного callee — это накладные FFI (батчь на пересечение). Сначала классифицируй, чини подходящую причину, затем перепрофилируй для подтверждения.

Продолжить восхождение ↑Горячие пути: диагностируй и почини две формы
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.