Die meisten Programmierjobs mit niedriger Latenz / hoher Frequenz (basierend auf Jobspezifikationen) scheinen auf Unix-Plattformen implementiert zu sein. In vielen Spezifikationen stellen sie besondere Anforderungen an Personen mit Linux-Erfahrung mit geringer Latenz.
Angenommen, dies bedeutet kein Echtzeit-Linux-Betriebssystem. Könnten mir die Leute helfen, worauf sich dies beziehen könnte? Ich weiß, dass Sie die CPU-Affinität für Threads festlegen können, aber ich gehe davon aus, dass sie viel mehr verlangen.
Kernel-Tuning? (obwohl ich gehört habe, dass Hersteller wie Solarflare sowieso Kernel-Bypass-Netzwerkkarten produzieren)?
Was ist mit DMA oder möglicherweise gemeinsam genutztem Speicher zwischen Prozessen? Wenn mir Leute kurze Ideen geben könnten, könnte ich auf Google recherchieren.
(Für diese Frage ist wahrscheinlich jemand erforderlich, der mit dem Hochfrequenzhandel vertraut ist.)
Antworten:
Ich habe eine Menge Arbeit geleistet, um HFT-Gruppen in IB- und Hedge-Fonds-Umgebungen zu unterstützen. Ich werde aus der Sysadmin-Ansicht antworten, aber einiges davon gilt auch für die Programmierung in solchen Umgebungen.
Es gibt einige Dinge, nach denen ein Arbeitgeber normalerweise sucht, wenn er sich auf die Unterstützung bei geringer Latenz bezieht. Einige davon sind "Rohgeschwindigkeits" -Fragen (wissen Sie, welche Art von 10-g-Karte Sie kaufen und in welchen Steckplatz Sie sie stecken müssen?), Aber in mehr geht es um die Art und Weise, in der sich eine Hochfrequenz-Handelsumgebung von einer herkömmlichen unterscheidet Unix-Umgebung. Einige Beispiele:
Unix ist traditionell so optimiert, dass es das Ausführen einer großen Anzahl von Prozessen unterstützt, ohne dass Ressourcen verloren gehen. In einer HFT-Umgebung möchten Sie jedoch wahrscheinlich eine Anwendung mit einem absoluten Minimum an Overhead für die Kontextumschaltung usw. ausführen . Als klassisches kleines Beispiel ermöglicht das Aktivieren von Hyperthreading auf einer Intel-CPU, dass mehrere Prozesse gleichzeitig ausgeführt werden. Dies hat jedoch erhebliche Auswirkungen auf die Leistung der Geschwindigkeit, mit der jeder einzelne Prozess ausgeführt wird. Als Programmierer müssen Sie sich ebenfalls die Kosten für Abstraktionen wie Threading und RPC ansehen und herausfinden, wo eine monolithischere Lösung - obwohl sie weniger sauber ist - Overhead vermeidet.
TCP / IP wird normalerweise optimiert, um Verbindungsabbrüche zu vermeiden und die verfügbare Bandbreite effizient zu nutzen. Wenn Ihr Ziel darin besteht, aus einer sehr schnellen Verbindung die geringstmögliche Latenz zu erzielen, anstatt aus einer eingeschränkteren Verbindung die höchstmögliche Bandbreite herauszuholen, sollten Sie die Optimierung des Netzwerkstapels anpassen. Von der Programmierseite aus sollten Sie sich ebenfalls die verfügbaren Socket-Optionen ansehen und herausfinden, welche Standardeinstellungen besser auf Bandbreite und Zuverlässigkeit abgestimmt sind als auf die Reduzierung der Latenz.
Wie beim Netzwerk, so auch beim Speicher - möchten Sie wissen, wie Sie ein Problem mit der Speicherleistung von einem Anwendungsproblem unterscheiden und welche E / A-Nutzungsmuster die Leistung Ihres Programms am wenigsten beeinträchtigen (als Erfahren Sie beispielsweise, wo sich die Komplexität der Verwendung von asynchronen E / A für Sie auszahlen kann und welche Nachteile dies hat.
Und noch schmerzhafter: Wir Unix-Administratoren möchten so viele Informationen wie möglich über den Status der von uns überwachten Umgebungen erhalten. Daher führen wir gerne Tools wie SNMP-Agenten, aktive Überwachungstools wie Nagios und Datenerfassungstools wie sar (1) aus. In einer Umgebung, in der Kontextwechsel absolut minimiert und die Verwendung von Festplatten- und Netzwerk-E / A streng kontrolliert werden muss, müssen wir jedoch den richtigen Kompromiss zwischen den Kosten für die Überwachung und der Bare-Metal-Leistung der überwachten Boxen finden. In ähnlicher Weise, welche Techniken verwenden Sie, die das Codieren erleichtern, aber Ihre Leistung kosten?
Schließlich gibt es noch andere Dinge, die mit der Zeit kommen. Tricks und Details, die Sie mit Erfahrung lernen. Diese sind jedoch spezialisierter (wann verwende ich epoll? Warum arbeiten zwei Modelle von HP Servern mit theoretisch identischen PCIe-Controllern so unterschiedlich?), Binden stärker an das, was Ihr spezifischer Shop verwendet, und ändern sich eher von einem Jahr zum anderen .
quelle
Zusätzlich zu der hervorragenden Antwort zur Hardware- / Setup-Optimierung von @jimwise bedeutet "Linux mit geringer Latenz":
Viele dieser Techniken überschneiden sich mit der Spieleentwicklung, was ein Grund dafür ist, dass die Finanzsoftwareindustrie kürzlich überflüssige Spieleprogrammierer aufnimmt (zumindest bis sie ihre Mietrückstände bezahlen).
Das zugrunde liegende Bedürfnis besteht darin, in der Lage zu sein, einen Strom von Marktdaten mit sehr hoher Bandbreite wie Wertpapierpreise (Aktien, Rohstoffe, Devisen) abzuhören und dann auf der Grundlage des Wertpapiers, des Preises, eine sehr schnelle Kauf- / Verkaufs- / Nichtstun-Entscheidung zu treffen und aktuelle Bestände.
Natürlich kann das alles auch spektakulär schief gehen .
Also werde ich auf den Punkt der Bit-Arrays näher eingehen . Nehmen wir an, wir haben ein Hochfrequenz-Handelssystem, das mit einer langen Liste von Aufträgen arbeitet (Kaufen Sie 5.000 IBM, verkaufen Sie 10.000 DELL usw.). Angenommen, wir müssen schnell feststellen, ob alle Aufträge erfüllt sind, damit wir mit der nächsten Aufgabe fortfahren können. In der traditionellen OO-Programmierung sieht dies folgendermaßen aus:
Die algorithmische Komplexität dieses Codes wird O (N) sein, da es sich um einen linearen Scan handelt. Werfen wir einen Blick auf das Leistungsprofil in Bezug auf Speicherzugriffe: Jede Iteration der Schleife in std :: any_of () ruft o.isFilled () auf, das inline ist, und wird so zu einem Speicherzugriff von _isFilled, 1 Byte (oder 4 abhängig von Ihrer Architektur, dem Compiler und den Compilereinstellungen) in einem Objekt von insgesamt 128 Bytes. Wir greifen also auf 1 Byte pro 128 Byte zu. Wenn wir das 1-Byte lesen, was im schlimmsten Fall angenommen wird, erhalten wir einen CPU-Datencache-Fehler. Dies führt zu einer Leseanforderung an den RAM, die eine ganze Zeile aus dem RAM liest ( siehe hier für weitere Informationen ), um nur 8 Bits auszulesen. Das Speicherzugriffsprofil ist also proportional zu N.
Vergleichen Sie dies mit:
Das Speicherzugriffsprofil davon ist unter der Annahme des schlimmsten Falls ELEMS geteilt durch die Breite einer RAM-Leitung (variiert - kann zweikanalig oder dreikanalig sein usw.).
Tatsächlich optimieren wir Algorithmen für Speicherzugriffsmuster. Es hilft keine RAM-Größe - es ist die Größe des CPU-Datencaches, die diesen Bedarf verursacht.
Hilft das?
Auf YouTube gibt es einen ausgezeichneten CPPCon-Vortrag über Programmierung mit geringer Latenz (für HFT): https://www.youtube.com/watch?v=NH1Tta7purM
quelle
Da ich nicht ein oder zwei Hochfrequenzsoftware in Produktion genommen hatte, würde ich die wichtigsten Dinge sagen:
Die einzige Person, die das System tatsächlich dazu bringt, Hochfrequenzhandel zu betreiben, ist ein Informatiker, der den Code in c ++ zusammenstellt
Zu den verwendeten Kenntnissen gehört
A. Vergleichen und Tauschen von Operationen.
Der talentierte Wissenschaftler wird mehr verwenden. Er sollte in den letzten neuen "Mustern" eines finden, das zuerst in Java erschien. Wird als DISRUPTOR-Muster bezeichnet. Fold in LMAX Exchange in Europa erklärte der Hochfrequenz-Community, dass die threadbasierte Nutzung in modernen Prozessoren die Verarbeitungszeit bei der Freigabe des Speichercaches durch die CPU verlieren würde, wenn die Daya-Warteschlange nicht an der Größe des modernen CPU-Cache = 64 ausgerichtet ist
Für dieses Readon haben sie einen Java-Code veröffentlicht, mit dem Multithreading-Prozesse den Hardware-CPU-Cache ohne Konfliktlösung korrekt verwenden können. Und ein guter Informatiker MUSS feststellen, dass das Muster bereits auf c ++ portiert wurde, oder sich selbst portieren.
Dies ist eine Kompetenz, die weit über jede Administratorkonfiguration hinausgeht. Dies ist heute im Herzen der Hochfrequenz.
Und Sie werden erstaunt sein, dass die Pipe NUR FÜR die Kernel-Benachrichtigung über eingetroffene Nachrichten verwendet wird. Sie können dort die 64-Bit-Nachrichtennummer eingeben. Für Inhalte gehen Sie jedoch zu Ihrer nicht sperrenden CAS-Warteschlange. Ausgelöst durch asynchronen Kernelaufruf
select()
.Wie Sie sehen können, ist Hochfrequenz ein ENTWICKLUNGSFELD. Sie können nicht nur ein C ++ - Programmierer sein, um erfolgreich zu sein.
Und wenn ich sage, um erfolgreich zu sein, meine ich, dass der Hedgefonds, für den Sie arbeiten würden, Tourbemühungen als jährliche Vergütung anerkennt, die über die Anzahl der Personen und Personalvermittler hinausgeht, über die gesprochen wird.
Die Zeiten einfacher FAQ zu Konstruktoren / Destruktoren sind für immer vorbei. Und c ++… selbst wurde mit neuen Compilern migriert, um Sie von der Speicherverwaltung zu entlasten und die Nichtvererbung von großer Tiefe in Klassen zu erzwingen. Zeitverschwendung. Das Paradigma der Wiederverwendung von Code hat sich geändert. Es geht nicht nur darum, wie viele Klassen Sie in Polymorph erstellt haben. Es geht um die direkt bestätigte Zeitleistung von Code, den Sie wiederverwenden können.
Sie haben also die Wahl, ob Sie dort in die Lernkurve einsteigen möchten oder nicht. Es wird niemals ein Stoppschild treffen.
quelle