C++, Rust, og høyfrekvent handel: der deterministisk ventetid bestemmer argumentet
Introduksjon
Programmeringsspråkdebatter tolereres vanligvis fordi de fleste systemer har råd til litt teater. En tjeneste er litt ineffektiv, en kø blir bredere enn den burde, en politikk for å prøve på nytt gjør noe moralsk tvilsomt, og alle fortsetter å bevege seg fordi produktet fortsatt fungerer, inntektene lander fortsatt, og latensdiagrammet er stygt på en overlevende måte.
Høyfrekvent handel er mindre sentimental. Det bryr seg ikke om hvilket språk som vant internett dette kvartalet. Det bryr seg om markedsdata blir stat, stat blir en beslutning, og beslutningen blir en ordre før vinduet lukkes. I slike omgivelser blir elegante meninger som ikke kan overleve målinger, raskt og vanligvis uten forvarsel ranet.
Det er derfor spørsmålet om C++ og Rust i HFT er interessant. Ikke fordi det ene språket er hellig og det andre er uredelig, men fordi HFT er et av de sjeldne domenene som tvinger hele argumentet til å utbetale faktisk systematferd. Den varme banen holder enten formen under press, eller så gjør den det ikke. Halelatens forblir enten disiplinert eller så gjør den det ikke. Replay forteller enten sannheten eller ikke. Arkitektur er ikke en personlighetstest der. Det er en faktura.
Dette er også grunnen til at svaret ikke er "C++ for alltid" eller "skriv om alt i Rust fordi sikkerhet er bra og frykt er en forretningsmodell." Det mer ærlige svaret er smalere og derfor mer nyttig. C++ dominerer fortsatt de hotteste HFT-banene fordi verden rundt verktøy, fôrhåndtering, minnekontroll, profilering og maskinvaretilstøtende praksis forblir ekstremt C++ formet. Rust er genuint nyttig rundt den kjernen, og noen ganger i nøye utvalgte deler av den, men det sletter ikke det grunnleggende faktum at handel med lav latens straffer abstraksjonsfeil raskere enn de fleste lag kan gi nytt navn til initiativet.
Så den rette samtalen handler ikke om identitet. Det handler om systemgrenser. Hvilke deler av stabelen trenger brutal kontroll over minne, layout, køer, affinitet og ledningsadferd? Hvilke deler drar mest nytte av sterkere korrekthetsbegrensninger og sikrere standardinnstillinger? Hvilke deler fortjener hybridbehandling i stedet for stammerenhet? De spørsmålene er langt mindre glamorøse enn språkprekener, men de er også spørsmålene som overlever kontakt med produksjonen.
Hvorfor HFT får dårlig teknisk filosofi til å se dyr ut
HFT er uvanlig flink til å avsløre en kjent ingeniørløgn: løgnen om at gjennomsnittlig oppførsel er nok. I mange vanlige produkter kan et system forbli respektabelt mens det skjuler sporadisk kaos bak gjennomstrømming, gjenforsøk eller brukerens tålmodighet. I HFT er gjennomsnittlig ventetid interessant, men haleadferd er ofte den delen som faktisk ydmyker deg. Et system som ser raskt ut til det rykker til feil tid, er ikke et raskt system i noen kommersielt meningsfylt forstand. Det er et selvtillitstriks med en benchmark vedlagt.
Det er derfor HFT ingeniører blir allergiske mot upresise abstraksjoner. De lærer at én ekstra tildeling på den varme banen ikke er «bare én tildeling». Det er en mulig kilde til jitter. Ett køhopp er ikke «bare ett køhopp». Det er et annet sted hvor tiden blir lagret, koordinasjonen utvides og synligheten blir dårligere. En cache-fiendtlig struktur er ikke bare en estetisk lyte. Det er en løpende skatt på hver markedshendelse som går gjennom systemet. Multipliser det med reelt fôrvolum, og plutselig blir et designvalg fra et lysbildekort en gjenganger i budsjettet for skuffelse.
Rust går inn i denne samtalen med legitim kraft fordi minnesikkerhet er viktig, samtidighet er viktig, og systemkode fortjener bedre standardverdier enn "vær forsiktig mens du sjonglerer med kniver over en grop." Den delen er sann. Men HFT belønner ikke sannhet isolert. Det belønner kombinert sannhet. Sikkerhet er viktig, ja. Det samme gjør modne fôrbehandlere, stabile ABI grenser, replay-verktøy, profildrevet iterasjon, moden utvekslingsintegrasjonskultur og muligheten til å inspisere nøyaktig hva maskinen gjør når markedet er uvennlig. C++ kommer fortsatt med mer av den omkringliggende infrastrukturen i de fleste HFT-miljøer.
Dette er en grunn til at kjøpere og ingeniørledere bør motstå renhetsfortellinger. Et språk kan være utmerket i en smal dimensjon og fortsatt være feil standard for den mest tidsfølsomme delen av en stabel hvis det omkringliggende økosystemet, verktøyene og teamopplevelsen ikke støtter den faktiske leveringsveien. HFT er hvor vakre lokale sannheter går for å lære at hele veien fortsatt betyr mer.
Stakken er ikke én ting, så språkvalget bør ikke late som noe annet
En av de dummeste feilene i seriøst systemarbeid er å snakke om "HFT-stakken" som om det var en enkelt teknisk organisme med ett foretrukket språk. Det er det ikke. Det er en samling av stier med svært forskjellige trykk og feilkostnader.
Banen for inntak av markedsdata har ett temperament. Oppdateringsbanen for ordreboken har en annen. Strategilogikk kan være numerisk tett, men strukturelt smal. Risikosjekker er ofte latenssensitive, men også korrekthetssensitive på en kjedelig, voksen, juridisk konsekvensmessig måte. Simulering og replay-infrastruktur kan sette pris på determinisme og introspeksjon over rå nanosekunders forfengelighet. Verktøy på kontrollplan, distribusjonshjelpere og operatøroverflater bryr seg mye mer om pålitelighet, vedlikehold og integreringshygiene enn de bryr seg om å barbere fem mikrosekunder fra en bane ingen kunde noen gang vil se.
Dette er viktig fordi det ofte er der en fornuftig C++ og Rust samtale begynner. C++ forblir sterkest når banen er brutalt varm, maskinvarebevisst, integreringstung og allerede omgitt av år med innfødt operasjonspraksis. Rust blir mer attraktiv når banen fortsatt er viktig, men den økonomiske verdien av sterkere mislighold, klarere eierskap og smalere eksponering for minnerisiko oppveier kostnadene ved økosystemfriksjon.
I praksis fører det ofte til hybride utfall. De heteste fôrhåndterings- og gatewaybanene forblir i C++. Replay-verktøy, konfigurasjonsvalidering, visse hjelpere på risikosiden, meldingsnormaliseringsverktøy, revisjonsverktøy eller interne operatørvendte komponenter kan være utmerkede Rust-kandidater. Dette er ikke ubesluttsomhet. Det er arkitektonisk voksenliv. Systemet blir behandlet som et sett med reelle grenser i stedet for som et språkfandom med et datasenter.
Der C++ fortsatt eier de hotteste stiene
C++ beholder sin plass i HFT av grunner som er mindre mystiske enn utenforstående noen ganger forestiller seg. Den første grunnen er minne- og layoutkontroll. HFT hot paths bryr seg om hvilke data som lever sammen, hvordan strukturer oppfører seg i cache, hvordan eierskap dukker opp under belastning, og om systemet kan forbli allokeringsdisiplinert når markedet slutter å være høflig. C++ gir fortsatt ingeniører uvanlig direkte innflytelse over disse valgene, og det gjør det i et økosystem som allerede har brukt flere tiår på å lære hvilke "små" kostnader som er hemmelig store.
Den andre grunnen er verktøytetthet. C++ i HFT betyr ikke bare et språk. Det betyr kompilatorer, desinfiseringsmidler, flammegrafer, C++, VTune, replay-seler, utvekslingsadaptere, folklore i kø, allokatorekspertise og en enorm mengde prestasjonskrigshistorier samlet under økonomisk press. Lag starter ikke fra null der. De arver en dyp operasjonell kultur, og den kulturen er viktig fordi HFT belønner målt iterasjon langt mer enn retorisk renslighet.
Den tredje grunnen er integrasjonstyngdekraften. Utvekslinger, native nettverksbaner, pakkefangstverktøy, optimalisering ved kjernen, FPGA-tilstøtende infrastruktur og hele økosystemet med lav latens er fortsatt svært komfortable i en C- og C++-verden. Rust kan samhandle med den verden, og noen ganger veldig effektivt, men "kan samhandle med" er ikke det samme som "er veien til minst friksjon gjennom hele systemet." I alvorlige HFT er friksjon ikke en følelsesmessig ulempe. Det er en mulig latensavgift, en feilsøkingsavgift og en leveringsavgift på samme tid.
Det er også en mer subtil grunn som betyr mer i AI-tiden: C++ har ganske enkelt mer operativt minne tilgjengelig rundt dette arbeidet. AI kodesystemer, kodesøk, offentlige eksempler, leverandørbiter, optimizer-folklore og feilsøkingsstier er tettere rundt C++ i systemer med lav latens enn rundt Rust. Det gjør ikke C++ edlere. Det gjør det lettere for mennesker og AI verktøy å samarbeide inne i stygge ekte kodebaser hvis sjarm utløp for mange år siden.
Hvor Rust faktisk hjelper i stedet for å utføre moral
Rust hjelper de fleste når det løser et reelt problem i stedet for å fungere som personlighetstilbehør for arkitekturdiagrammer. I HFT vises de sterkeste Rust-brukstilfellene ofte rundt den varme kjernen i stedet for i det absolutte sentrum av den.
Rust er nyttig for komponenter der korrekthetsfeil er dyre, men latensbudsjettet ikke måles med et mikroskop. Meldingsvalideringslag, konfigurasjons- og distribusjonsverktøy, visse protokollnormaliseringsbaner, kontrolltjenester, administrative verktøy, offline-analysatorer og interne operatørverktøy kan dra nytte av språkets skjevhet mot eksplisitthet. Poenget der er ikke å se moderne ut. Poenget er å redusere klassen av dumme, repeterende, strukturelt unngåelige feil som trekker oppmerksomheten fra viktigere arbeid.
Rust kan også hjelpe til med nøye utvalgte nesten varme komponenter når teamet har riktig ekspertise og grensen er ærlig. En parser med lav latens, en avgrenset tilstandsmaskin eller et stykke deterministisk infrastruktur kan være en solid Rust-kandidat hvis teamet kan holde FFI og allokeringshistorien under kontroll, og hvis den omkringliggende økosystembyrden er forstått på forhånd i stedet for å bli oppdaget klokken 2:40 om morgenen under en utrulling.
Men det er akkurat her lagene trenger disiplin. Rust er ikke verdifull når den slippes inn i midten av en naturlig handelsstabel som en trosbasert renovering. Det er verdifullt når grensen er ren, målebanen er åpenbar, og driftskostnadene for integrasjonen er lavere enn sikkerhets- eller vedlikeholdsgevinsten den skaper. Ellers blir prosjektet en vakker case-studie i hvordan man kan bruke seriøs ingeniørtid på å flytte usikkerhet sidelengs.
Grensen betyr mer enn prekenen
En vanlig feil i C++ versus Rust diskusjoner er å anta at bruk av Rust automatisk fjerner fare. Det gjør det ikke. Det endrer hvor faren sitter. I HFT er dette grensespørsmålet spesielt viktig fordi varme stier sjelden ender ved språklinjen. De ender ved nettverksgrenser, køgrenser, planleggingsgrenser, FFI grenser og grenser for dataoppsett.
Hvis en Rust-komponent må krysse inn i en C++-utvekslingsadapter, snakke med en innfødt kø, levere data til en strategimotor med stramme layoutforutsetninger, eller opprettholde deterministisk oppførsel på tvers av grenseoverganger, så er ikke det virkelige ingeniørarbeidet "vi brukte Rust." Det virkelige arbeidet er hvor nøye sømmen ble definert og verifisert. Usikker oppførsel kan fortsatt komme gjennom ABI mismatch, eierskapsforvirring, skjulte kopier, køfeil eller tidsoverraskelser. Språket alene er ikke din styringsmodell. Grensen er.
Dette er grunnen til at modne lag snakker om en smal varm sti og en smal utrygg overflate. De er ikke avhengige av slagord som "minnesikkerhet som standard" for å løse det som fundamentalt er et systemdesignproblem. Gode lag stiller styggere og derfor mer nyttige spørsmål. Hvor skjer kopien? Hvor er køhoppet? Hvilken side eier bufferen? Hvilken sti tildeler? Hva skjer under mottrykk? Hva er gjenspillbart? Hva kan benchmarkes isolert sett, og hva må benchmarkes ende-til-ende fordi lokale seire har lang tradisjon for å bli globale skuffelser?
Praktiske saker verdt å løse først
Det smarteste første prosjektet er sjelden «rewrite the hot path». Det er den tekniske ekvivalenten med å gå inn i et hus og bestemme den første nyttige handlingen er å bytte ut hele skjelettet før du sjekker hvilket rør som allerede oversvømmer kjøkkenet.
Det bedre første prosjektet er ett av disse:
Fôrbehandler bevisarbeid
Hvis teamet krangler om hvorvidt parsing, normalisering, kø eller overlevering virkelig er latensproblemet, bygg bevisveien først. Fang representativ trafikk, spill den deterministisk på nytt, og tving systemet til å innrømme hvor tid og jitter faktisk kommer inn i kjeden. De fleste HFT systemer trenger ikke mer ideologi her. De trenger en bedre løgndetektor.
Gateway og risikogrenseopprydding
Mange stabler blir ikke ødelagt av kjernestrategilogikken. De er ødelagt av grensesurvet mellom risiko, gateway-logikk og operasjonell koordinering. En forsiktig omskriving eller omstrukturering i disse sømmene kan forbedre påliteligheten og diagnostiseringen uten den kommersielle risikoen ved å berøre den absolutt hotteste sløyfen først.
Hybrid kontroll-plan opprydding
Hvis operatørverktøy, distribusjonshjelpere, gjenopprettingsverktøy eller replay-verktøy er skjøre, kan Rust være en sterk kandidat der. Disse komponentene former ofte helsen til hele organisasjonen selv når de ikke sitter i den raskeste mikrosekundbanen. Renere verktøy kan gjøre det varme systemet roligere uten å late som om hver binær i boet fortjener det samme språket.
Hands-On Lab: Bygg en liten sekvens-gap-detektor og gjør den ærlig
La oss holde laboratoriet lite og nyttig. HFT systemer lever og dør av sekvensdisiplin lenge før de når glamorøs strategilogikk. Dette leketøysprogrammet spiller av en strømlignende strøm og rapporterer hvor hull dukket opp.
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";
}
}
Bygge
På Linux eller macOS:
g++ -O2 -std=c++20 -o gap_detector main.cpp
./gap_detector
På Windows:
cl /O2 /std:c++20 main.cpp
.\main.exe
Hvorfor denne lille øvelsen er viktig
Fordi det tvinger frem den riktige typen tenkning:
- deterministisk tilstandsoppdatering
- ærlig sekvensering
- replay før teori
- avgrenset, målbar atferd
Det er allerede mer HFT enn et overraskende antall konferanselysbilder.
Testoppgaver for entusiaster
- Port den samme detektoren til Rust og sammenlign ikke benchmark-forfengelighet, men grenseklarhet, avhengighetsfriksjon og hvor enkelt hver versjon passer til ditt eksisterende verktøy.
- Forleng avspillingen slik at manglende pakker senere kan komme ut av drift, og avgjør om detektoren skal bufre, avvise eller flagge dem.
- Legg til timing og mål forskjellen mellom en vektor-støttet replay og en ring-buffer-støttet replay.
- Introduser én unødvendig allokering på den varme banen og mål hvor raskt en "liten" beslutning begynner å forurense resultatet.
- Legg til en logggren i
on_packetog se hvor raskt observerbarhet blir sabotasje når den plasseres uforsiktig.
Sammendrag
Den virkelige C++ og Rust samtalen i HFT handler ikke om hvilket språk som fortjener den finere mytologien. Det handler om hvilke deler av systemet som trenger direkte kontroll, hvilke deler som drar nytte av sterkere standardinnstillinger, og hvilke grenser som kan gjøres ærlige nok til å støtte hybriddesign uten vrangforestillinger.
C++ dominerer fortsatt de hotteste HFT-banene fordi domenet belønner kontroll over minneoppsett, kødannelse, ledningsatferd, profilering, replay og integrasjon med et modent økosystem med lav latens. Rust er nyttig der korrekthet, eksplisitt og vedlikeholdsvennlighet skaper mer verdi enn ekstra økosystemfriksjonskostnader. Begge kan høre til i en seriøs stabel. Voksentrekket er å bestemme hvor, og å la bevis i stedet for språkfandom holde poeng.
Referanser
- NASDAQ TotalView-ITCH spesifikasjon: ITCH
- FIX Handelsfellesskapsstandarder: FIX
- DPDK-dokumentasjon: https://doc.dpdk.org/guides/
- Linux dokumentasjon for tidsstempling: Linux
- Brendan Gregg på Flame Graphs: https://www.brendangregg.com/flamegraphs.html
- Rust Performance Book: Rust