12 KiB
Lab048: профилирование и ускорение автономного BPSK-приёмника
Состояние
Lab048 завершена успешно только на сохранённой аппаратной RX-записи Lab047.
Новый радиосеанс не выполнялся: RF TX = 0, HackRF не запускался. Научные
критерии обнаружения и принятия кадра, BPSK, PRBS7, PHY, FEC, CRC, формат
пакета и порог 0.030964473262429237 не изменены.
Итоговая обработка 15,0 с эфира занимает 13,966058 с. Это в 2,679633 раза быстрее исходных 37,423910 с и впервые быстрее длительности самой записи: отношение времени обработки к времени эфира равно 0,931071.
Исходная контрольная точка
Использована та же запись:
C:\Users\user\Documents\SDR_Rover_Lab047_raw\20260903_115747\rx_stream.cs8.
Размер — 240000000 байт, 120000000 комплексных отсчётов, длительность 15,0 с,
SHA-256 8521bf1b574cd1fe4e0a4f980c00c8664f8bb51551a241e46449e1eb2c77e635.
Принятые до оптимизации метрики Lab047:
- полное время: 37,423910 с;
- строгая ступень: 32,459969 с;
- среднее время строгой проверки: 0,470434 с;
- строгих кандидатов: 69;
- приблизительный пик памяти: 1545755352 байта, около 1,44 ГиБ;
- действительных кадров: 36;
- CRC PASS/FAIL:
36/0; - полный JPEG, SHA-256 совпадает, ошибок байтов и битов нет.
Базовое профилирование
До изменения кода выполнен один полный проход под cProfile. Он сохранил
185828 локальных максимумов, 32414 кандидатов первого каскада, 69 строгих
проверок, 36 действительных кадров, CRC PASS/FAIL 36/0 и точный JPEG.
Накладные расходы профилировщика увеличили полное время до 45,947287 с,
поэтому коэффициент ускорения считается относительно принятого измерения без
профилировщика 37,423910 с.
Десять функций по суммарному времени:
| Функция | Вызовы | Суммарно, с |
|---|---|---|
_run_with_fixed_candidate_rule |
1 | 45,945 |
stream_process_capture |
1 | 45,930 |
decode_candidate_from_memmap |
69 | 40,912 |
_extract_soft_window |
105 | 25,697 |
_matched_waveform |
105 | 21,199 |
fftconvolve |
252 | 13,660 |
_freq_domain_conv |
252 | 12,811 |
__ua_function__ FFT |
756 | 11,889 |
root_raised_cosine_taps |
175 | 11,885 |
_execute_nD FFT |
756 | 11,881 |
Десять функций по собственному времени:
| Функция | Вызовы | Собственное время, с |
|---|---|---|
scipy.fft c2c |
756 | 11,222 |
_matched_waveform |
105 | 5,704 |
numpy.isclose |
1400175 | 5,407 |
upfirdn.apply_filter |
9 | 3,171 |
_timing_offsets |
105 | 2,859 |
root_raised_cosine_taps |
175 | 2,306 |
_scale_and_carrier |
70 | 1,801 |
генератор numpy.isclose |
7000875 | 1,160 |
_cs8_window |
113 | 1,105 |
_freq_domain_conv |
252 | 0,917 |
Профиль показал повторное формирование одной опорной преамбулы 70 раз и одинаковых RRC-коэффициентов 175 раз. Согласованный фильтр вызывался 105 раз: 69 коротких окон для PHY и 36 полных окон. Временная синхронизация выполняла 1601 Python-итерацию для каждого окна. Витерби занимал лишь 0,875 с на все 36 кадров, CFO/фаза — 0,054 с на 105 вызовов, PHY-заголовок — 0,006 с. Поэтому FEC, CFO, фаза, PHY и CRC не оптимизировались.
Выполненные оптимизации
Изменены только вычислительные детали приёмника:
- Опорная PRBS7-преамбула формируется один раз, кэшируется и защищается от записи.
- RRC-коэффициенты формируются один раз, хранятся как неизменяемый
float32. - Та же полная линейная RRC-свёртка вычисляется методом overlap-add
(
oaconvolve) вместо единой FFT (fftconvolve). - Неизменяемые векторы переноса несущей кэшируются по началу и длине окна; кэш ограничен четырьмя записями.
- Рабочие массивы согласованного фильтра переведены с
complex128наcomplex64. Максимальное абсолютное расхождение с прежним фильтром на проверочном шумовом массиве равно1,263e-6, менее5e-7от пика. - Преамбульная временная синхронизация считает те же 1601 нормированные оценки группами по 400 фазам и не вычисляет второй идентичный набор оценок.
Прямой тест прежнего fftconvolve/complex128 и нового
oaconvolve/complex64 требует абсолютное расхождение не более 2e-6 и
отношение максимального расхождения к пику менее 5e-7. Отдельные тесты
подтверждают точность и неизменяемость кэшей, ограничение их размера, тот же
timing offset и отсутствие TX/expected-frame параметров у независимого API.
Существующие сквозные тесты защищают те же PHY, FEC, CRC и полезные данные.
Единственный итоговый полный проход
После программного шлюза выполнен один итоговый полный проход той же записи. Первый вызов нового командного файла завершился до импорта проекта из-за отсутствующего загрузочного пути: RX не открывался и файл-свидетель не был создан. После исправления загрузочного блока выполнен ровно один фактический полный проход; автоматического повтора нет.
Независимая стадия не получала ожидаемое число кадров, TX offsets, номера
TX-фрагментов, переданный JPEG, пакеты или кодированные биты. Только после
completed = true и OBJECT COMPLETE = YES выполнена диагностическая сверка
с TX.
Результат:
- полное время: 13,966058 с;
- грубый поиск: 4,776677 с;
- строгая ступень: 9,171629 с;
- среднее время строгой проверки: 0,132922 с;
- ускорение полного прохода: 2,679633 раза;
- отношение обработки к длительности эфира: 0,931071;
REAL-TIME CAPABLE = YES;- локальных максимумов: 185828;
- кандидатов первого каскада: 32414;
- строгих проверок: 69;
- пропущено внутри уже принятых областей: 32345;
- точных дубликатов: 0;
- PHY-заголовок PASS/FAIL:
36/33; - FEC attempts/PASS/FAIL:
36/36/0; - CRC PASS/FAIL:
36/0; - действительных кадров и фрагментов:
36/36; - отсутствующих фрагментов: 0;
OBJECT COMPLETE = YES.
Только после независимого вердикта подтверждены 36/36 переданных кадров,
19 ошибок кодированных бит до FEC, 0 ошибок протокольных бит после FEC и 0
ложных CRC PASS. Восстановленный JPEG имеет 8724 байта, RGB 320 x 240,
декодируется Pillow и имеет SHA-256
e913dab9ac32795250880a4be67fbbbf89a4f96536842476ab591d1acaafc02a.
SHA-256 переданного файла совпадает; ошибочных байтов 0, ошибочных битов 0.
Время стадий после оптимизации
Измерения вложенных функций пересекаются по времени:
| Стадия | Вызовы | Суммарно, с |
|---|---|---|
преобразование CS8 в complex64 |
113 | 1,487798 |
| уточнение начала | 69 | 3,121533 |
| согласованный фильтр | 105 | 3,282575 |
| временная синхронизация | 105 | 1,270783 |
| CFO, фаза и мягкие решения | 105 | 0,042236 |
| PHY-заголовок | 105 | 0,004111 |
| FEC, разбор пакета и CRC | 36 | 0,645559 |
| Витерби | 36 | 0,627109 |
Следующее узкое место верхнего уровня — грубый поиск, 4,776677 с. Внутри строгой ступени сопоставимы согласованный фильтр, 3,282575 с, и уточнение начала, 3,121533 с. Запас реального времени пока мал: около 1,03 с на 15 с эфира.
Память
Консервативная пиковая оценка осталась 1545755352 байта, около 1,44 ГиБ, поскольку максимум создаёт неизменённая обработка 16-миллионного блока грубого поиска. Крупнейшие явно управляемые массивы:
| Назначение | dtype | shape | MiB |
|---|---|---|---|
| окно грубого поиска | complex64 |
16000000 | 122,070 |
| прореженный поток | complex64 |
800000 | 6,104 |
| вектор переноса грубого поиска | complex64 |
800000 | 6,104 |
| оценки корреляции | float32 |
773561 | 2,951 |
| полное строгое окно | complex64 |
2306400 | 17,596 |
| кэшированный вектор несущей | complex64 |
2306400 | 17,596 |
| основная полоса строгого окна | complex64 |
2306400 | 17,596 |
| выход согласованного фильтра | complex64 |
2310400 | 17,627 |
| RRC-коэффициенты | float32 |
4001 | 0,015 |
Переход строгой ступени с complex128 на complex64 уменьшил её рабочие
массивы, но не общий пик всего процесса. Уменьшение блока грубого поиска в
Lab048 не выполнялось, чтобы не менять блочные границы и результаты детектора.
Артефакты
experiments/lab048_receiver_performance.py;tests/test_lab048_receiver_performance.py;data/processed/lab048/final_processing_attempt.json;data/processed/lab048/result.json;data/processed/lab048/recovered_image.jpg.
Файл cProfile сохранён только как временный локальный профиль вне проекта:
C:\Users\user\AppData\Local\Temp\lab048_baseline_20260903_6fb7e4e.pstats.
Проверки
- связанные тесты после поэтапных изменений: 169 passed;
python tools/quick_gate.py: 547 тестов и 11 функциональных проверок, PASS;python -m pytest tests/ -q: 547 passed, 0 failed;git diff --check: PASS.
Следующий этап
STOP. Не переходить к Lab049, видео, QPSK, повышению символьной скорости или
новому RF-сеансу без отдельной постановки. RF TX = 0; HackRF не запускать.