Расскажите о самом интересном проекте, над которым вы работали.

Ответ

Один из самых интересных проектов — это разработка высокопроизводительного трейдингового движка (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 мире каждая наносекунда на счету, и иногда ради неё нужно выковыривать сопли из кода самой хитрой жопой.