C++ Rusland: hoe het gebeurde

Als je aan het begin van het stuk zegt dat er C++-code aan de muur hangt, dan zal het je aan het einde ongetwijfeld in de voet schieten.

Bjarne Stroustrup

Van 31 oktober tot en met 1 november vond in St. Petersburg de C++ Russia Piter-conferentie plaats - een van de grootschalige programmeerconferenties in Rusland, georganiseerd door JUG Ru Group. Gastsprekers zijn onder meer leden van de C++ Standards Committee, CppCon-sprekers, O'Reilly-boekauteurs en beheerders van projecten zoals LLVM, libc++ en Boost. De conferentie is bedoeld voor ervaren C++-ontwikkelaars die hun expertise willen verdiepen en ervaringen willen uitwisselen op het gebied van live communicatie. Studenten, studenten en universiteitsdocenten krijgen hele mooie kortingen.

De Moskou-editie van de conferentie zal al in april volgend jaar te bezoeken zijn, maar in de tussentijd zullen onze studenten je vertellen welke interessante dingen ze tijdens het laatste evenement hebben geleerd. 

C++ Rusland: hoe het gebeurde

Foto van conferentie-album

Wie zijn wij?

Twee studenten van de National Research University Higher School of Economics - St. Petersburg werkten aan dit bericht:

  • Liza Vasilenko is een 4e jaars student die Programmeertalen studeert als onderdeel van het programma Toegepaste Wiskunde en Informatica. Nadat ik in mijn eerste jaar aan de universiteit kennis had gemaakt met de taal C++, heb ik er vervolgens via stages in de industrie ervaring mee opgedaan. Mijn passie voor programmeertalen in het algemeen en functioneel programmeren in het bijzonder heeft zijn stempel gedrukt op de selectie van rapporten op de conferentie.
  • Danya Smirnov is een 1e jaars student van het masterprogramma “Programming and Data Analysis”. Toen ik nog op school zat, schreef ik Olympiade-problemen in C++, en toen gebeurde het op de een of andere manier dat de taal voortdurend ter sprake kwam in onderwijsactiviteiten en uiteindelijk de belangrijkste werktaal werd. Ik besloot deel te nemen aan de conferentie om mijn kennis te verbeteren en ook om nieuwe mogelijkheden te leren kennen.

In de nieuwsbrief deelt de faculteitsleiding vaak informatie over educatieve evenementen gerelateerd aan ons specialisme. In september zagen we informatie over C++ Rusland en besloten we ons te registreren als luisteraar. Dit is onze eerste ervaring met deelname aan dergelijke conferenties.

Conferentiestructuur

  • Доклады

In de loop van twee dagen lazen experts 30 rapporten, waarin veel actuele onderwerpen aan bod kwamen: ingenieus gebruik van taalfuncties om toegepaste problemen op te lossen, komende taalupdates in verband met de nieuwe standaard, compromissen in het C++-ontwerp en voorzorgsmaatregelen bij het omgaan met de gevolgen ervan, voorbeelden van interessante projectarchitectuur, evenals enkele details onder de motorkap van de taalinfrastructuur. Er vonden tegelijkertijd drie uitvoeringen plaats, meestal twee in het Russisch en één in het Engels.

  • Discussiezones

Na de toespraak werden alle niet gestelde vragen en onafgemaakte discussies overgebracht naar speciaal aangewezen ruimtes voor communicatie met de sprekers, uitgerust met markeerborden. Een goede manier om de pauze tussen de toespraken door te brengen met een prettig gesprek.

  • Lightning Talks en informele discussies

Als u een kort verslag wilt geven, kunt u zich op het whiteboard aanmelden voor de avond Lightning Talk en krijgt u vijf minuten de tijd om iets over het conferentieonderwerp te vertellen. Bijvoorbeeld een korte introductie over ontsmettingsmiddelen voor C++ (voor sommigen was het nieuw) of een verhaal over een bug in het genereren van sinusgolven die alleen hoorbaar is, maar niet gezien.

Een ander format is de paneldiscussie ‘Met een Hart tot Hart Comité’. Op het podium staan ​​enkele leden van de standaardisatiecommissie, op de projector staat een open haard (officieel - om een ​​oprechte sfeer te creëren, maar de reden “omdat ALLES IN BRAND staat” lijkt grappiger), vragen over de standaard en de algemene visie van C++ , zonder verhitte technische discussies en holiwars. Het bleek dat in de commissie ook levende mensen zitten die misschien ergens niet helemaal zeker van zijn of iets niet weten.

Voor fans van holivars bleef het derde evenement op de agenda: de BOF-sessie “Go vs. C++”. We nemen een Go-liefhebber, een C++-liefhebber, vóór aanvang van de sessie bereiden ze samen 100500 slides voor over een onderwerp (zoals problemen met pakketten in C++ of het gebrek aan generieke geneesmiddelen in Go), en daarna hebben ze een levendige discussie met elkaar en met het publiek, en het publiek probeert twee standpunten tegelijk te begrijpen. Als een holivar uit de context begint, komt de moderator tussenbeide en verzoent de partijen. Dit format is verslavend: enkele uren na de start was nog maar de helft van de slides voltooid. Het einde moest enorm worden versneld.

  • Partner staat

De partners van de conferentie waren vertegenwoordigd in de hallen; op de stands spraken ze over lopende projecten, boden stages en werk aan, hielden quizzen en kleine wedstrijden en verlootten ook leuke prijzen. Tegelijkertijd boden sommige bedrijven zelfs aan om de eerste fasen van interviews te doorlopen, wat nuttig zou kunnen zijn voor degenen die niet alleen naar rapporten kwamen luisteren.

Technische details van de rapporten

We hebben beide dagen naar reportages geluisterd. Soms was het moeilijk om één rapport uit de parallelle rapporten te kiezen - we spraken af ​​om op te splitsen en de opgedane kennis tijdens de pauzes uit te wisselen. En toch lijkt het erop dat er veel wordt weggelaten. Hier willen we het hebben over de inhoud van enkele van de rapporten die wij het meest interessant vonden

Uitzonderingen in C++ door het prisma van compileroptimalisaties, Roman Rusyaev

C++ Rusland: hoe het gebeurde
Schuif van presentaties

Zoals de titel suggereert, bekeek Roman het werken met uitzonderingen met LLVM als voorbeeld. Tegelijkertijd kan het rapport voor degenen die Clang niet gebruiken in hun werk toch een idee geven van hoe de code mogelijk kan worden geoptimaliseerd. Dit komt omdat de ontwikkelaars van compilers en bijbehorende standaardbibliotheken met elkaar communiceren en veel succesvolle oplossingen kunnen samenvallen.

Om een ​​uitzondering af te handelen, moet je dus een heleboel dingen doen: de afhandelingscode aanroepen (indien aanwezig) of grondstoffen vrijmaken op het huidige niveau en de stapel hoger laten draaien. Dit alles leidt ertoe dat de compiler aanvullende instructies toevoegt voor oproepen die mogelijk uitzonderingen veroorzaken. Als de uitzondering niet daadwerkelijk wordt gegenereerd, zal het programma dus nog steeds onnodige acties uitvoeren. Om de overhead op de een of andere manier te verminderen, heeft LLVM verschillende heuristieken voor het bepalen van situaties waarin code voor het afhandelen van uitzonderingen niet hoeft te worden toegevoegd of het aantal “extra” instructies kan worden verminderd.

De spreker onderzoekt er ongeveer een dozijn en laat zowel situaties zien waarin ze de uitvoering van programma's helpen versnellen, als situaties waarin deze methoden niet toepasbaar zijn.

Roman Rusyaev leidt studenten dus tot de conclusie dat code die uitzonderingsafhandeling bevat niet altijd kan worden uitgevoerd zonder overhead, en geeft het volgende advies:

  • bij het ontwikkelen van bibliotheken is het de moeite waard om in principe uitzonderingen te laten varen;
  • als er nog steeds uitzonderingen nodig zijn, is het de moeite waard om waar mogelijk overal noexcept (en const) modifiers toe te voegen, zodat de compiler zoveel mogelijk kan optimaliseren.

Over het algemeen bevestigde de spreker het standpunt dat uitzonderingen het beste tot een minimum kunnen worden beperkt of helemaal kunnen worden afgeschaft.

De rapportdia's zijn beschikbaar via de volgende link: ["C++-uitzonderingen door de lens van LLVM-compileroptimalisaties"]

Generatoren, coroutines en andere hersenkrakende zoetigheden, Adi Shavit

C++ Rusland: hoe het gebeurde
Schuif van presentaties

Een van de vele rapporten op deze conferentie gewijd aan innovaties in C++20 was niet alleen gedenkwaardig vanwege de kleurrijke presentatie, maar ook vanwege de duidelijke identificatie van bestaande problemen met de logica voor verzamelingsverwerking (for loop, callbacks).

Adi Shavit benadrukt het volgende: de momenteel beschikbare methoden doorlopen de hele verzameling en bieden geen toegang tot een interne tussenstatus (of wel in het geval van callbacks, maar met een groot aantal onaangename bijwerkingen, zoals Callback Hell) . Het lijkt erop dat er iterators zijn, maar zelfs met hen is alles niet zo soepel: er zijn geen gemeenschappelijke entry- en exitpunten (begin → end versus rbegin → rend enzovoort), het is niet duidelijk hoe lang we zullen itereren? Vanaf C++20 zijn deze problemen opgelost!

Eerste optie: bereiken. Door iterators in te pakken, krijgen we een gemeenschappelijke interface voor het begin en einde van een iteratie, en krijgen we ook de mogelijkheid om te componeren. Dit alles maakt het eenvoudig om volwaardige dataverwerkingspijplijnen te bouwen. Maar niet alles verloopt zo soepel: een deel van de berekeningslogica bevindt zich in de implementatie van een specifieke iterator, wat het begrijpen en debuggen van de code lastig kan maken.

C++ Rusland: hoe het gebeurde
Schuif van presentaties

Welnu, voor dit geval heeft C++20 coroutines toegevoegd (functies waarvan het gedrag vergelijkbaar is met generatoren in Python): de uitvoering kan worden uitgesteld door een huidige waarde terug te geven terwijl een tussenliggende status behouden blijft. Zo kunnen we niet alleen werken met gegevens zoals deze verschijnen, maar ook alle logica in een specifieke coroutine inkapselen.

Maar er zit een vlieg in de zalf: op dit moment worden ze slechts gedeeltelijk ondersteund door bestaande compilers, en worden ze ook niet zo netjes geïmplementeerd als we zouden willen: het is bijvoorbeeld nog niet de moeite waard om referenties en tijdelijke objecten in coroutines te gebruiken. Bovendien zijn er enkele beperkingen op wat coroutines kunnen zijn, en constexpr-functies, constructors/destructors en main zijn niet opgenomen in deze lijst.

Coroutines lossen dus een aanzienlijk deel van de problemen op met de eenvoud van dataverwerkingslogica, maar hun huidige implementaties vereisen verbetering.

materialen:

C++-trucs van Yandex.Taxi, Anton Polukhin

In mijn professionele activiteiten moet ik soms puur aanvullende zaken implementeren: een wrapper tussen de interne interface en de API van een bibliotheek, loggen of parseren. In dit geval is er meestal geen behoefte aan aanvullende optimalisatie. Maar wat als deze componenten worden gebruikt in enkele van de meest populaire services op RuNet? In zo'n situatie zul je alleen al terabytes per uur aan logs moeten verwerken! Dan telt elke milliseconde en daarom moet je toevlucht nemen tot verschillende trucjes - Anton Polukhin vertelde erover.

Misschien wel het meest interessante voorbeeld was de implementatie van het pointer-to-implementation (pimpl)-patroon. 

#include <third_party/json.hpp> //PROBLEMS! 
struct Value { 
    Value() = default; 
    Value(Value&& other) = default; 
    Value& operator=(Value&& other) = default; 
    ~Value() = default; 

    std::size_t Size() const { return data_.size(); } 

private: 
    third_party::Json data_; 
};

In dit voorbeeld wil ik eerst de headerbestanden van externe bibliotheken verwijderen - dit zal sneller compileren en je kunt jezelf beschermen tegen mogelijke naamconflicten en andere soortgelijke fouten. 

Oké, we hebben #include verplaatst naar het .cpp-bestand: we hebben een forward-declaratie van de ingepakte API nodig, evenals std::unique_ptr. Nu hebben we dynamische toewijzingen en andere onaangename zaken, zoals gegevens verspreid over een hele reeks gegevens en verminderde garanties. std::aligned_storage kan hierbij helpen. 

struct Value { 
// ... 
private: 
    using JsonNative = third_party::Json; 
    const JsonNative* Ptr() const noexcept; 
    JsonNative* Ptr() noexcept; 

    constexpr std::size_t kImplSize = 32; 
    constexpr std::size_t kImplAlign = 8; 
    std::aligned_storage_t<kImplSize, kImplAlign> data_; 
};

Het enige probleem: je moet de grootte en uitlijning voor elke wrapper opgeven - laten we onze pimpl-sjabloon met parameters maken , gebruik enkele willekeurige waarden en voeg een vinkje toe aan de destructor dat we alles goed hebben: 

~FastPimpl() noexcept { 
    validate<sizeof(T), alignof(T)>(); 
    Ptr()->~T(); 
}

template <std::size_t ActualSize, std::size_t ActualAlignment>
static void validate() noexcept { 
    static_assert(
        Size == ActualSize, 
        "Size and sizeof(T) mismatch"
    ); 
    static_assert(
        Alignment == ActualAlignment, 
        "Alignment and alignof(T) mismatch"
    ); 
}

Omdat T al is gedefinieerd bij het verwerken van de destructor, wordt deze code correct geparseerd en wordt in de compilatiefase de vereiste grootte en uitlijningswaarden weergegeven die als fouten moeten worden ingevoerd. Dus ten koste van één extra compilatierun schrappen we de dynamische toewijzing van verpakte klassen, verbergen we de API in een .cpp-bestand met de implementatie en krijgen we ook een ontwerp dat geschikter is voor caching door de processor.

Logging en parsing leken minder indrukwekkend en zullen daarom in deze review niet worden vermeld.

De rapportdia's zijn beschikbaar via de volgende link: ["C++-trucs van Taxi"]

Moderne technieken om uw code DROOG te houden, Björn Fahller

In deze lezing laat Björn Fahller verschillende manieren zien om de stilistische fout van herhaalde conditiecontroles te bestrijden:

assert(a == IDLE || a == CONNECTED || a == DISCONNECTED);

Klinkt bekend? Door verschillende krachtige C++-technieken te gebruiken die in recente standaarden zijn geïntroduceerd, kunt u op elegante wijze dezelfde functionaliteit implementeren zonder dat dit ten koste gaat van de prestaties. Vergelijken:   

assert(a == any_of(IDLE, CONNECTED, DISCONNECTED));

Om een ​​onbepaald aantal controles af te handelen, moet u onmiddellijk variadische sjablonen en vouwuitdrukkingen gebruiken. Laten we aannemen dat we de gelijkheid van verschillende variabelen met het state_type-element van de enum willen controleren. Het eerste dat in je opkomt is het schrijven van een helperfunctie is_any_of:


enum state_type { IDLE, CONNECTED, DISCONNECTED };

template <typename ... Ts>
bool is_any_of(state_type s, const Ts& ... ts) { 
    return ((s == ts) || ...); 
}

Dit tussenresultaat is teleurstellend. Tot nu toe wordt de code niet beter leesbaar:

assert(is_any_of(state, IDLE, DISCONNECTING, DISCONNECTED)); 

Niet-type sjabloonparameters zullen de situatie enigszins helpen verbeteren. Met hun hulp zullen we de opsombare elementen van de enum overbrengen naar de lijst met sjabloonparameters: 

template <state_type ... states>
bool is_any_of(state_type t) { 
    return ((t == states) | ...); 
}
	
assert(is_any_of<IDLE, DISCONNECTING, DISCONNECTED>(state)); 

Door auto te gebruiken in een niet-type sjabloonparameter (C++17), generaliseert de aanpak eenvoudigweg naar vergelijkingen, niet alleen met state_type-elementen, maar ook met primitieve typen die kunnen worden gebruikt als niet-type sjabloonparameters:


template <auto ... alternatives, typename T>
bool is_any_of(const T& t) {
    return ((t == alternatives) | ...);
}

Door deze opeenvolgende verbeteringen wordt de gewenste vloeiende syntaxis voor controles bereikt:


template <class ... Ts>
struct any_of : private std::tuple<Ts ...> { 
// поленимся и унаследуем конструкторы от tuple 
        using std::tuple<Ts ...>::tuple;
        template <typename T>
        bool operator ==(const T& t) const {
                return std::apply(
                        [&t](const auto& ... ts) {
                                return ((ts == t) || ...);
                        },
                        static_cast<const std::tuple<Ts ...>&>(*this));
        }
};

template <class ... Ts>
any_of(Ts ...) -> any_of<Ts ... >;
 
assert(any_of(IDLE, DISCONNECTING, DISCONNECTED) == state);

In dit voorbeeld dient de deductiegids om de gewenste structuursjabloonparameters voor te stellen aan de compiler, die de typen constructorargumenten kent. 

Verder - interessanter. Bjorn leert hoe je de resulterende code kunt generaliseren voor vergelijkingsoperatoren voorbij ==, en vervolgens voor willekeurige bewerkingen. Gaandeweg worden functies als het attribuut no_unique_address (C++20) en sjabloonparameters in lambda-functies (C++20) uitgelegd aan de hand van gebruiksvoorbeelden. (Ja, nu is de syntaxis van lambda zelfs nog gemakkelijker te onthouden - dit zijn vier opeenvolgende paren haakjes van allerlei aard.) De uiteindelijke oplossing waarbij functies als constructordetails worden gebruikt, verwarmt mijn ziel, om nog maar te zwijgen van de uitdrukking tupel in de beste tradities van lambda rekening.

Vergeet op het einde niet om het op te poetsen:

  • Vergeet niet dat lambda's gratis constexpr zijn; 
  • Laten we perfecte forwarding toevoegen en kijken naar de lelijke syntaxis ervan in relatie tot het parameterpakket in de lambda-afsluiting;
  • Laten we de compiler meer mogelijkheden geven voor optimalisaties met voorwaardelijke noexcept; 
  • Laten we zorgen voor begrijpelijker foutuitvoer in sjablonen dankzij expliciete retourwaarden van lambdas. Dit dwingt de compiler om meer controles uit te voeren voordat de sjabloonfunctie daadwerkelijk wordt aangeroepen - in de typecontrolefase. 

Voor details verwijzen wij naar het lesmateriaal: 

Onze indrukken

Onze eerste deelname aan C++ Rusland was gedenkwaardig vanwege de intensiteit ervan. Ik kreeg de indruk van C++ Rusland als een oprecht evenement, waar de grens tussen training en live communicatie bijna onmerkbaar is. Alles, van de stemming van de sprekers tot de wedstrijden van de evenementpartners, is bevorderlijk voor verhitte discussies. De inhoud van de conferentie, bestaande uit rapporten, bestrijkt een vrij breed scala aan onderwerpen, waaronder C++-innovaties, case studies van grote projecten en ideologische architecturale overwegingen. Maar het zou oneerlijk zijn om de sociale component van het evenement te negeren, die taalbarrières helpt overwinnen, niet alleen met betrekking tot C++.

Wij danken de organisatoren van de conferentie voor de mogelijkheid om aan een dergelijk evenement deel te nemen!
Misschien heb je het bericht van de organisatoren over het verleden, heden en de toekomst van C++ Rusland gezien op de JUG Ru-blog.

Bedankt voor het lezen, en we hopen dat het opnieuw vertellen van de gebeurtenissen nuttig was!

Bron: www.habr.com

Voeg een reactie