Files
ewsdr/doc/PERF_PROFILING.md
T
ew8bakandClaude Fable 5 0adaff2d21 perf(spectrum): full-raw CPU renderer + dword blenders (frame 8.3ms -> 1.7ms)
Canvas на битмапе кадра больше не используется вообще (кроме редкого
ADC-overload пути с восстановлением альфы) — убраны все raw<->canvas
переходы, каждый из которых синкал весь битмап на Qt6 (~2мс):

- RawBegin/RawEnd: прямой доступ к пикселям через RawImage.Data +
  BytesPerLine (bottom-up DIB win32 учтён отрицательным шагом)
- RawVLine/RawHLine (с пунктиром), RawFillRect, RawTriangleDown,
  RawCurve (кривая = связные вертикальные сегменты по колонкам)
- кэш текстовых масок (TRawTextMask, LRU 48): строка рендерится canvas'ом
  один раз белым-на-чёрном в свой мини-битмап, блитится raw с цветом;
  все тексты кадра теперь Courier New
- конвертированы: маркер (метка без случайной цветной плашки), бикон-
  маркеры, буквы слайсов, слайс-линии, AGC (пунктир 4/2 и 1/2, подпись
  с фоном SpecFilter как раньше), края/VFO/TX-курсоры с треугольниками
- пофреймовый альфа-OR проход удалён: альфа чинится один раз в кэше
  СЕТКИ после ребилда (canvas-текст там), кадр наследует её через memcpy

Дворд-блендеры (два 16-бит лейна на умножение, /256 вместо /255,
расхождение <0.4%) вместо побайтовых с div 255:
- BandPlanOverlay.CompositeStrip (~1мс -> ~0.2мс)
- SampleRateOverlay.CompositeCache (per-px альфа)
- VfoOverlay.BlendBitmapKey (keyed-магента дворд-сравнением)
- SpectrumView.DrawSpectrumGradient (source-лейны 1 раз на строку,
  ~1.6мс -> ~0.8мс)

Порог перестройки бэндплана: полпикселя вместо 1 Гц (на QO-100 с
decoder-lock центр подтюнивается непрерывно — был canvas-ребилд полоски
каждый кадр). Композиты внутри raw-лока (их BeginUpdate вложенные).
PerfLog: зона ui.composites отделена от ui.spec_alpha (=RawEnd).

Замер на эфире (Pluto, 576k, 52fps): кадр 8.3мс -> 1.67мс (5x),
со слайсом 2.1-2.3мс; UI-поток ~45% -> ~26% ядра, из них 15% — блиты
paint_spectrum/paint_waterfall (потолок Qt6-вывода). Визуально проверено.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 00:11:31 +03:00

86 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CPU-профилирование (ветка perf/cpu-profiling)
Временная инструментация для поиска самых дорогих по CPU мест.
Модуль `PerfLog.pas` + зонные замеры, помеченные комментарием `// PERF`.
**Не мержить в main** — после выводов ветка выбрасывается или чистится.
## Как запустить
```sh
lazbuild -B --ws=qt6 ewsdr.lpr
./bin/x86_64-linux/ewsdr-debug # отчёт в stderr каждые 5 с
EWSDR_PERF_FILE=/tmp/ewsdr-perf.log ./bin/x86_64-linux/ewsdr-debug # + в файл
EWSDR_PERF=0 ./bin/x86_64-linux/ewsdr-debug # выключить замеры
```
## Как читать отчёт
```
[PERF] ===== interval 5.00 s =====
[PERF] zone calls c/s avg_us max_us total_ms core%
[PERF] dsp.push_iq 2929 586 55.1 412 161.4 3.2
```
`core%` — доля одного ядра CPU, потраченная в зоне за интервал. Это главная
колонка: сортируем по ней и смотрим, куда уходит время. `max_us` показывает
выбросы (джиттер), `avg_us` — стоимость одного вызова.
Зоны вложены: `dsp.push_iq` включает `dsp.spectrum0` + `dsp.fex_main` +
`dsp.audio_cb` + `dsp.slices`; `dsp.slices` включает `dsp.slice_fex` и
`audio.write` слайсов; `ui.spectrum_tick` включает `ui.draw_*`, `ui.slice_flags`,
`ui.vfo_overlay`, `ui.web_push`; `dsp.tx_block` включает `pluto.tx_resamp`
(SendDUCIQ зовётся из FOnTXIQ). Разница «родитель минус дети» = ненаблюдаемый
остаток (циклы копирования, мелочь).
Бэкенды: на openHPSDR работает `net.*`, на Pluto вместо них `pluto.*`
(RX-поток Pluto конвертит iio-буфер и кладёт в ту же DSP-очередь).
## Карта зон
| Зона | Поток | Что меряет |
|---|---|---|
| net.ddc_iq | receive | копия IQ-пакета + постановка в DSP-очередь (openHPSDR) |
| net.wideband | receive | сборка wideband-кадра (openHPSDR) |
| pluto.rx_conv | Pluto RX | конверсия iio-буфера int16→24bit BE + отдача в DSP-очередь |
| pluto.tx_resamp | TX DSP | полифазный интерполятор 192k→device-rate + 24→16 + FIFO |
| pluto.tx_fill | Pluto TX | наполнение iio-буфера из FIFO (без блокирующего push) |
| dsp.push_iq | DSP | весь путь пакета: декод 24-бит, аккумуляция, всё ниже |
| dsp.spectrum0 | DSP | скармливание display-блока анализатору WDSP |
| dsp.fex_main | DSP | fexchange0 главного RX-канала |
| dsp.audio_cb | DSP | маршрут RX-аудио (web/радио/звуковуха) |
| dsp.slices | DSP | fan-out всех слайсов целиком |
| dsp.slice_fex | DSP | fexchange0 одного слайс-канала |
| dsp.tx_block | TX DSP | TX-блок целиком |
| disp.get_pixels | display | GetPixels спектр+водопад |
| audio.write | DSP | запись в PortAudio ring (главный + все слайсы) |
| ui.spectrum_tick | UI | кадровый таймер целиком |
| ui.meter_tick | UI | 10 Гц метр-таймер |
| ui.draw_spectrum / ui.draw_waterfall | UI | CPU-рендер кадра в битмап |
| ui.paint_spectrum / ui.paint_waterfall | UI | блит на канву (Qt6) или GL-кадр со SwapBuffers |
| ui.slice_flags | UI | позиционирование + S-метры слайс-флагов |
| ui.vfo_overlay | UI | обновление главного VFO-флага |
| ui.wideband_paint | UI | кадр wideband-панорамы |
| ui.web_push | UI | WebAdapter.PushState |
| ui.spec_grid…spec_alpha | UI | разбивка ui.draw_spectrum (full-raw рендер): grid=ребилд+memcpy сетки, bands=raw-заливки полос, curve=точки+градиент, overlays=raw-линии/текст/кривая/маркеры, composites=бэндплан/панель/флаги, alpha=RawEnd/EndUpdate (+редкий canvas-путь ADC overload) |
| ui.gl_swap | UI | SwapBuffers отдельно — включает ожидание vsync, НЕ чистый CPU |
## Чего зоны НЕ видят
- Общесистемное: Qt event loop, композитор, PortAudio callback-поток
(сам колбэк не инструментирован — он читает ring без работы).
- Для сравнения с общим CPU процесса: `pidstat -p $(pidof ewsdr-debug) 5 -t`
(покажет и раскладку по потокам — сопоставляется с зонами по потокам).
## Методика измерений
Снять отчёты в фиксированных сценариях по ~30 с каждый и сравнить core%:
1. RX, 1 слайс (только VFO A), спектр CPU-рендер.
2. То же + OpenGL (`ewsdr-debug --opengl` или как включается GL-свитч).
3. RX + 34 слайса (Ctrl+ЛКМ) — рост dsp.slices / dsp.slice_fex / audio.write.
4. Разные FPS дисплея и размеры FFT.
5. Открытый web-клиент (ui.web_push, dsp.audio_cb с Opus).
6. Wideband включён/выключен (openHPSDR).
7. Pluto: разные sample rate (576k/1536k/3072k) — pluto.rx_conv и dsp.push_iq
растут линейно с рейтом; TX (MOX/TUN) — pluto.tx_resamp vs dsp.tx_block.