C++, Rust ja High Frequency Trading: Missä deterministinen latenssi ratkaisee väitteen

C++, Rust ja High Frequency Trading: Missä deterministinen latenssi ratkaisee väitteen

C++, Rust ja High Frequency Trading: Missä deterministinen latenssi ratkaisee väitteen

Johdanto

Ohjelmointikieliset keskustelut ovat yleensä suvaita, koska useimmilla järjestelmillä on varaa vähän teatteria. Palvelu on vähän tehoton, jono levenee kuin sen pitäisi, uudelleenyrityspolitiikka tekee jotain moraalisesti kyseenalaista ja kaikki jatkavat liikkeessä, koska tuote toimii edelleen, tulot laskeutuvat edelleen ja latenssikaavio on ruma selviytymiskelpoisella tavalla.

Korkean taajuuden kaupankäynti on vähemmän sentimentaalista. Sillä ei ole väliä, mikä kieli voitti Internetin tällä vuosineljänneksellä. Se välittää siitä, muuttuuko markkinatiedoista tila, tilasta päätös ja päätöksestä tilaus ennen kuin ikkuna sulkeutuu. Tällaisessa ympäristössä elegantit mielipiteet, jotka eivät kestä mittausta, ryöstetään nopeasti ja yleensä ilman varoitusta.

Tästä syystä kysymys C++:sta ja Rust:sta HFT:ssä on mielenkiintoinen. Ei siksi, että yksi kieli on pyhä ja toinen petollinen, vaan koska HFT on yksi harvoista aloista, joka pakottaa koko väitteen rahaksi järjestelmän todellisessa toiminnassa. Kuuma polku joko säilyttää muotonsa paineen alaisena tai ei. Hännän latenssi joko pysyy kurinalaisena tai ei. Replay joko kertoo totuuden tai ei. Arkkitehtuuri ei ole siellä persoonallisuustesti. Se on lasku.

Tästä syystä vastaus ei ole "C++ ikuisesti" tai "kirjoita kaikkea uudelleen Rustiin, koska turvallisuus on hyvä asia ja pelko on liiketoimintamalli." Rehellisempi vastaus on kapeampi ja siksi hyödyllisempi. C++ hallitsee edelleen kuumimpia HFT polkuja, koska ympäröivä työkalujen, syötteiden käsittelyn, muistinhallinnan, profiloinnin ja laitteiston kanssa vierekkäisten käytäntöjen maailma on edelleen erittäin C++ muotoinen. Rust on aidosti hyödyllinen tämän ytimen ympärillä ja joskus sen huolellisesti valituissa osissa, mutta se ei poista perusasiaa, että alhaisen latenssin kaupankäynti rankaisee abstraktiovirheitä nopeammin kuin useimmat tiimit voivat nimetä aloitteen uudelleen.

Oikea keskustelu ei siis ole identiteetistä. Kyse on järjestelmän rajoista. Mitkä pinon osat tarvitsevat julman muistin, asettelun, jonojen, affiniteetin ja johtojen toiminnan hallinnan? Mitkä osat hyötyvät eniten vahvemmista oikeellisuusrajoituksista ja turvallisemmista oletusarvoista? Mitkä osat ansaitsevat hybridikäsittelyn heimopuhtauden sijaan? Nämä kysymykset ovat paljon vähemmän lumoavia kuin kielisaarnat, mutta ne ovat myös kysymyksiä, jotka säilyvät kosketuksessa tuotantoon.

Miksi HFT saa huonon teknisen filosofian näyttämään kalliilta?

HFT on epätavallisen hyvä paljastamaan tutun insinöörivalheen: valheen, jonka mukaan keskimääräinen käyttäytyminen riittää. Monissa tavallisissa tuotteissa järjestelmä voi pysyä kunniallisena piilottaen satunnaisen kaaoksen suorituskyvyn, uudelleenyritysten tai käyttäjän kärsivällisyyden taakse. HFT:ssä keskimääräinen latenssi on mielenkiintoinen, mutta hännän käyttäytyminen on usein se osa, joka todella nöyryyttää sinua. Järjestelmä, joka näyttää nopealta, kunnes se nykii väärään aikaan, ei ole nopea järjestelmä kaupallisesti mielekkäässä mielessä. Se on luottamustemppu, johon on liitetty vertailukohta.

Tästä syystä HFTin insinöörit ovat allergisia epätarkille abstraktioille. He oppivat, että yksi ylimääräinen allokaatio kuumalla polulla ei ole "vain yksi allokaatio". Se on mahdollinen tärinän lähde. Yksi jonohyppy ei ole "vain yksi jonohyppy". Se on toinen paikka, johon aika tallentuu, koordinaatio laajenee ja näkyvyys huononee. Yksi välimuistiin vihamielinen rakenne ei ole vain esteettinen virhe. Se on jatkuva vero jokaisesta järjestelmän läpi kulkevasta markkinatapahtumasta. Kerro se todellisella syöttömäärällä, ja yhtäkkiä diakannen suunnitteluvalinnasta tulee pettymysbudjetin toistuva rivikohta.

Rust astuu tähän keskusteluun oikeutetulla voimalla, koska muistin turvallisuus ja samanaikaisuuden oikeellisuus ovat tärkeitä ja järjestelmäkoodi ansaitsee parempia oletusarvoja kuin "ole varovainen jongleeraatessasi veitsillä kuopan päällä". Tuo osa on totta. Mutta HFT ei palkitse totuutta erikseen. Se palkitsee yhdistetyn totuuden. Turvallisuus on tärkeää, kyllä. Samoin kypsät rehunkäsittelijät, vakaat ABI-rajat, toistotyökalut, profiiliohjattu iteraatio, kypsä vaihdon integraatiokulttuuri ja mahdollisuus tarkastaa tarkasti, mitä kone tekee, kun markkinat ovat epäystävälliset. C++ tulee yhä enemmän ympäröivää infrastruktuuria useimpiin HFT-ympäristöihin.

Tämä on yksi syy, miksi ostajien ja insinöörijohtajien tulisi vastustaa puhtauskertomuksia. Kieli voi olla erinomainen kapeassa ulottuvuudessa ja silti väärä oletusarvo pinon ajoitusherkimmälle osalle, jos ympäröivä ekosysteemi, työkalut ja tiimin kokemus eivät tue varsinaista toimituspolkua. HFT on paikka, jossa ihanat paikalliset totuudet oppivat, että koko polku on edelleen tärkeämpi.

Pino ei ole yksi asia, joten kielivalinnan ei pitäisi teeskennellä toisin

Yksi typerimmistä virheistä vakavassa järjestelmätyössä on puhua "HFT-pinosta" ikään kuin se olisi yksi tekninen organismi, jolla on yksi suositeltu kieli. Se ei ole. Se on kokoelma polkuja, joilla on hyvin erilaiset paineet ja epäonnistumiskustannukset.

Markkinatietojen käsittelypolulla on yksi temperamentti. Tilauskirjan päivityspolulla on toinen. Strategialogiikka voi olla numeerisesti tiheä, mutta rakenteellisesti kapea. Riskitarkistukset ovat usein latenssiherkkiä, mutta myös oikeellisuusherkkiä tylsällä, aikuisella, lainmukaisella tavalla. Simulaatio- ja toistoinfrastruktuuri voi arvostaa determinismia ja itsetutkiskelua raakana nanosekunnin turhamaisuuden sijaan. Ohjaustason työkalut, käyttöönoton apulaitteet ja käyttöpinnat välittävät luotettavuudesta, ylläpidettävyydestä ja integrointihygieniasta paljon enemmän kuin ne välittävät viiden mikrosekunnin ajoreitistä, jota kukaan asiakas ei koskaan näe.

Tällä on merkitystä, koska siitä alkaa usein järkevä C++- ja Rust-keskustelu. C++ pysyy vahvimpana, kun polku on raa'an kuuma, laitteistotietoinen, integraatioraskas ja sitä ympäröi jo vuosien natiivi käyttökäytäntö. Rust:sta tulee houkuttelevampi, kun polku on edelleen tärkeä, mutta vahvempien maksuhäiriöiden, selkeämmän omistajuuden ja suppeamman muistiriskialtistuksen taloudellinen arvo ylittää ekosysteemien kitkan kustannukset.

Käytännössä se johtaa usein hybridituloksiin. Kuumimmat syötteenkäsittely- ja yhdyskäytäväreitit pysyvät C++issa. Toistotyökalut, asetusten tarkistus, tietyt riskipuolen apuohjelmat, viestien normalisointiapuohjelmat, auditointityökalut tai sisäiset käyttäjäkohtaiset komponentit voivat olla erinomaisia ​​Rust-ehdokkaita. Tämä ei ole päättämättömyyttä. Se on arkkitehtonista aikuisuutta. Järjestelmää käsitellään pikemminkin todellisten rajojen joukkona kuin kielifandomina, jossa on datakeskus.

Missä C++ omistaa edelleen kuumimmat polut

C++ säilyttää paikkansa HFT:ssä syistä, jotka ovat vähemmän mystisiä kuin ulkopuoliset joskus kuvittelevat. Ensimmäinen syy on muistin ja asettelun hallinta. HFT kuumat polut välittävät siitä, mitkä tiedot elävät yhdessä, kuinka rakenteet käyttäytyvät välimuistissa, kuinka omistajuus näkyy kuormitettuna ja voiko järjestelmä pysyä allokoinnin kurinalaisena, kun markkinat lakkaavat olemasta kohteliaita. C++ antaa edelleen insinööreille epätavallisen suoraa vaikutusvaltaa näihin valintoihin, ja se tekee niin ekosysteemissä, joka on jo vuosikymmeniä oppinut, mitkä "pienet" kustannukset ovat salaa suuria.

Toinen syy on työkalujen tiheys. C++ in HFT ei tarkoita vain kieltä. Se tarkoittaa kääntäjiä, desinfiointiaineita, liekkikaavioita, C++, VTunea, toistovaljaita, vaihtosovittimia, jonotusperinteitä, allokaattoriasiantuntemusta ja valtavaa joukkoa taloudellisen paineen alaisena kertynyttä suorituskykysotatarinoita. Joukkueet eivät aloita siellä nollasta. He perivät syvän toimintakulttuurin, ja sillä on merkitystä, koska HFT palkitsee mitatun iteroinnin paljon enemmän kuin retorista puhtautta.

Kolmas syy on integraation painovoima. Vaihteet, alkuperäiset verkkopolut, pakettien sieppaustyökalut, ytimen viereinen optimointi, FPGA:n viereinen infrastruktuuri ja koko matalan viiveen ekosysteemi ovat edelleen erittäin mukavia C- ja C++-maailmassa. Rust voi olla vuorovaikutuksessa tuon maailman kanssa, ja joskus erittäin tehokkaasti, mutta "voi olla vuorovaikutuksessa" ei ole sama asia kuin "on pienimmän kitkan polku koko järjestelmän läpi". Vakavassa HFTissa kitka ei ole emotionaalinen haitta. Se on samanaikaisesti mahdollinen latenssivero, virheenkorjausvero ja toimitusvero.

On myös hienovaraisempi syy, joka on tärkeämpi AI-aikakaudella: C++:lla on vain enemmän toimintamuistia käytettävissä tämän työn ympärille. AI-koodausjärjestelmät, koodihaku, julkiset esimerkit, toimittajan katkelmat, optimoijien kansanperinteet ja virheenkorjausreitit ovat tiheämpiä C++:n ympärillä matalaviiveisissä järjestelmissä kuin Rustissa. Se ei tee C++:sta jalompaa. Se helpottaa ihmisten ja AI-työkalujen yhteistyötä rumien todellisten koodikantojen sisällä, joiden viehätys vanheni vuosia sitten.

Missä Rust todella auttaa moraalin suorittamisen sijaan

Rust auttaa eniten, kun se ratkaisee todellista ongelmaa sen sijaan, että se toimisi arkkitehtuurikaavioiden persoonallisuuden lisävarusteena. HFT:ssä vahvimmat Rust-käyttötapaukset näkyvät usein kuuman ytimen ympärillä sen sijaan, että ne olisivat sen absoluuttisessa keskustassa.

Rust on hyödyllinen komponenteille, joissa virheellisyydet ovat kalliita, mutta latenssibudjettia ei mitata mikroskoopilla. Viestien vahvistuskerrokset, konfigurointi- ja käyttöönottotyökalut, tietyt protokollien normalisointipolut, ohjauspalvelut, hallintaohjelmat, offline-analysaattorit ja sisäiset operaattorin työkalut voivat hyötyä kielen eksplisiittisyydestä. Tarkoitus ei ole näyttää nykyaikaiselta. Tarkoituksena on vähentää tyhmien, toistuvien, rakenteellisesti vältettävissä olevien virheiden luokkaa, jotka vievät huomion tärkeämmästä työstä.

Rust voi myös auttaa huolella valituissa lähes kuumissa komponenteissa, kun tiimillä on oikea asiantuntemus ja raja on rehellinen. Pienen latenssin jäsentäjä, rajoitetun tilan kone tai osa determinististä infrastruktuuria voivat olla vankka Rust-ehdokas, jos tiimi pystyy pitämään FFI- ja allokointitarinan hallinnassa ja jos ympäröivä ekosysteemin kuormitus ymmärretään etukäteen sen sijaan, että se havaitaan kello 2.40 aamulla käyttöönoton aikana, jota kukaan ei halunnut.

Mutta juuri tässä joukkueet tarvitsevat kurinalaisuutta. Rust ei ole arvokas, kun se pudotetaan alkuperäisen kauppapinon keskelle uskoon perustuvana remonttina. Se on arvokasta, kun raja on puhdas, mittauspolku selvä ja integroinnin käyttökustannukset ovat alhaisemmat kuin sen luoma turvallisuus- tai ylläpidettävyyshyöty. Muuten projektista tulee kaunis tapaustutkimus, kuinka käyttää vakavaa insinööriaikaa siirtämällä epävarmuutta sivuttain.

Rajalla on enemmän merkitystä kuin saarnalla

Yleinen virhe C++-keskusteluissa Rust-keskusteluissa on oletus, että Rust:n käyttäminen poistaa vaaran automaattisesti. Se ei tee. Se muuttaa vaaran sijaintia. HFT:ssä tämä rajakysymys on erityisen tärkeä, koska kuumat polut päättyvät harvoin kieliriville. Ne päättyvät verkon rajoihin, jonorajoihin, ajoitusrajoihin, FFI-rajoihin ja data-asettelun rajoihin.

Jos Rust-komponentin täytyy siirtyä C++-vaihtosovittimeen, puhua natiivijonolle, luovuttaa tiedot strategiamoottorille tiukoilla layout-olettamuksilla tai ylläpitää determinististä käyttäytymistä rajasiirtymien yli, todellinen suunnittelutyö ei ole "käytimme Rust". Todellinen työ on se, kuinka huolellisesti sauma määriteltiin ja tarkistettiin. Epäturvallista toimintaa voi silti esiintyä ABI-epävastaavuuden, omistajuuden hämmennyksen, piilokopioiden, jonotusvirheiden tai ajoitusyllätysten vuoksi. Pelkkä kieli ei ole hallintomallisi. Raja on.

Tästä syystä kypsät tiimit puhuvat kapeasta kuumasta polusta ja kapeasta vaarallisesta pinnasta. He eivät luota iskulauseisiin, kuten "muistin turvallisuus oletusarvoisesti", ratkaistakseen pohjimmiltaan järjestelmän suunnitteluongelman. Hyvät tiimit kysyvät rumia ja siten hyödyllisempiä kysymyksiä. Missä kopiointi tapahtuu? Missä on jonohyppy? Kumpi puoli omistaa puskurin? Mikä polku jakaa? Mitä tapahtuu vastapaineen aikana? Mikä on toistettavissa? Mitä voidaan verrata erillään ja mitä on verrattava päästä päähän, koska paikallisilla voitoilla on pitkä perinne muuttua maailmanlaajuisiksi pettymyksiksi?

Käytännön tapaukset, jotka kannattaa ratkaista ensin

Älykkäin ensimmäinen projekti on harvoin "kirjoita kuuma polku uudelleen". Se on tekninen vastine taloon astumiselle ja päättämiselle, että ensimmäinen hyödyllinen teko on vaihtaa koko luuranko ennen kuin tarkistetaan, mikä putki jo tulvii keittiöön.

Parempi ensimmäinen projekti on yksi näistä:

Rehunkäsittelijän todisteet toimivat

Jos ryhmä kiistää siitä, onko jäsentäminen, normalisointi, jonottaminen tai yhteysvastuun vaihto todellakin latenssiongelma, luo ensin todisteiden polku. Kaappaa edustavaa liikennettä, toista se deterministisesti ja pakota järjestelmä tunnustamaan, missä aika ja tärinä todella tulevat ketjuun. Useimmat HFT-järjestelmät eivät tarvitse lisää ideologiaa tähän. He tarvitsevat paremman valheenpaljastimen.

Yhdyskäytävän ja riskirajojen puhdistus

Ydinstrategialogiikka ei pilaa monia pinoja. Ne ovat pilalla riskin, yhdyskäytävän logiikan ja toiminnan koordinoinnin välisen rajahuimauksen vuoksi. Huolellinen uudelleenkirjoitus tai uudelleenjärjestely noissa saumoissa voi parantaa luotettavuutta ja diagnosoitavuutta ilman kaupallista riskiä koskettaa ehdottomasti kuuminta silmukkaa ensin.

Hybridiohjaustason puhdistus

Jos operaattorityökalut, käyttöönottoapuohjelmat, palautustyökalut tai toistotyökalut ovat hauraita, Rust voi olla siinä vahva ehdokas. Nämä komponentit muokkaavat usein koko organisaation terveyttä, vaikka ne eivät olekaan nopeimmalla mikrosekunnin polulla. Puhtaammat työkalut voivat tehdä kuumasta järjestelmästä rauhallisemman teeskentelemättä, että kaikki kiinteistön binaarit ansaitsevat saman kielen.

Hands-On Lab: Rakenna pieni sekvenssivälin ilmaisin ja tee siitä rehellinen

Pidetään laboratorio pienenä ja hyödyllisenä. HFT-järjestelmät elävät ja kuolevat sekvenssikurin mukaan kauan ennen kuin ne saavuttavat lumoavan strategialogiikan. Tämä leluohjelma toistaa syötemäisen streamin ja raportoi aukoista.

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";
    }
}

Rakentaa

Linux tai macOS:

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

Windows:

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

Miksi tällä pienellä harjoituksella on merkitystä

Koska se pakottaa oikeanlaisen ajattelun:

  • deterministinen tilapäivitys
  • rehellinen sekvensointi
  • toista ennen teoriaa
  • rajallinen, mitattavissa oleva käyttäytyminen

Se on jo enemmän HFT kuin yllättävä määrä konferenssidioja.

Testitehtävät harrastajille

  1. Yhdistä sama ilmaisin Rust:iin ja vertaa rajojen selkeyttä, riippuvuuskitkaa ja sitä, kuinka helposti kukin versio sopii olemassa oleviin työkaluihisi.
  2. Laajenna toistoa niin, että puuttuvat paketit voivat myöhemmin saapua epäkunnossa, ja päätä sitten, pitäisikö ilmaisimen puskuroida, hylätä vai merkitä ne.
  3. Lisää ajoitus ja mittaa ero vektori- ja rengaspuskurituetun toiston välillä.
  4. Ota käyttöön yksi tarpeeton jako kuumalle polulle ja mittaa kuinka nopeasti "pieni" päätös alkaa saastuttaa tulosta.
  5. Lisää hakkuuhaara koodin on_packet sisään ja katso kuinka nopeasti havainnointi muuttuu sabotaasiksi, kun se asetetaan huolimattomasti.

Yhteenveto

Todellinen C++- ja Rust-keskustelu HFT:ssä ei ole sitä, mikä kieli ansaitsee mukavamman mytologian. Kyse on siitä, mitkä järjestelmän osat tarvitsevat suoraa ohjausta, mitkä osat hyötyvät vahvemmista oletusarvoista ja mitkä rajat voidaan tehdä tarpeeksi rehellisiksi tukemaan hybridisuunnittelua ilman harhaa.

C++ hallitsee edelleen kuumimpia HFT polkuja, koska verkkotunnus palkitsee muistin asettelun, jonotuksen, johtojen käyttäytymisen, profiloinnin, toiston ja integroinnin kypsän matalan latenssin ekosysteemin kanssa. Rust on hyödyllinen silloin, kun oikeellisuus, selkeys ja ylläpidettävyys luovat enemmän arvoa kuin ylimääräiset ekosysteemin kitkakustannukset. Molemmat voivat kuulua vakavaan pinoon. Aikuisten tehtävä on päättää missä ja antaa todisteiden säilyttää arvosanan kielen fanimin sijaan.

Viitteet

  1. NASDAQ TotalView-ITCH: ITCH
  2. FIX Kaupankäynnin yhteisön standardit: FIX
  3. DPDK-dokumentaatio: https://doc.dpdk.org/guides/
  4. Linux aikaleimadokumentaatio: Linux
  5. Brendan Gregg Flame Graphsista: https://www.brendangregg.com/flamegraphs.html
  6. Rust Performance Book: Rust
Philip P.

Philip P. – CTO

Takaisin Blogeihin

Ota yhteyttä

Aloita keskustelu

Muutama selkeä viiva riittää. Kuvaile järjestelmää, painetta ja päätöstä, joka on estetty. Tai kirjoita suoraan osoitteeseen midgard@stofu.io.

01 Mitä järjestelmä tekee
02 Mikä nyt sattuu
03 Mikä päätös on estetty
04 Valinnainen: lokit, tiedot, jäljet, erot
0 / 10000
Tiedostoa ei ole valittu