Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
18 - Telegram Web
Telegram Web
Привет!
Меня зовут Александр, работаю C++ программистом в сфере High Frequency Trading, работаю с API бирж, поддерживаю торговую инфраструктуру, оптимизирую сетевые подключения, ускоряю код. Люблю оптимизировать код, а HFT сильно требователен к latency процессов, мне это подходит. До этого работал в core команде в Ntech в над системой видеоаналитики, где мы разрабатывали на C++ и Go компоненты, работающие с видео, и запускали нейронки на раздекоженных кадрах (поиск и классификация объектов, извлечение биометрических векторов и т.п.). До этого работал в gamedev индустрии в Allods Team над Armata Warfare и Sky Forge.

Буду писать здесь про:
- C++ (17-23). Возможно, иногда Python, Go, Bash.
- иногда свои заметки, мысли, находки, баги и интересные случаи
- иногда компляция чужих статей и заметок, либо переводы
- Linux
- Микро-оптимизации C++ кода
- Concurrency
- High Frequency Algo Trading индустрию
- Тулзы программиста
- Собеседования
- Ссылки на хорошие статьи, новости и книги
- Общие мысли по технологиям
- Задачки и вопросы для читателей
- Свои проекты, если они будут

Я не планирую быть экспертом и всезнайкой. Если я где-то допустил неточность и вы хотите поправить или хотите дополнить информацию, пишите в комментарии или в личку. Уровень - различный, от простого до сложного.
👍5
Первое: хочу обратить ваше внимание на книгу Дениса Бахвалова "Performance Analysis and Tuning on Modern CPU". Он сейчас пишет второе издание, и книга ещё не готова, но многие главы уже можно прочесть, заглянув в Github репозиторий книги.

Мне очень нравится такой процесс написания книги. Он какой-то максимально понятный (для меня) и гиковский: github, pull-request'ы, issues, текст в markdown, pandoc для генерации pdf. Например, посмотрите в "Pull requests", там люди реально предлагают небольшие улучшения, и Денис их принимает.

Заходите в github и забирайте книгу в разделе "Releases":
https://github.com/dendibakh/perf-book/releases

А Pandoc нормальная софтина, я с её помощью генерировал из Markdown полноценную документацию к SDK, который делал в предыдущем месте работы. Получалось красиво.
👍5
Если хочется поисследовать что компилятор делает с кодом, то у нас есть один божественный инструмент - Godbolt.

Я периодически им пользуюсь, и для упрощения работы есть несколько идей:

1. Можно навести на assembly команду и получить короткую справку. В контекстном меню можно нажать "View assembly documentation" и там получить более детальную документацию и ссылку на полные спеки.

2. Не обязательно писать main, т.к. компилятор собирает object file, а не финальный бинарник. Хотя собрать бинарник и запустить тоже можно (и даже прописать stdin для него).

3. Можно подключить библиотеки из некого ограниченного набора. Например, есть libfmt, boost, pybind, simdjson, qt. Отдельно упомяну, что можно инклудить single file header-only по http url'у (пример), но лично я не нашел этой фиче применения.

4. Для запоминания конфигурации окон есть Templates в верхнем меню. Например, я сохранил и использую шаблон для компиляции сразу в clang и в gcc с типовыми аргументами -O3 -DNDEBUG -std=c++20.

5. В Obsidian держу наготове snippet:
// __attribute__((noinline)) // [[gnu::noinline]]

template <class Tp>
inline __attribute__((always_inline)) void noopt(Tp const& value) {
asm volatile("" : : "r,m"(value) : "memory");
}

Можно также сохранить в "Brower local storage" прямо в compiler explorer.

Если ты батя, которому это всё понятно и очевидно, то буду благодарен твоим персональным tips&tricks в комментах.

А вообще, удивительно, насколько широко он развился. Сейчас поддерживается несколько десятков языков (47 на конец 2022 года), и почти две тысячи компиляторов/интерпретаторов. Из понятных мне есть Go, Python, C#, Java, Javascript, Rust, Zig, Assembly, а из интересного, есть компиляция в CUDA бинарник.

Репозиторий проекта: https://github.com/compiler-explorer/compiler-explorer
Grafana для любопытных: https://ce.grafana.net/public-dashboards/326d9aa2606b4efea25f4458a4c3f065?orgId=0&refresh=1m
👍3
UBBook: https://github.com/Nekrolm/ubbook - довольно интересная серия заметок про undefined behavior в языке.

Несложный пример с condition_variable из книги:
void thread1() {
std::unique_lock lock { event_mutex };
cv.wait(lock, [&] {
return event_happened;
});
}

void thread2() {
...
event_happened = true;
cv.notify_one();
}

Мы здесь видим, что в thread2 event_happened изменяется без мьютекса, и знаем, что это некорректно. Но что конкретно может произойти не так?
👍3
Один микросек - C++, low latency, concurrency, HFT
UBBook: https://github.com/Nekrolm/ubbook - довольно интересная серия заметок про undefined behavior в языке. Несложный пример с condition_variable из книги: void thread1() { std::unique_lock lock { event_mutex }; cv.wait(lock, [&] { return…
Когда я пришел в HFT, то мне быстро попался в поле зрения Henrique Bucher в Substack. Он интересный и полезный, но стоит нескромно - $8 в месяц (и это если платишь за год). На мой взгляд, стоит почитать бесплатную версию (свежие статьи открыты, а старые уходят в платный архив). Например, сейчас можно почитать статью про реализацию rate limit'ов на клиентской стороне.

На любой бирже ты не можешь спамить ордерами с произвольной частотой, биржа задаёт лимиты. Например, в Bybit: не больше 600 http запросов в 5 секундном плавающим окне per IP address, и не больше 10 ордеров в секунду. Если нарушаешь расчёты, то получаешь троттлинг через ответ HTTP 429 (Too many requests), а могут и временно забанить, чтобы было время хорошо подумать. На бинансе существуют еще суточные лимиты на кол-во ордеров, и лимиты без плавающего окна (сбрасывающиеся каждые N секунд, довольно удобная для расчётов схема, мне нравится идея). В итоге на каждый ордер тебе нужно рассчитывать "можно ли посылать сейчас" (либо знать это заранее), лимитов много разных видов, поэтому задача хоть и небольшая, но важная. Если хочешь выше лимиты, то получай vip tier, договаривайся лично, используй больше source ip адресов, и batch запросы.

https://lucisqr.substack.com/p/controlling-rate-limited-events-implementation
👍21
Некоторое время назад начал записывать результаты бенчмарков в базу знаний в Obsidian. Спустя год-два ты ведь уже не вспомнишь что быстрее std::to_string или fmt::to_string или my_to_string, и насколько, и почему, и какие инсайты это тебе дало. А если записано, то позже вернёшься и освежишь в памяти.

Для корректности стоит так же записать используемые:
- железо (CPU, RAM, caches)
- окружение (os distribution, kernel, версия используемого компилятора, версия std lib)
- флаги процесса, с котороми запускал бенч (например, для google benchmark использую --benchmark_min_warmup_time=0.1 --benchmark_repetitions=5 --benchmark_report_aggregates_only=1, но со временем привычки могут меняться, и это должно быть записано)
- версии библиотек
- флаги компилятора и линковщика

Например:
--------------------------------
Benchmark Time CPU
--------------------------------
bench_libfmt_mean 45.4 ns 45.4 ns
bench_libfmt_median 45.3 ns 45.3 ns
bench_libfmt_stddev 0.455 ns 0.402 ns

bench_fmt_to_string_mean 23.7 ns 23.7 ns
bench_fmt_to_string_median 23.6 ns 23.6 ns
bench_fmt_to_string_stddev 0.340 ns 0.340 ns

bench_std_to_string_mean 132 ns 132 ns
bench_std_to_string_median 132 ns 132 ns
bench_std_to_string_stddev 0.460 ns 0.445 ns


libfmt: v8.1.1
c++23
g++: v11.4.0


CPU: AMD Ryzen 7 5800U
RAM: DDR4
CPU Caches:
L1 Data 32 KiB (x8)
L1 Instruction 32 KiB (x8)
L2 Unified 512 KiB (x8)
L3 Unified 16384 KiB (x1)
OS: 23.04, kernel 5.15.0-89-generic
Run: `--benchmark_min_warmup_time=0.1 --benchmark_repetitions=5 --benchmark_report_aggregates_only=1`


По сему получается, fmt::to_string заметно шустрее.

Чего не хватает в моей реализации записок:
- Иногда не храню исходник самого бенча. Через год уже и не вспомнишь, что подавал на вход, что конкретно мерил, и не сделал ли ошибок.
- Не автоматизирован сбор данных об окружении.
- На записаны флаги компиляции.
👍5
Надеюсь, это вам не пригодится:

В CMake 3.27 появился дебаггер. Это не впечатлит CLion и MS VS, у них есть такая функциональность больше года, но тогда они собирали свои форки cmake'а, а теперь дебаггер есть официально. Не то, чтобы я мечтал дебажить эту историю, но за десяток последних лет это бы мне пригодилось.

Прямо сейчас узнал, что у меня стоит v3.27 и оно работает и ждёт подключения:
> cmake --debugger --debugger-pipe ./abc -Bbbb .
Running with debugger on.
Waiting for debugger client to connect...


Clion умеет этим пользоваться с версии 2023.3 :)
👍1
Открытие дня: некоторые спрятанные под paywall статьи с Substack могут быть получены через Pocket (приложение для скрейпинга статей в удобо-читаемый формат).

Например, так я почитал платную статью "Stop measuring time", где автор предлагает измерять длительность циклами процессора, а не временем, потому что замеры времени:
- имеют больший latency (самого замера)
- имеют больший jitter
- несравнимы для разных частот ядра (а частота легко может меняться)

Я как-то раз пробовал замерять кол-во циклов, всё хорошо, но непривычно. Мозг привык оперировать нано/микросекундами, и 12.3 микросекунды воспринимается проще, чем 37240 циклов.
👍3
Если бы C++ придумали в СССР:
шаблон<класс Переборщик>
ничего_не_возвращай отсортируй(Переборщик первый, Переборщик крайний)
{
если (первый == крайний)
верни;

авто пивот = *следующий(первый, расстояние(первый, крайний) / 2);
авто серединка1 = раздели(первый, крайний, [пивот](нетрогай авто& эм) {
верни эм < пивот;
});
авто серединка2 = раздели(серединка1, крайний, [пивот](нетрогай авто& эм) {
верни !(пивот < эм);
});

отсортируй(первый, серединка1);
отсортируй(серединка2, крайний);
}

цел main() {
массив<цел> в1 = {9, 5, 7, 1};
отсортируй(в1.начало(), в1.конец());
для (авто в : в1) {
печать << в << " ";
}
верни 0;
}


Компилируется и работает: пруф. Но есть нюанс...
🗿2🔥1
Новая рубрика в канале: "Random job interview shit", другими словами, вопросы с собеседований, как хорошие, так и всратые.

1) Что будет результатом выполнения?

std::string s = "1234567890123";
std::string_view sv = s + "456";
std::cout << sv;


2) Как результат зависит от размера строки `s`?
2025/07/09 19:14:45
Back to Top
HTML Embed Code: