Unix / Linux с низкой задержкой

11

Большинство заданий на программирование с низкой задержкой / высокой частотой (на основе спецификаций заданий), по-видимому, реализуются на платформах Unix. Во многих спецификациях они делают особый запрос для людей с опытом работы с «низкой задержкой Linux».

Предполагая, что это не означает ОС Linux в реальном времени, могут ли люди помочь мне с тем, к чему это может относиться? Я знаю, что вы можете установить привязку процессора к потокам, но я предполагаю, что они просят гораздо большего.

Тюнинг ядра? (хотя я слышал, что производители, такие как solarflare, производят сетевые карты с обходом ядра)?

Как насчет DMA или, возможно, общей памяти между процессами? Если бы люди могли дать мне краткие идеи, я мог бы пойти и исследовать Google.

(Этот вопрос, вероятно, потребует кого-то знакомого с высокочастотной торговлей)

user997112
источник
2
Настройка ядра - это способ сделать ОС не в реальном времени максимально реалистичной. Закрепление резьбы также обязательно. Вы можете прочитать больше о том , что в этой статье: coralblocks.com/index.php/2014/04/...
rdalmeida
Также связано: stackoverflow.com/q/15702601/632951
Pacerier

Ответы:

26

Я проделал большую работу по поддержке групп HFT в настройках IB и Hedge Fund. Я собираюсь ответить с точки зрения системного администратора, но кое-что из этого применимо и к программированию в таких средах.

Есть несколько вещей, которые обычно ищет работодатель, когда ссылаются на поддержку «Low Latency». Некоторые из них представляют собой вопросы «сырой скорости» (знаете, какой тип карты 10g купить и в какой слот ее вставлять?), Но большинство из них касаются того, чем среда высокочастотной торговли отличается от традиционной Unix среда. Несколько примеров:

  • Традиционно Unix настроен на поддержку запуска большого количества процессов, не требуя ни одного из них для ресурсов, но в среде HFT вы, вероятно, захотите запустить одно приложение с абсолютным минимумом накладных расходов для переключения контекста и так далее. В качестве небольшого классического примера включение гиперпоточности на процессоре Intel позволяет одновременно запускать больше процессов, но оказывает значительное влияние на производительность на скорость, с которой выполняется каждый отдельный процесс. Как программист, вам также придется посмотреть на стоимость абстракций, таких как многопоточность и RPC, и выяснить, где более монолитное решение - хотя и менее чистое - позволит избежать накладных расходов.

  • TCP / IP обычно настраивается для предотвращения разрыва соединения и эффективного использования доступной пропускной способности. Если ваша цель состоит в том, чтобы получить максимально возможную задержку в очень быстром канале - вместо того, чтобы получить максимально возможную полосу пропускания в более ограниченном канале - вам нужно будет настроить параметры сетевого стека. Со стороны программирования вы также захотите взглянуть на доступные опции сокетов и выяснить, какие из них имеют настройки по умолчанию, более настроенные на пропускную способность и надежность, чем на снижение задержки.

  • Как с сетью, так и с хранилищем - вы захотите узнать, как отличить проблему производительности хранилища от проблемы приложения, и узнать, какие шаблоны использования ввода-вывода с наименьшей вероятностью будут влиять на производительность вашей программы (например, Например, узнайте, где сложность использования асинхронного ввода-вывода может окупиться для вас, и каковы недостатки).

  • И, наконец, еще более болезненно: мы, администраторы Unix, хотим получить как можно больше информации о состоянии сред, которые мы отслеживаем, поэтому нам нравится запускать инструменты, такие как агенты SNMP, инструменты активного мониторинга, такие как Nagios, и инструменты сбора данных, такие как sar (1). В среде, где необходимо полностью свести к минимуму переключение контекста и жестко контролировать использование дискового и сетевого ввода-вывода, мы должны найти правильный компромисс между затратами на мониторинг и производительностью отслеживаемых блоков. Точно так же, какие методы вы используете, которые облегчают кодирование, но снижают производительность?

Наконец, есть другие вещи, которые просто приходят со временем; хитрости и детали, которые вы узнаете с опытом. Но они более специализированы (когда я использую epoll? Почему две модели серверов HP с теоретически идентичными контроллерами PCIe работают так по-разному?), Более привязаны к тому, что использует ваш конкретный магазин, и с большей вероятностью меняются от года к другому ,

jimwise
источник
1
Спасибо, хотя мне был интересен программный ответ, это было очень полезно и информативно.
user997112
5
@ user997112 Это программный ответ. Если это не так, продолжайте читать, пока не увидите :)
Тим Пост
15

В дополнение к превосходному ответу по настройке оборудования / настройки от @jimwise, «linux linux с низкой задержкой» подразумевает:

  • C ++ по причинам детерминизма (без неожиданной задержки при включении GC), доступа к низкоуровневым средствам (ввод / вывод, сигналы), языковой мощности (полное использование TMP и STL, безопасность типов).
  • предпочтение скорости над памятью:> 512 ГБ ОЗУ является общим; базы данных находятся в памяти, кэшируются заранее или являются экзотическими продуктами NoSQL.
  • выбор алгоритма: как можно быстрее против разумного / понятного / расширяемого, например, без блокировки, многобитовые массивы вместо свойств «массив объектов с булевыми».
  • полное использование возможностей ОС, таких как общая память, между процессами на разных ядрах.
  • ненадежно. Программное обеспечение HFT обычно размещается на фондовой бирже, поэтому возможности вредоносного ПО неприемлемы.

Многие из этих методов частично совпадают с разработкой игр, что является одной из причин того, почему индустрия финансового программного обеспечения поглощает любых недавно избыточных программистов игр (по крайней мере, до тех пор, пока они не выплатят свои долги по аренде).

Основная потребность заключается в том, чтобы иметь возможность прослушивать очень рыночный поток рыночных данных, таких как цены ценных бумаг (акции, товары, форекс), а затем принимать очень быстрое решение купить / продать / ничего не делать, основываясь на безопасности, цене и текущие авуары.

Конечно, все это тоже может пойти не так .


Поэтому я подробно остановлюсь на точке битовых массивов . Допустим, у нас есть система высокочастотной торговли, которая работает с длинным списком ордеров (купить 5 тысяч IBM, продать 10 тысяч DELL и т. Д.). Допустим, нам нужно быстро определить, все ли заказы выполнены, чтобы мы могли перейти к следующему заданию. В традиционном ОО-программировании это будет выглядеть так:

class Order {
  bool _isFilled;
  ...
public:
  inline bool isFilled() const { return _isFilled; }
};

std::vector<Order> orders;
bool needToFillMore = std::any_of(orders.begin(), orders.end(), 
  [](const Order & o) { return !o.isFilled(); } );

алгоритмическая сложность этого кода будет O (N), поскольку это линейное сканирование. Давайте посмотрим на профиль производительности с точки зрения доступа к памяти: каждая итерация цикла внутри std :: any_of () будет вызывать o.isFilled (), который является встроенным, поэтому становится доступ к памяти _isFilled, 1 байт (или 4 в зависимости от вашей архитектуры, настроек компилятора и компилятора) в объекте, скажем, всего 128 байт. Таким образом, мы получаем доступ к 1 байту на каждые 128 байтов. Когда мы прочитаем 1 байт, предполагая наихудший случай, мы получим промах кэша данных ЦП. Это вызовет запрос на чтение в ОЗУ, который считывает всю строку из ОЗУ ( см. Здесь для получения дополнительной информации ) просто для считывания 8 бит. Таким образом, профиль доступа к памяти пропорционален N.

Сравните это с:

const size_t ELEMS = MAX_ORDERS / sizeof (int);
unsigned int ordersFilled[ELEMS];

bool needToFillMore = std::any_of(ordersFilled, &ordersFilled[ELEMS+1],
   [](int packedFilledOrders) { return !(packedOrders == 0xFFFFFFFF); }

профиль доступа к памяти этого, при условии, опять же, наихудшего случая, представляет собой ELEMS, деленные на ширину линии ОЗУ (варьируется - может быть двухканальным или трехканальным и т. д.).

Итак, по сути, мы оптимизируем алгоритмы для шаблонов доступа к памяти. Никакой объем оперативной памяти не поможет - это обусловлено размером кэша данных ЦП.

Это помогает?


На YouTube есть отличный рассказ о программировании с малой задержкой (для HFT): https://www.youtube.com/watch?v=NH1Tta7purM

JBRWilkinson
источник
«множественные битовые массивы вместо свойств array-objects-with-bool-properties» что вы подразумеваете под этим?
user997112
1
Я разработал с примерами и ссылками.
JBRWilkinson
Если пойти дальше - вместо использования целого байта, чтобы указать, заполнен ли ордер или нет, - вы можете просто использовать один бит. Таким образом, в одной строке кэша (64 байта) вы можете представить состояние 256 порядков. Так что - меньше промахов.
Quixver
Также - если вы выполняете линейное сканирование памяти - аппаратный предварительный сборщик отлично справляется с загрузкой ваших данных. При условии, что вы получаете доступ к памяти последовательно или пошагово или что-то простое. Но если вы обращаетесь к памяти каким-либо непоследовательным способом - предварительная выборка процессора запутывается. Например, бинарный поиск. В этот момент программист может помочь процессору с подсказками - _mm_prefetch.
Quixver
-2

Поскольку я выпустил не одно или два высокочастотных программного обеспечения в производство, я бы сказал самые важные вещи:

  1. Конфигурация оборудования и системные администраторы вместе с сетевыми инженерами НЕ определяют хороший результат количества ордеров, обрабатываемых торговой системой, но они могут значительно понизить его, если они не знают основ, изложенных выше.
  2. Единственный человек, который на самом деле заставляет систему заниматься высокочастотной торговлей, - это ученый, который собирает код на С ++.

    Среди используемых знаний

    А. Операции сравнения и обмена.

    • как CAS используется в процессоре и как компьютер поддерживает его для использования в так называемой обработке без блокировки. Или обработка без блокировки. Я не буду писать здесь целую книгу. Вкратце, компилятор GNU и компилятор Microsoft поддерживают прямое использование инструкций CAS. Это позволяет вашему коду иметь «No.Wair» при извлечении элемента из очереди или добавлении нового в очередь.
  3. Талантливый ученый будет использовать больше. Он должен найти в недавних новых «шаблонах» тот, который появился в Java первым. Называется шаблон DISRUPTOR. Свертывание LMAX-обмена в Европе объяснило высокочастотному сообществу, что использование потоков в современных процессорах приведет к потере времени обработки при освобождении кэша памяти процессором, если очередь daya не соответствует размеру современного кэша процессора = 64

    Поэтому для этого чтения они обнародовали код Java, который позволяет многопоточному процессу правильно использовать аппаратный кэш процессора без разрешения конфликтов. И хороший компьютерный ученый должен найти этот шаблон уже портированным на c ++ или сделать это сам.

    Это умение выходить за рамки любой конфигурации администратора. Это в самом сердце высокой частоты сегодня.

  4. Специалист по информатике ДОЛЖЕН написать много кода на C ++, а не только для того, чтобы помочь людям, отвечающим за обеспечение качества. Но также
    • проверить в лице трейдеров доказали достигнутую скорость
    • осудить использование различных старых технологий и выставить их со своим собственным кодом, чтобы показать, что они не дают хороших результатов
    • написать собственный многопоточный коммуникационный код на C ++, основанный на проверенной скорости ядра pupe / select вместо использования старых технологий. Я приведу пример - современная библиотека tcp - это ICE. И люди, которые сделали это, яркие. Но их приоритеты были в области совместимости со многими языками. Так. Вы можете сделать лучше в C ++. Так что ищите примеры максимальной производительности на основе ASYNCHRONOUS select-call. И не делайте для нескольких потребителей нескольких производителей - не для HF.
      И вы будете удивлены, обнаружив, что канал используется ТОЛЬКО ДЛЯ уведомления ядра о поступившем сообщении. Вы можете поместить туда 64-битный номер сообщения - но для контента вы идете в очередь без блокировки CAS. Вызывается асинхронным select()вызовом ядра .
    • Кроме того. Узнайте о назначении с помощью нити c ++ для вашей нити, которая занимается передачей / отправкой ваших сообщений. Этот поток должен иметь сродство с ядром. Никто другой не должен использовать тот же номер ядра процессора.
    • и так далее.

Как видите, высокая частота - это развивающееся поле. Вы не можете быть просто программистом C ++, чтобы преуспеть.

И когда я говорю «добиться успеха», я имею в виду, что хедж-фонд, на который вы будете работать, БУДЕТ признавать гастроли в виде годовой компенсации сверх того количества, о котором говорят люди и рекрутеры.

Времена простого конструктора / деструктора часто задаваемые вопросы прошли навсегда. И C ++ ... сам мигрировал с новыми компиляторами, чтобы избавить вас от управления памятью и обеспечить отсутствие наследования большой глубины в классах. Пустая трата времени. Код повторного использования парадигмы изменился. Дело не только в том, сколько классов вы создали в полиморфе. Речь идет о подтвержденном времени выполнения кода, который вы можете использовать повторно.

Таким образом, это ваш выбор - пойти на кривую обучения или нет. Он никогда не достигнет знака остановки.

алекс п
источник
6
Возможно, вы захотите приложить некоторые усилия для написания и форматирования. В своем нынешнем виде этот пост едва понятен.
CodesInChaos
1
Вы описываете ситуацию 10 лет назад. Аппаратные решения в настоящее время легко превосходят чистый C ++, независимо от того, насколько оптимизирован ваш C ++.
Сьерд
Для тех, кто хочет знать, что такое аппаратные решения - это в основном решения на основе ПЛИС, в которых код фактически записывается в быструю память и не изменяется при перезапуске так называемой ПЗУ. Только чтение
alex p
@alexp Вы явно не знаете, о чем говорите. FPGA - это нечто иное, чем «код, записанный в быстрой памяти».
Сьерд