Ответ
Один из самых интересных проектов — это разработка высокопроизводительного трейдингового движка (market data processor) на C++ для фондовой биржи. Задача заключалась в обработке потоков рыночных данных (order book updates) с латентностью менее 100 микросекунд.
Мой вклад и решенные технические задачи:
-
Оптимизация обработки сообщений: Мы отказались от динамических аллокаций в «горячем» пути. Использовали кольцевые буферы (ring buffers) на основе предварительно выделенных массивов и атомарных операций для обмена данными между потоками (producer-consumer pattern).
template<typename T, size_t Size> class SPSCRingBuffer { std::array<T, Size> buffer_{}; std::atomic<size_t> head_{0}, tail_{0}; public: bool try_push(T&& item) noexcept { /* ... lock-free enqueue */ } bool try_pop(T& item) noexcept { /* ... lock-free dequeue */ } }; -
Снижение латентности сети: Для приема UDP-мультикаст-потоков использовали
boost::asioв режиме низкой латентности, настройку сокетов (SO_RCVBUF) и привязку потоков к конкретным CPU-ядрам (pthread_setaffinity_np) для минимизации переключений контекста и кэш-промахов. -
Профилирование и оптимизация: Активно использовали
perf,Intel VTuneиValgrind/Callgrindдля поиска узких мест. Одной из ключевых оптимизаций была замена полиморфных вызовов виртуальных функций на шаблонный CRTP (Curiously Recurring Template Pattern) для статического полиморфизма, что устранило затраты на косвенные вызовы и улучшило предсказание ветвлений.
Результат: Нам удалось достичь стабильной латентности обработки в ~85 микросекунд на 99-м перцентиле, что соответствовало жестким требованиям заказчика. Проект был сложным, но дал бесценный опыт в low-level оптимизациях, многопоточности и работе с реальным hardware.
Ответ 18+ 🔞
А, слушай, вот это проект был — просто ёперный театр! Разрабатывали мы трейдинговый движок, эту самую штуку, которая рыночные данные жрёт как не в себя. Задача — обрабатывать обновления стакана задержкой меньше 100 микросекунд. Представляешь? Моргнуть не успеешь, а у нас уже всё должно быть посчитано. Волнение ебать!
Чем я там занимался и какие костыли пришлось выпиливать:
-
Оптимизация обработки сообщений: Первое, что сделали — выкинули нахуй все динамические аллокации на горячем пути. Ну серьёзно,
new/deleteв таком месте — это чистая смерть. Взяли кольцевые буферы на заранее выделенных массивах и атомарные операции, чтобы потоки друг другу не мешали. Паттерн producer-consumer, классика, но доведённая до ума.template<typename T, size_t Size> class SPSCRingBuffer { std::array<T, Size> buffer_{}; std::atomic<size_t> head_{0}, tail_{0}; public: bool try_push(T&& item) noexcept { /* ... lock-free enqueue */ } bool try_pop(T& item) noexcept { /* ... lock-free dequeue */ } }; -
Снижение сетевой задержки: Данные летели к нам по UDP-мультикасту. Использовали
boost::asio, но не просто так, а с кучей танцев с бубном: настраивали размеры буферов сокетов (SO_RCVBUF), привязывали каждый рабочий поток к своему CPU-ядру, чтобы он там сидел как царь и не шастал между ядрами. Кэш-промахи — это наш главный враг, хуй с горы! -
Профилирование и ещё раз профилирование: Без этого — никуда. Гоняли код через
perf,VTuneиValgrind. Удивление пиздец, когда находишь узкое место! Самый жирный прирост получили, когда заменили классический полиморфизм с виртуальными функциями на CRTP. Ну знаешь, этот шаблонный трюк. Виртуальные вызовы — они же по таблицам, непредсказуемо для процессора. А тут — всё инлайнится на этапе компиляции, красота! После этого предсказатель ветвлений в процессоре просто плакал от счастья.
Итог: Выебали систему до стабильных 85 микросекунд на 99-м перцентиле. Заказчик аж прослезился. Проект, конечно, был пиздопроебибна по сложности, но опыт — овердохуища. Понял, что в high-performance мире каждая наносекунда на счету, и иногда ради неё нужно выковыривать сопли из кода самой хитрой жопой.