C++, Rust en hoogfrequente handel: waar deterministische latentie het argument bepaalt

C++, Rust en hoogfrequente handel: waar deterministische latentie het argument bepaalt

C++, Rust en hoogfrequente handel: waar deterministische latentie het argument bepaalt

Invoering

Discussies over programmeertalen worden doorgaans getolereerd omdat de meeste systemen zich een beetje theater kunnen veroorloven. Een service is een beetje inefficiënt, een wachtrij wordt breder dan zou moeten, een beleid voor opnieuw proberen doet iets moreel twijfelachtig, en iedereen blijft in beweging omdat het product nog steeds werkt, de inkomsten nog steeds binnenkomen en de latentiegrafiek lelijk is op een manier die overleefd kan worden.

Hoogfrequente handel is minder sentimenteel. Het maakt niet uit welke taal dit kwartaal het internet heeft gewonnen. Het maakt niet uit of marktgegevens status worden, status een beslissing wordt, en de beslissing een order wordt voordat het venster sluit. In dat soort omgevingen worden elegante meningen die metingen niet kunnen overleven, snel en meestal zonder waarschuwing overvallen.

Daarom is de vraag C++ en Rust in HFT interessant. Niet omdat de ene taal heilig is en de andere frauduleus, maar omdat HFT een van de zeldzame domeinen is die het hele argument dwingt om geld te verdienen in feitelijk systeemgedrag. Het hete pad behoudt zijn vorm onder druk of niet. De staartlatentie blijft gedisciplineerd of niet. Herhaling vertelt de waarheid of niet. Architectuur is daar geen persoonlijkheidstest. Het is een factuur.

Dit is ook de reden waarom het antwoord niet is "C++ voor altijd" of "alles herschrijven in Rust omdat veiligheid goed is en angst een bedrijfsmodel is." Het eerlijkere antwoord is beperkter en daarom nuttiger. C++ domineert nog steeds de populairste HFT-paden omdat de omringende wereld van tooling, feedverwerking, geheugencontrole, profilering en hardware-aangrenzende praktijken extreem C++-vormig blijft. Rust is echt nuttig rond die kern, en soms binnen zorgvuldig gekozen delen ervan, maar het doet niets af aan het fundamentele feit dat handelen met lage latentie abstractiefouten sneller bestraft dan de meeste teams het initiatief kunnen hernoemen.

Het juiste gesprek gaat dus niet over identiteit. Het gaat om systeemgrenzen. Welke delen van de stapel hebben brute controle nodig over geheugen, lay-out, wachtrijen, affiniteit en draadgedrag? Welke onderdelen profiteren het meest van strengere correctheidsbeperkingen en veiligere standaardinstellingen? Welke delen verdienen een hybride behandeling in plaats van tribale zuiverheid? Deze vragen zijn veel minder glamoureus dan taalpreken, maar het zijn ook de vragen die het contact met de productie overleven.

Waarom HFT slechte technische filosofie duur doet lijken

HFT is buitengewoon goed in het blootleggen van een bekende technische leugen: de leugen dat gemiddeld gedrag voldoende is. Bij veel gewone producten kan een systeem respectabel blijven, terwijl het zo nu en dan een chaos verbergt achter doorvoer, nieuwe pogingen of geduld van de gebruiker. In HFT is de gemiddelde latentie interessant, maar staartgedrag is vaak het onderdeel dat je daadwerkelijk vernedert. Een systeem dat er snel uitziet totdat het op het verkeerde moment een schok geeft, is geen snel systeem in commercieel betekenisvolle zin. Het is een vertrouwenstruc waaraan een benchmark is gekoppeld.

Dat is de reden waarom HFT-ingenieurs allergisch worden voor onnauwkeurige abstracties. Ze leren dat één extra toewijzing op het warme pad niet ‘slechts één toewijzing’ is. Het is een mogelijke bron van jitter. Eén wachtrij-hop is niet 'slechts één wachtrij-hop'. Het is een andere plek waar tijd wordt opgeslagen, de coördinatie zich uitbreidt en de zichtbaarheid slechter wordt. Eén cache-vijandige structuur is niet alleen een esthetische smet. Het is een voortdurende belasting op elke marktgebeurtenis die door het systeem gaat. Vermenigvuldig dat met het werkelijke voervolume en plotseling wordt een ontwerpkeuze uit een slide-deck een terugkerend item in het budget voor teleurstellingen.

Rust gaat dit gesprek met legitieme kracht aan omdat geheugenveiligheid belangrijk is, gelijktijdigheidscorrectheid belangrijk is en systeemcode betere standaardwaarden verdient dan "wees alsjeblieft voorzichtig terwijl je met messen over een kuil jongleert." Dat deel is waar. Maar HFT beloont de waarheid op zichzelf niet. Het beloont gecombineerde waarheid. Veiligheid is belangrijk, ja. Dat geldt ook voor volwassen feed-handlers, stabiele ABI-grenzen, replay-tooling, profielgestuurde iteratie, volwassen uitwisselingsintegratiecultuur en de mogelijkheid om precies te inspecteren wat de machine doet als de markt onaardig is. C++ komt nog steeds met meer van die omringende infrastructuur in de meeste HFT-omgevingen.

Dit is een van de redenen waarom kopers en technische leiders zich moeten verzetten tegen zuiverheidsverhalen. Een taal kan uitstekend zijn in een beperkte dimensie en toch de verkeerde standaard zijn voor het meest timinggevoelige deel van een stapel als het omringende ecosysteem, de tooling en de teamervaring het daadwerkelijke leveringstraject niet ondersteunen. HFT is waar mooie lokale waarheden naartoe gaan om te leren dat het hele pad nog steeds belangrijker is.

De stapel is niet één ding, dus de taalkeuze mag niet anders doen voorkomen

Een van de domste fouten bij serieus systeemwerk is het spreken over ‘de HFT-stack’ alsof het één enkel technisch organisme is met één voorkeurstaal. Dat is niet zo. Het is een verzameling trajecten met zeer uiteenlopende drukken en faalkosten.

Het pad voor de opname van marktgegevens heeft één karakter. Het updatepad voor het orderboek heeft een ander pad. Strategielogica kan numeriek compact zijn, maar structureel smal. Risicocontroles zijn vaak latentiegevoelig, maar ook correctheidsgevoelig op een saaie, volwassen, juridisch consequente manier. Simulatie- en herhalingsinfrastructuur kan determinisme en introspectie verkiezen boven rauwe ijdelheid van nanoseconden. Control-plane tooling, implementatiehulpmiddelen en operatoroppervlakken geven veel meer om betrouwbaarheid, onderhoudbaarheid en integratiehygiëne dan om vijf microseconden te besparen op een pad dat geen enkele klant ooit zal zien.

Dit is belangrijk omdat dit vaak de plek is waar een verstandig C++ en Rust gesprek begint. C++ blijft het sterkst wanneer het pad brutaal heet is, hardwarebewust, zwaar integratief en al omgeven door jaren van native operationele praktijk. Rust wordt aantrekkelijker als het pad nog steeds belangrijk is, maar de economische waarde van sterkere wanbetalingen, duidelijker eigendom en een beperktere blootstelling aan geheugenrisico's groter is dan de kosten van ecosysteemwrijving.

In de praktijk leidt dat vaak tot hybride uitkomsten. De populairste feed-handling- en gateway-paden blijven in C++. Replay-tools, configuratievalidatie, bepaalde risicohulpmiddelen, hulpprogramma's voor berichtnormalisatie, audittools of interne, op de operator gerichte componenten kunnen uitstekende Rust-kandidaten zijn. Dit is geen besluiteloosheid. Het is architectonische volwassenheid. Het systeem wordt behandeld als een reeks echte grenzen in plaats van als een taalfandom met een datacenter.

Waar C++ nog steeds de populairste paden bezit

C++ behoudt zijn plaats in HFT om redenen die minder mystiek zijn dan buitenstaanders zich soms voorstellen. De eerste reden is geheugen- en lay-outcontrole. HFT Hotpaths maken zich zorgen over welke gegevens samenleven, hoe structuren zich in de cache gedragen, hoe eigendom onder belasting verschijnt en of het systeem allocatiegedisciplineerd kan blijven als de markt niet langer beleefd is. C++ geeft ingenieurs nog steeds een ongewoon directe invloed op die keuzes, en wel binnen een ecosysteem dat al tientallen jaren heeft geleerd welke 'kleine' kosten in het geheim groot zijn.

De tweede reden is de gereedschapsdichtheid. C++ in HFT betekent niet alleen een taal. Het betekent compilers, ontsmettingsmiddelen, vlamgrafieken, C++, VTune, replay-harnassen, uitwisselingsadapters, wachtrijfolklore, expertise van allocators en een enorme hoeveelheid prestatieoorlogverhalen die zijn verzameld onder financiële druk. Teams beginnen daar niet vanaf nul. Ze erven een diepe operationele cultuur, en die cultuur is van belang omdat HFT afgemeten iteratie veel meer beloont dan retorische zuiverheid.

De derde reden is de integratiezwaartekracht. Uitwisselingen, native netwerkpaden, tooling voor het vastleggen van pakketten, kernel-aangrenzende optimalisatie, FPGA-aangrenzende infrastructuur en het hele ecosysteem met lage latentie zijn nog steeds zeer comfortabel in een C- en C++-wereld. Rust kan communiceren met die wereld, en soms heel effectief, maar 'kan communiceren met' is niet hetzelfde als 'is de weg van de minste wrijving door het hele systeem'. Bij ernstige HFT is wrijving geen emotioneel ongemak. Het is tegelijkertijd een mogelijke latentiebelasting, een debug-belasting en een leveringsbelasting.

Er is ook een subtielere reden die er meer toe doet in het AI-tijdperk: C++ heeft simpelweg meer operationeel geheugen beschikbaar voor dit werk. AI coderingssystemen, code zoeken, openbare voorbeelden, leveranciersfragmenten, folklore over optimalisaties en foutopsporingspaden zijn dichter bij C++ in systemen met lage latentie dan rond Rust. Dat maakt C++ niet nobeler. Het maakt het gemakkelijker voor mensen en AI-tools om samen te werken in lelijke, echte codebases waarvan de charme al jaren geleden is verlopen.

Waar Rust daadwerkelijk helpt in plaats van moraliteit uit te voeren

Rust helpt het meest als het een echt probleem oplost, in plaats van te fungeren als een persoonlijkheidsaccessoire voor architectuurdiagrammen. In HFT verschijnen de sterkste Rust gebruiksscenario's vaak rond de hete kern in plaats van in het absolute centrum ervan.

Rust is handig voor componenten waarbij correctheidsfouten duur zijn, maar het latentiebudget niet met een microscoop wordt gemeten. Berichtvalidatielagen, configuratie- en implementatietools, bepaalde protocolnormalisatiepaden, controlediensten, administratieve hulpprogramma's, offline analysers en tools van interne operators kunnen profiteren van de voorkeur van de taal voor explicietheid. Het gaat er niet om er modern uit te zien. Het punt is om de klasse van domme, repetitieve, structureel vermijdbare fouten te verminderen die de aandacht afleiden van belangrijker werk.

Rust kan ook helpen bij zorgvuldig gekozen, bijna hete componenten wanneer het team over de juiste expertise beschikt en de grens eerlijk is. Een parser met lage latentie, een begrensde toestandsmachine of een stuk deterministische infrastructuur kan een solide Rust kandidaat zijn als het team het FFI en het toewijzingsverhaal onder controle kan houden en als de omringende ecosysteemlast van tevoren wordt begrepen in plaats van om 2.40 uur in de ochtend ontdekt te worden tijdens een uitrol die niemand wilde.

Maar dit is precies waar teams discipline nodig hebben. Rust is niet waardevol als het in het midden van een oorspronkelijke handelsstapel wordt geplaatst als een op geloof gebaseerde renovatie. Het is waardevol als de grens schoon is, het meetpad duidelijk is en de operationele kosten van de integratie lager zijn dan de winst op het gebied van veiligheid of onderhoudbaarheid die het oplevert. Anders wordt het project een mooie case study over hoe je serieuze technische tijd kunt besteden aan het opzij schuiven van onzekerheid.

De grens is belangrijker dan de preek

Een veelgemaakte fout in discussies tussen C++ en Rust is de veronderstelling dat het gebruik van Rust automatisch gevaar wegneemt. Dat is niet het geval. Het verandert waar het gevaar zit. In HFT is die grensvraag vooral belangrijk omdat hete paden zelden eindigen op de taalgrens. Ze eindigen bij netwerkgrenzen, wachtrijgrenzen, planningsgrenzen, FFI-grenzen en grenzen voor de data-indeling.

Als een Rust-component een C++-uitwisselingsadapter moet binnenkomen, met een native wachtrij moet spreken, gegevens moet overhandigen aan een strategie-engine met strakke layout-aannames, of deterministisch gedrag moet handhaven over grensovergangen heen, dan is het echte technische werk niet "we gebruikten Rust." Het echte werk is hoe zorgvuldig de naad werd gedefinieerd en geverifieerd. Onveilig gedrag kan nog steeds voortkomen uit een ABI-mismatch, verwarring over de eigendommen, verborgen kopieën, fouten in de wachtrij of verrassingen op het gebied van de timing. De taal alleen is niet uw bestuursmodel. De grens is.

Dit is de reden waarom volwassen teams praten over een smal heet pad en een smal, onveilig oppervlak. Ze vertrouwen niet op slogans als 'geheugenveiligheid standaard' om een ​​fundamenteel systeemontwerpprobleem op te lossen. Goede teams stellen lelijkere en dus nuttiger vragen. Waar gebeurt de kopie? Waar is de wachtrij-hop? Welke kant is eigenaar van de buffer? Welk pad wijst toe? Wat gebeurt er tijdens tegendruk? Wat is herspeelbaar? Wat kan afzonderlijk worden gebenchmarkt, en wat moet end-to-end worden gebenchmarkt, omdat lokale overwinningen een lange traditie hebben om mondiale teleurstellingen te worden?

Praktische gevallen die de moeite waard zijn om eerst op te lossen

Het slimste eerste project is zelden 'het hete pad herschrijven'. Dat is het technische equivalent van het betreden van een huis en beslissen dat de eerste nuttige handeling het vervangen van het hele skelet is voordat wordt gecontroleerd welke leiding de keuken al onder water zet.

Het betere eerste project is een van deze:

Bewijswerk van feed-handler

Als het team ruzie maakt over de vraag of parseren, normaliseren, in de wachtrij plaatsen of overdragen werkelijk het latentieprobleem is, bouw dan eerst het bewijspad op. Leg representatief verkeer vast, speel het deterministisch af en dwing het systeem te bekennen waar tijd en jitter daadwerkelijk de keten binnenkomen. De meeste HFT-systemen hebben hier niet meer ideologie nodig. Ze hebben een betere leugendetector nodig.

Gateway en risicogrens opruimen

Veel stapels worden niet geruïneerd door de kernstrategielogica. Ze worden geruïneerd door slordigheid op de grens tussen risico, gateway-logica en operationele coördinatie. Een zorgvuldige herschrijving of herstructurering van die naden kan de betrouwbaarheid en diagnosticeerbaarheid verbeteren zonder het commerciële risico dat je als eerste de absoluut heetste lus raakt.

Hybride opruiming van het controlevlak

Als operatortools, implementatiehulpmiddelen, herstelhulpprogramma's of replay-tools kwetsbaar zijn, kan Rust daar een sterke kandidaat zijn. Deze componenten bepalen vaak de gezondheid van de hele organisatie, zelfs als ze zich niet in het snelste microsecondepad bevinden. Schonere tools kunnen het hete systeem rustiger maken zonder te doen alsof elk binair bestand in het landgoed dezelfde taal verdient.

Hands-On Lab: Bouw een kleine sequentie-gap-detector en maak deze eerlijk

Laten we het lab klein en nuttig houden. HFT systemen leven en sterven door sequentiediscipline lang voordat ze glamoureuze strategielogica bereiken. Dit speelgoedprogramma herhaalt een feed-achtige stream en rapporteert waar hiaten verschenen.

main.cpp

#include <cstdint>
#include <iostream>
#include <string>
#include <vector>

struct Packet {
    std::uint64_t seq;
    std::string payload;
};

struct Gap {
    std::uint64_t expected;
    std::uint64_t received;
};

class GapDetector {
public:
    void on_packet(const Packet& packet) {
        if (!started_) {
            expected_ = packet.seq + 1;
            started_ = true;
            return;
        }

        if (packet.seq != expected_) {
            gaps_.push_back({expected_, packet.seq});
        }

        expected_ = packet.seq + 1;
    }

    const std::vector<Gap>& gaps() const {
        return gaps_;
    }

private:
    bool started_ = false;
    std::uint64_t expected_ = 0;
    std::vector<Gap> gaps_;
};

int main() {
    std::vector<Packet> replay{
        {1001, "AAPL bid"},
        {1002, "AAPL ask"},
        {1003, "MSFT bid"},
        {1007, "MSFT ask"},
        {1008, "NVDA bid"},
        {1011, "NVDA ask"}
    };

    GapDetector detector;
    for (const auto& packet : replay) {
        detector.on_packet(packet);
    }

    if (detector.gaps().empty()) {
        std::cout << "no gaps\n";
        return 0;
    }

    for (const auto& gap : detector.gaps()) {
        std::cout << "gap expected=" << gap.expected
                  << " received=" << gap.received << "\n";
    }
}

Bouwen

Op Linux of macOS:

g++ -O2 -std=c++20 -o gap_detector main.cpp
./gap_detector

Op Windows:

cl /O2 /std:c++20 main.cpp
.\main.exe

Waarom deze kleine oefening ertoe doet

Omdat het de juiste manier van denken afdwingt:

  • deterministische statusupdate
  • eerlijke volgorde
  • herhaling vóór de theorie
  • begrensd, meetbaar gedrag

Dat is al meer HFT dan een verrassend aantal conferentiedia's.

Testtaken voor liefhebbers

  1. Porteer dezelfde detector naar Rust en vergelijk niet de ijdelheid, maar de duidelijkheid van de grenzen, de afhankelijkheidsfrictie en hoe gemakkelijk elke versie bij uw bestaande gereedschap past.
  2. Verleng de herhaling zodat ontbrekende pakketten later in de verkeerde volgorde kunnen aankomen en beslis vervolgens of de detector ze moet bufferen, afwijzen of markeren.
  3. Voeg de timing toe en meet het verschil tussen een vector-backed replay en een ring-buffer-backed replay.
  4. Introduceer een onnodige toewijzing op het warme pad en meet hoe snel een "kleine" beslissing het resultaat begint te vervuilen.
  5. Voeg een logtak toe in on_packet en kijk hoe snel de waarneembaarheid saboteert als deze onzorgvuldig wordt geplaatst.

Samenvatting

Het echte gesprek tussen C++ en Rust in HFT gaat niet over welke taal de mooiere mythologie verdient. Het gaat erom welke delen van het systeem directe controle nodig hebben, welke delen profiteren van sterkere standaarden, en welke grenzen eerlijk genoeg kunnen worden gemaakt om hybride ontwerp zonder waanvoorstellingen te ondersteunen.

C++ domineert nog steeds de populairste HFT-paden omdat het domein controle beloont over de geheugenindeling, wachtrijen, draadgedrag, profilering, herhaling en integratie met een volwassen ecosysteem met lage latentie. Rust is nuttig wanneer correctheid, explicietheid en onderhoudbaarheid meer waarde creëren dan extra wrijvingskosten voor het ecosysteem. Beide kunnen in een serieuze stapel thuishoren. De stap van volwassenen is om te beslissen waar, en om bewijsmateriaal in plaats van taalfandom de score te laten behouden.

Referenties

  1. NASDAQ TotalView-ITCH-specificatie: ITCH
  2. FIX Handelsgemeenschapsnormen: FIX
  3. DPDK-documentatie: https://doc.dpdk.org/guides/
  4. Linux tijdstempeldocumentatie: Linux
  5. Brendan Gregg over Flame Graphs: https://www.brendangregg.com/flamegraphs.html
  6. Het Rust prestatieboek: Rust
Philip P.

Philip P. – CTO

Terug naar Blogs

Contact

Begin het gesprek

Een paar duidelijke lijnen zijn voldoende. Beschrijf het systeem, de druk en de beslissing die wordt geblokkeerd. Of schrijf rechtstreeks naar midgard@stofu.io.

01 Wat het systeem doet
02 Wat doet het nu pijn
03 Welk besluit is geblokkeerd
04 Optioneel: logs, specificaties, sporen, diffs
0 / 10000
Geen bestand gekozen