Większość zadań programowania o niskich opóźnieniach / wysokiej częstotliwości (na podstawie specyfikacji zadań) wydaje się być realizowana na platformach unix. W wielu specyfikacjach zwracają się one szczególnie do osób z doświadczeniem typu „Linux z niskim opóźnieniem”.
Zakładając, że nie oznacza to systemu Linux działającego w czasie rzeczywistym, czy ludzie mogliby mi pomóc z tym, co to może oznaczać? Wiem, że możesz ustawić powinowactwo procesora dla wątków, ale zakładam, że proszą o wiele więcej niż to.
Strojenie jądra? (mimo to słyszałem, że producenci tacy jak Solarflare produkują karty sieciowe z obejściem jądra)?
Co z DMA lub ewentualnie pamięcią współdzieloną między procesami? Gdyby ludzie mogli dać mi krótkie pomysły, mogę przejść do badań w Google.
(To pytanie prawdopodobnie będzie wymagać osoby zaznajomionej z transakcjami wysokiej częstotliwości)
Odpowiedzi:
Wykonałem sporo pracy, wspierając grupy HFT w ustawieniach IB i Hedge Fund. Odpowiem z widoku sysadmin, ale niektóre z nich dotyczą również programowania w takich środowiskach.
Jest kilka rzeczy, których zwykle szuka pracodawca, gdy odnoszą się do wsparcia „Low Latency”. Niektóre z nich to pytania „surowej prędkości” (czy wiesz, jaki typ karty 10 g kupić i jakie gniazdo do włożenia?), Ale więcej z nich dotyczy sposobów, w jakie środowisko handlu wysokoczęstotliwościowego różni się od tradycyjnego Środowisko Unix. Kilka przykładów:
Unix jest tradycyjnie dostrojony do obsługi dużej liczby procesów bez głodzenia zasobów, ale w środowisku HFT prawdopodobnie będziesz chciał uruchomić jedną aplikację z absolutnie minimalnym narzutem na przełączanie kontekstu i tak dalej. Jako klasyczny mały przykład włączenie hyperthreading na procesorze Intel pozwala na jednoczesne uruchomienie większej liczby procesów - ale ma znaczący wpływ na wydajność na szybkość wykonywania każdego procesu. Jako programista będziesz również musiał spojrzeć na koszt abstrakcji, takich jak wątki i RPC, i dowiedzieć się, gdzie bardziej monolityczne rozwiązanie - choć mniej czyste - pozwoli uniknąć kosztów ogólnych.
Protokół TCP / IP jest zazwyczaj dostrojony, aby zapobiegać zrywaniu połączeń i efektywnie wykorzystywać dostępną przepustowość. Jeśli Twoim celem jest uzyskanie możliwie najniższego opóźnienia z bardzo szybkiego łącza - zamiast uzyskania jak największej przepustowości z bardziej ograniczonego łącza - będziesz chciał dostosować strojenie stosu sieciowego. Od strony programowania będziesz również chciał spojrzeć na dostępne opcje gniazd i dowiedzieć się, które z nich mają ustawienia domyślne bardziej dostosowane do przepustowości i niezawodności niż do zmniejszenia opóźnień.
Podobnie jak w przypadku sieci, tak też w przypadku pamięci masowej - będziesz chciał wiedzieć, jak odróżnić problem z wydajnością pamięci masowej od problemu z aplikacją i dowiedzieć się, jakie wzorce użycia we / wy najmniej zakłócają wydajność programu (jako na przykład dowiedz się, gdzie złożoność korzystania z asynchronicznego we / wy może się opłacić i jakie są wady).
Wreszcie, bardziej boleśnie: my, administratorzy uniksowi, chcemy uzyskać jak najwięcej informacji o stanie monitorowanych przez nas środowisk, więc lubimy uruchamiać narzędzia takie jak agenci SNMP, aktywne narzędzia monitorowania takie jak Nagios i narzędzia do gromadzenia danych, takie jak sar (1). Jednak w środowisku, w którym przełączniki kontekstu muszą być absolutnie zminimalizowane, a użycie dysku i sieci IO ściśle kontrolowane, musimy znaleźć właściwy kompromis między kosztem monitorowania a czystą wydajnością monitorowanych urządzeń. Podobnie, jakich technik używasz, aby ułatwić kodowanie, ale kosztuje to wydajność?
Wreszcie, są inne rzeczy, które przychodzą z czasem; sztuczki i szczegóły, których uczysz się z doświadczeniem. Ale są one bardziej wyspecjalizowane (kiedy używam epoll? Dlaczego dwa modele serwerów HP z teoretycznie identycznymi kontrolerami PCIe działają tak inaczej?), Bardziej powiązane z tym, z czego korzysta konkretny sklep, i częściej zmieniają się z roku na rok .
źródło
Oprócz doskonałej odpowiedzi dotyczącej strojenia sprzętu / konfiguracji z @jimwise, „linux o niskim opóźnieniu” oznacza:
Wiele z tych technik pokrywa się z tworzeniem gier, co jest jednym z powodów, dla których branża oprogramowania finansowego absorbuje ostatnio zwolnionych programistów gier (przynajmniej dopóki nie spłacą zaległych czynszów).
Podstawową potrzebą jest możliwość słuchania bardzo szerokiego pasma danych rynkowych, takich jak ceny papierów wartościowych (akcje, towary, kursy walutowe), a następnie podejmowanie bardzo szybkich decyzji kupna / sprzedaży / braku działania w oparciu o bezpieczeństwo, cenę i bieżące gospodarstwa.
Oczywiście to wszystko może również pójść spektakularnie źle .
Omówię więc punkt dotyczący tablic bitów . Załóżmy, że mamy system transakcyjny wysokiej częstotliwości, który działa na długiej liście zamówień (Kup 5 tys. IBM, Sprzedaj 10 tys. DELL itp.). Powiedzmy, że musimy szybko ustalić, czy wszystkie zamówienia są wypełnione, abyśmy mogli przejść do następnego zadania. W tradycyjnym programowaniu OO będzie to wyglądać następująco:
złożoność algorytmiczna tego kodu będzie równa O (N), ponieważ jest to skan liniowy. Spójrzmy na profil wydajności pod względem dostępu do pamięci: każda iteracja pętli wewnątrz std :: any_of () wywoła o.isFilled (), która jest wstawiona, więc staje się dostępem do pamięci _isFilled, 1 bajt (lub 4 w zależności od architektury, kompilatora i ustawień kompilatora) w obiekcie, powiedzmy 128 bajtów łącznie. Mamy więc dostęp do 1 bajtu na każde 128 bajtów. Kiedy czytamy 1 bajt, zakładając, że jest to najgorszy przypadek, otrzymamy brak pamięci podręcznej danych procesora. Spowoduje to żądanie odczytu do pamięci RAM, które odczytuje całą linię z pamięci RAM ( więcej informacji tutaj ), aby odczytać 8 bitów. Profil dostępu do pamięci jest więc proporcjonalny do N.
Porównaj to z:
profil dostępu do pamięci, przy założeniu, że jest to najgorszy przypadek, to ELEMS podzielony przez szerokość linii RAM (różni się - może być dwukanałowy lub potrójny, itp.).
W efekcie optymalizujemy algorytmy pod kątem wzorców dostępu do pamięci. Żadna ilość pamięci RAM nie pomoże - to wielkość pamięci podręcznej procesora powoduje tę potrzebę.
czy to pomaga?
Na YouTube jest doskonała rozmowa o CPPCon na temat programowania z niskim opóźnieniem (dla HFT): https://www.youtube.com/watch?v=NH1Tta7purM
źródło
Ponieważ nie wprowadziłem do produkcji jednego lub dwóch programów wysokiej częstotliwości, powiedziałbym najważniejsze rzeczy:
Jedyną osobą, która faktycznie sprawia, że system wykonuje transakcje z wysoką częstotliwością, jest informatyk, który tworzy kod w c ++
Wśród wykorzystanej wiedzy jest
A. Operacje porównania i zamiany.
Utalentowany naukowiec wykorzysta więcej. Powinien znaleźć w ostatnich nowych „wzorach” ten, który pojawił się jako pierwszy w Javie. Nazywany wzorem DISRUPTOR. Fold w wymianie LMAX w Europie wyjaśnił społeczności o wysokiej częstotliwości, że wykorzystanie wątków we współczesnych procesorach straciłoby czas przetwarzania przy zwolnieniu pamięci podręcznej przez procesor, jeśli kolejka daya nie jest dopasowana do wielkości nowoczesnej pamięci podręcznej procesora = 64
Więc dla tego odczytu opublikowali kod Java, który pozwala procesowi wielowątkowemu na prawidłowe używanie sprzętowej pamięci podręcznej procesora bez rozwiązywania konfliktów. I dobry informatyk MUSI odkryć, że ten wzorzec został już przeniesiony do c ++ lub sam się przeportował.
Jest to biegłość wykraczająca poza konfigurację administratora. Jest to dziś prawdziwe serce wysokiej częstotliwości.
Będziesz zaskoczony, gdy zobaczysz, że potok jest używany TYLKO DO powiadomienia jądra o nadejściu wiadomości. Możesz tam umieścić 64-bitowy numer wiadomości - ale w przypadku treści przechodzisz do kolejki CAS bez blokowania. Wyzwalane przez asynchroniczne
select()
wywołanie jądra .Jak widać - wysoka częstotliwość to DZIAŁANIE ROZWOJOWE. Aby odnieść sukces, nie możesz być programistą C ++.
A kiedy mówię, że odniesie sukces, mam na myśli to, że fundusz hedgingowy, dla którego pracowałbyś, rozpozna wysiłki związane z trasami koncertowymi w rocznym wynagrodzeniu przekraczającym liczbę osób i rekruterów, o których mówią.
Czasy najczęstszych pytań o konstruktor / destruktor minęły bezpowrotnie. Sama c ++… migrowała z nowymi kompilatorami, aby uwolnić cię od zarządzania pamięcią i wymusić brak dziedziczenia o dużej głębi w klasach. Strata czasu. Zmieniono paradygmat ponownego wykorzystywania kodu. Nie chodzi tylko o to, ile klas wykonałeś polimorfem. Chodzi o prosto potwierdzoną wydajność kodu, którego można użyć ponownie.
Więc to jest twój wybór, aby przejść do krzywej uczenia się, czy nie. Nigdy nie trafi w znak stopu.
źródło