Hlavní navigace

Vlákno názorů ke článku C++20: Jak se budí korutiny od alex6bbc - C++11 pridalo novou syntaxi napr. &&, to by...

  • 31. 10. 2022 22:40

    alex6bbc

    C++11 pridalo novou syntaxi napr. &&, to by se mi libilo kdyby se primo do C++ pridal channel a goroutiny jako v Golangu :-) netusim jaka by byla vhodna syntaxe, ale ze by se to stalo organickou soucasti jazyka.

  • 31. 10. 2022 22:51

    Ondřej Novák

    Channel a gorutiny mi pripominaji bash script :)

    Korutina vraci future promennou, kterou muzes priradit kam chceš než získá hodnotu. Až když ji potřebuješ do výpočtu tak si ji vyzvedneš přes co_await. Neni to podobne? Nebo můžeš do korutiny nastrkat nekolik cocls::queue a pak v jine korutine ty fronty cist a máš to jako pipes

  • 2. 11. 2022 23:40

    Králík

    Channly v Gočku jsou prostě MPMC fronty. Přirovnávní s shellem není na místě, shell pipes jsou SPSC. Nejsem nějaký kdovíjaký fanoušek Gočka, ale ty jejich channely jsou implemetovány velmi dobře - mají velmi slušný výkon, ale přitom jsou poměrně flexibilní - je možné rozdělit frontu na producer/consumer nebo nechat v jednom a je možné specifikovat maximální kapacitu.

    Vzehldem k tomu, že si sám píšeš MPMC frontu, Go channels bych nepodceňoval a podíval se na jejich implementaci, naivně implementovanou frontu+runtime pravděpodobně roznesou svým výkonem na kopytech :-) ... bez urážky. Stejnětak jsou hodny pozornosti channels v Rustu. Určitě by bylo záhodno nabízet zastropovatelnou frontu (kvůli propagaci backpressure).

  • 3. 11. 2022 17:14

    Ondřej Novák

    naivně implementovanou frontu+runtime pravděpodobně roznesou svým výkonem na kopytech

    Chtěl bych napsat _UKAŽ_. Trochu mi to tu zavání vírou a fundamentalismem, než nějakou technickou diskuzí. Co mi brání napsat frontu v C++ která se výkonově vyrovná? Jsem schopen napsat frontu rychleji jako prasárnu, nebo si C++ překladač poradí s výkonem i když použiju std::queue. Jen tak mimochodem, std::queue na tom s výkonem není vůbec špatně (oproti třeba implementaci rour ve Win32 :-D, které jsou realizované jako pole bajtů, které se šoupe pamětí - tedy stav ve WinXP, od té doby jsem byl v linuxu, tak nevím)

    Ale můj argument je trochu jinde. Drtivá většina korutin, ale i threadů nepotřebuje na předání výsledku frontu. To musí být obrovský výkonový overhead, když pro předání výsledku musím alokovat prostor pro výměnu dat. Zrovna v korutinách si mohu předat jen pointer i kdyby výsledkem byla třeba komplikovaná struktura nebo velký objekt.

    Zastropovaná fronta je problém. Jak řešíš její naplnění? Výjimkou. No a dál? Je to vlastně řešení? Nehledě na to, že si to můžeš řešit samozřejmě sám, zeptáš se na velikost fronty a pokud je moc velká, hodíš výjimku - třeba si můžeš ten objekt podědit a doimplementovat si to. Jasně, rozumím tomu, že zastropovaná fronta má jednodušší memory managment. Ale tam se získa promile výkonu, nestojí za optimalizaci - a vůbec, klidně mohu tu frontu udělat i genericky, jako že konkrétní implementaci fronty si dodá uživatel jak parametr šablony.

  • 4. 11. 2022 0:20

    Králík

    Trochu mi to tu zavání vírou a fundamentalismem, než nějakou technickou diskuzí. Co mi brání napsat frontu v C++ která se výkonově vyrovná?
    Hlavně v klidu. Bylo to myšleno jako inspirace. V C++ samozřejmě je možné napsat rychlou frontu.

    Ale můj argument je trochu jinde. Drtivá většina korutin, ale i threadů nepotřebuje na předání výsledku frontu.
    Samozřejmě. Proto taky třeba ty gorutiny mají normální návratovné hodnoty. Fronta/kanál se použije pouze pokud to je potřeba.

    Zastropovaná fronta je problém. Jak řešíš její naplnění? Výjimkou.
    No to rozhodně ne. Jednak zmiňované jazyky výjimky ani nemají a jednak tím by se úplně popřel smysl zastropované fronty. Korutina, která se snaží zapsat do plné fronty je uspána, dokud se ve frontě neudělá místo. Tzn. když máš třeba skupinu producerů nějakých dat a nějaký consumer [nebo několik], obvykle je vhodné použít na to MPSC [MPMC] frontu se stropem, protože když z nějakého důvodu dojde třeba ke zpomalení consumerů, nechceš, aby fronta začala bobnat v paměti do závratných objemů. (Samozřejmě je možné tohle signalizovat producerům i nějak bokem / mimo frontu, ale často právě přes frontu to je dostačující a celkem elegantní řešení.)

    Jasně, rozumím tomu, že zastropovaná fronta má jednodušší memory managment.
    Není jednoduší, je to stejné. Zastropovaná neznamená konstantní velikost / konstantní alokaci, ta fronta je stále alokovaná úplně stejně dynamicky jako nezastropovaná, pouze je tam navíc ta garance, že nepřekročí nikdy určitou velikost (nevyleze mi z paměti, když se zaseknout consumeři).

  • 4. 11. 2022 9:37

    Ondřej Novák

    Jako člověk, co pracuje s producenty typu "burzovní informace" nejsem s řešením pozastavení producenta moc spokojen

    Go mimochodem hodí výjimku, vyzkoušeno (nebo možná chybu, nezkoušel jsem, jestli se dá zachytit)

    Zastavování producera může vést k deadlocku. Například pokud je ta fronta oboustraná tedy ten vztah je symetrický a obě strany se přeplní. Takový pattern request-response, kdy requester vygeneruje tolik requestů, že zaplní request frontu zatímco responder zaplní response frontu. Pak requester místo aby vybíral respond frontu je bloklý na request frontě, která je plná. Znám moc dobře tyhle bolestivé situace z praxe.

    Ale k tomu se možná spíš hodí publisher-subscriber, který tohle má řešeno různou formou, například přeskočením fronty na její top, nebo přeskakování na konci, když už subscriber (což je role konzumenta) nestíhá a fronta se mu pod rukama maže, začne přeskakovat data. Tohle si lze samozřejmě v nějakém option vybrat přímo na subscriberovi, s tím, že jedna z options je samozřejmě vyhození výjimky na straně subscribera, když nestíhý - aniž bych ovlivnil publishera.

    Zpomalování producera/publishe­ra nechci řešit na téhle základní úrovni, mimochodem by to znamenalo další frontu čekajících korutin, další signalizační mechanismus, další komplikace jinak jednoduchého kódu. Pokud to někdo potřebuje, ať si to podědí, a doimplementuje si to. Asi není těžký udělata awaitable operaci push, awaitera k tomu určenému a mechanismus probouzení čekající producerů, jakmile někdo udělá pop().

  • 4. 11. 2022 10:56

    Králík

    Jako člověk, co pracuje s producenty typu "burzovní informace" nejsem s řešením pozastavení producenta moc spokojen
    Samozřejmě, existují producery, kde je kritické, aby produkovaly. Ale i v takovém případě bude nejspíše zastavení producera frontou menší katastrofa než bad_allocy vyskakující na X různých místech v kódu nebo si nechat sestřelit OOM killerem celý proces... Pokud dojde paměť, ten producer stejně tak jako tak nemůže pokračovat (nemá kam dávat výsledky)... Samozřejmě záleží na konkrétní situaci, reálně bude možná spíš řešením použít kafku nebo tak něco... Ale to už bychom se dostali někam úplně jinam...

    Go mimochodem hodí výjimku, vyzkoušeno (nebo možná chybu, nezkoušel jsem, jestli se dá zachytit)
    Výjimku Go opravdu nevyhodí. Možná ti to z nějakého důvodu panikuje, což může vypadat jako výjimka (a je to i implementováno podobně, ale <i>není</i> to mechanismus oštření chyb, spíš něco jako assert). Pokud ti to panikovalo, možná ses pokoušel zapisovat do zavřeného kanálu. Jinak zápis do plného kanálu by opravdu měl pozastavit korutinu, případně je možné se dotázat kanálu, jestli je možné aktuálně zapisovat (nevzpomínám si na syntaxi, ale jde to).

    Zastavování producera může vést k deadlocku.
    To je velmi obecný argument, v MT/multitask programování může vést k deadlocku leccos. S korutinami si můžeš uhnat deadlock i na jednom vláknu, a to dokonce i v tom Rustu (kde kompilátor umí eliminovat race-conditions, ale deadlocky ne). Prostě je potřeba si dát na deadlocky pozor obecně :-)

    mimochodem by to znamenalo další frontu čekajících korutin, další signalizační mechanismus
    Jistě. Což ale není žádná katastrofa. Různé implementace to řeší různě. V Rust Tokio frameworku jsou k dispozici dva různé typy kanálů (V C++ by se řeklo třídy, rust nemá třídy), zastropovaný a nezastropovaný. Anebo je možné to implementovat v jednom a mít třeba dva různé konstruktory a v tom bez stropu prostě nenaalokovat frontu tasků na vstupu. To už jsou implementační detaily...

  • 19. 2. 2023 0:03

    František Ryšánek

    Omluva za exhumaci, čtu diskusi po delší době od publikace.
    A nepobírám korutiny, a jsem hobbík, ale kdysi jsem psal nějaký protokolový stack, který dělal request/response (modbusovou gateway). Takže chci zareagovat na toto:

    Zastavování producera může vést k deadlocku. Například pokud je ta fronta oboustraná tedy ten vztah je symetrický a obě strany se přeplní. Takový pattern request-response, kdy requester vygeneruje tolik requestů, že zaplní request frontu zatímco responder zaplní response frontu. Pak requester místo aby vybíral respond frontu je bloklý na request frontě, která je plná. Znám moc dobře tyhle bolestivé situace z praxe.

    Toto jsem rozdělil do dvou vláken. Jedno pro TX (request), druhé pro RX (response). Každé vlákno smí nést riziko "usnutí na neurčito" (zablokování) pouze v jednom bodě - a pokud se pracuje s frontou ošetřenou pomocí podmínkové proměnné, tak k tomu blokování třeba při čekání na I/O nesmí dojít při zamčeném mutexu této fronty+proměnné. Kritická sekce kolem "manipulace s frontou" má být minimalistická, má se vrátit bez zbytečného zdržování a bez rizika náhodných chybových stavů.

  • 20. 2. 2023 22:30

    Ondřej Novák

    No ja radši používám nekonečné fronty. Nám takhle umřel server hry Prima Trefa ve špičce při reklamě během serialu "slunečná". Přišlo desitky tisíc requestů během sekundy a všechny fronty kolem všech vláken vydeadlockovaly, protože měly limit. Proč jsem na strojích s pamětí nekolik desitek GB daval limity? Asi jsem měl zatmění

  • 3. 11. 2022 17:29

    Ondřej Novák

    No a je to tam, mohu si definovat jak implementaci vlastní fronty, tak fronty čekajících korutin (klidně si tam mohu dát i frontu o jednom prvku, jen na to zatím není patřičná třída, ale to je primitivní) a mohu si určit i implementaci zámku, takže v single thread prostředí si tam místo toho mohu dát prázdný zámek a nic se zamykat nebude.

    https://github.com/ondra-novak/coroutines_classes/blob/master/src/coclasses/queue.h#L22