C++ Rusija: kako se to dogodilo

Ako na početku predstave kažete da na zidu visi C++ kod, onda će vam na kraju sigurno pucati u nogu.

Bjarne Stroustrup

Od 31. oktobra do 1. novembra u Sankt Peterburgu je održana konferencija C++ Russia Piter - jedna od velikih programskih konferencija u Rusiji u organizaciji JUG Ru Grupe. Pozvani govornici uključuju članove C++ Standards Committee, CppCon govornike, O'Reillyjeve autore knjiga i održavatelje projekata kao što su LLVM, libc++ i Boost. Konferencija je namijenjena iskusnim C++ programerima koji žele produbiti svoju stručnost i razmijeniti iskustva u komunikaciji uživo. Studentima, postdiplomcima i univerzitetskim nastavnicima obezbeđeni su veoma lepi popusti.

Moskovsko izdanje konferencije moći će se posjetiti već u aprilu iduće godine, a u međuvremenu će vam naši studenti ispričati koje su zanimljive stvari naučili na prošlom događaju. 

C++ Rusija: kako se to dogodilo

Fotografija iz konferencijski album

О нас

Na ovom postu su radila dva studenta Visoke škole ekonomije Nacionalnog istraživačkog univerziteta - Sankt Peterburg:

  • Liza Vasilenko je student 4. godine dodiplomskih studija koji studira programske jezike u okviru programa primijenjene matematike i računarstva. Nakon što sam se upoznao sa jezikom C++ na prvoj godini fakulteta, kasnije sam stekao iskustvo u radu s njim kroz praksu u industriji. Moja strast prema programskim jezicima općenito, a posebno funkcionalnom programiranju ostavila je traga na odabiru izvještaja na konferenciji.
  • Danya Smirnov je student 1. godine master programa „Programiranje i analiza podataka“. Još u školi sam pisao olimpijske zadatke u C++-u, a onda se nekako dogodilo da se taj jezik stalno javljao u obrazovnim aktivnostima i na kraju postao glavni radni jezik. Odlučio sam da učestvujem na konferenciji kako bih unapredio svoje znanje i naučio o novim mogućnostima.

U biltenu, rukovodstvo fakulteta često dijeli informacije o edukativnim događajima vezanim za našu specijalnost. U septembru smo vidjeli informacije o C++ Rusiji i odlučili da se registrujemo kao slušaoci. Ovo je naše prvo iskustvo učešća na ovakvim konferencijama.

Struktura konferencije

  • Izvještaji

Tokom dva dana, stručnjaci su pročitali 30 izvještaja, koji pokrivaju mnoge vruće teme: genijalna upotreba jezičnih karakteristika za rješavanje primijenjenih problema, predstojeća ažuriranja jezika u vezi s novim standardom, kompromisi u C++ dizajnu i mjere opreza pri radu s njihovim posljedicama, primjeri zanimljive arhitekture projekta, kao i nekih detalja ispod haube jezičke infrastrukture. Tri predstave su se odvijale istovremeno, najčešće dva na ruskom i jedan na engleskom.

  • Zone za diskusiju

Nakon govora, sva nepostavljena pitanja i nedovršene diskusije prebačene su u posebno određene prostore za komunikaciju sa govornicima, opremljene markerima. Dobar način da pauzu između govora odvojite ugodnim razgovorom.

  • Munjeviti razgovori i neformalne diskusije

Ako želite dati kratak izvještaj, možete se prijaviti na beloj tabli za večernji Lightning Talk i dobiti pet minuta vremena za razgovor o bilo čemu o temi konferencije. Na primjer, brzi uvod u sredstva za čišćenje za C++ (za neke je to bilo novo) ili priča o grešci u generiranju sinusnih valova koja se može samo čuti, ali ne i vidjeti.

Drugi format je panel diskusija „Od srca do srca odbora“. Na bini su neki članovi odbora za standardizaciju, na projektoru je kamin (zvanično - da bi se stvorila iskrena atmosfera, ali razlog „jer SVE GORI“ deluje smešnije), pitanja o standardu i opštoj viziji C++-a , bez žučnih tehničkih rasprava i holiwara. Ispostavilo se da u komitetu postoje i živi ljudi koji možda u nešto nisu sasvim sigurni ili nešto ne znaju.

Za ljubitelje holivara ostao je treći događaj - BOF sesija “Go vs. C++”. Uzimamo Go ljubitelja, ljubitelja C++, prije početka sesije oni zajedno pripreme 100500 slajdova na temu (poput problema s paketima u C++ ili nedostatka generika u Go), a zatim imaju živu diskusiju između sebe i sa publikom, a publika pokušava da razume dve tačke gledišta odjednom. Ako holivar krene van konteksta, moderator interveniše i pomiruje strane. Ovaj format stvara ovisnost: nekoliko sati nakon početka, samo polovina slajdova je završena. Kraj je morao biti jako ubrzan.

  • Partnerski štandovi

Partneri konferencije bili su zastupljeni u salama - na štandovima su pričali o aktuelnim projektima, nudili praksu i zapošljavanje, održavali kvizove i mala takmičenja, a takođe su izvlačili lepe nagrade. Istovremeno, neke kompanije su čak ponudile da prođu kroz početne faze intervjua, što bi moglo biti korisno za one koji nisu došli samo da slušaju izvještaje.

Tehnički detalji izvještaja

Oba dana smo slušali izvještaje. Ponekad je bilo teško izabrati jedan izveštaj od paralelnih – dogovorili smo se da se raziđemo i razmenimo znanja stečena u pauzama. Pa čak i tako, čini se da je mnogo toga izostavljeno. Ovdje bismo željeli govoriti o sadržaju nekih od izvještaja koji su nam bili najzanimljiviji

Izuzeci u C++ kroz prizmu optimizacije kompajlera, Roman Rusjajev

C++ Rusija: kako se to dogodilo
Slide from prezentacije

Kao što naslov sugeriše, Roman je posmatrao rad sa izuzecima koristeći LLVM kao primer. U isto vrijeme, za one koji ne koriste Clang u svom radu, izvještaj još uvijek može dati neku ideju o tome kako bi se kod potencijalno mogao optimizirati. To je zato što programeri kompajlera i odgovarajućih standardnih biblioteka međusobno komuniciraju i mnoga uspješna rješenja mogu se poklopiti.

Dakle, da biste obradili izuzetak, morate učiniti mnogo stvari: pozvati kod za rukovanje (ako postoji) ili osloboditi resurse na trenutnom nivou i podići stog više. Sve ovo dovodi do činjenice da kompajler dodaje dodatne instrukcije za pozive koji potencijalno bacaju izuzetke. Stoga, ako se izuzetak zapravo ne pokrene, program će i dalje izvoditi nepotrebne radnje. Kako bi nekako smanjio opterećenje, LLVM ima nekoliko heurističkih metoda za određivanje situacija u kojima se ne mora dodati kod za obradu izuzetaka ili se broj “dodatnih” instrukcija može smanjiti.

Govornik ispituje desetak njih i pokazuje situacije u kojima pomažu da se ubrza izvršavanje programa i one u kojima ove metode nisu primjenjive.

Dakle, Roman Rusyaev dovodi studente do zaključka da kod koji sadrži obradu izuzetaka ne može uvek da se izvrši sa nultim troškovima, i daje sledeće savete:

  • kada se razvijaju biblioteke, vrijedi napustiti izuzetke u principu;
  • ako su izuzeci i dalje potrebni, onda kad god je to moguće vrijedi svuda dodati modifikatore noexcept (i const) kako bi kompajler mogao optimizirati što je više moguće.

Općenito, govornik je potvrdio stav da je izuzetke najbolje iskoristiti na minimum ili ih potpuno napustiti.

Slajdovi izvještaja dostupni su na sljedećem linku: [“C++ izuzeci kroz sočivo optimizacije LLVM kompajlera”]

Generatori, korutine i druge slatkoće koje odmotaju mozak, Adi Shavit

C++ Rusija: kako se to dogodilo
Slide from prezentacije

Jedan od brojnih izvještaja na ovoj konferenciji posvećenih inovacijama u C++20 ostao je nezaboravan ne samo po svojoj živopisnoj prezentaciji, već i po jasnoj identifikaciji postojećih problema sa logikom obrade kolekcije (for petlja, povratni pozivi).

Adi Shavit ističe sljedeće: trenutno dostupne metode prolaze kroz cijelu kolekciju i ne daju pristup nekom internom međustanju (ili to čine u slučaju povratnih poziva, ali sa velikim brojem neugodnih nuspojava, kao što je Callback Hell) . Čini se da postoje iteratori, ali čak ni sa njima nije sve tako glatko: nema zajedničkih ulaznih i izlaznih tačaka (početak → kraj nasuprot rbegin → rend i tako dalje), nije jasno koliko dugo ćemo iterirati? Počevši od C++20, ovi problemi su riješeni!

Prva opcija: rasponi. Omotavanjem iteratora dobijamo zajednički interfejs za početak i kraj iteracije, a takođe dobijamo i mogućnost sastavljanja. Sve ovo olakšava izgradnju punopravnih cevovoda za obradu podataka. Ali nije sve tako glatko: dio logike izračunavanja nalazi se unutar implementacije specifičnog iteratora, što može zakomplikovati kod za razumijevanje i otklanjanje grešaka.

C++ Rusija: kako se to dogodilo
Slide from prezentacije

Pa, za ovaj slučaj, C++20 je dodao korutine (funkcije čije je ponašanje slično generatorima u Pythonu): izvršenje se može odgoditi vraćanjem neke trenutne vrijednosti uz očuvanje međustanja. Na taj način postižemo ne samo rad s podacima kako izgledaju, već i kapsuliranje cijele logike unutar određene korutine.

Ali postoji muha: trenutno ih samo djelimično podržavaju postojeći kompajleri, a također nisu implementirani onako uredno kako bismo željeli: na primjer, još uvijek se ne isplati koristiti reference i privremene objekte u programskim programima. Osim toga, postoje neka ograničenja u pogledu toga što mogu biti korutine, a funkcije constexpr, konstruktori/destruktori i main nisu uključeni u ovu listu.

Dakle, korutine rješavaju značajan dio problema jednostavnošću logike obrade podataka, ali njihove trenutne implementacije zahtijevaju poboljšanje.

Materijali:

C++ trikovi iz Yandex.Taxi, Anton Polukhin

U svojim profesionalnim aktivnostima, ponekad moram implementirati čisto pomoćne stvari: omot između internog interfejsa i API-ja neke biblioteke, logovanje ili raščlanjivanje. U ovom slučaju obično nema potrebe za dodatnom optimizacijom. Ali šta ako se ove komponente koriste u nekim od najpopularnijih servisa na RuNetu? U takvoj situaciji, morat ćete obraditi terabajte po satu samo dnevnika! Tada se računa svaka milisekunda i zato morate pribjeći raznim trikovima - pričao je o njima Anton Polukhin.

Možda je najzanimljiviji primjer implementacija obrasca pokazivača na implementaciju (pimpl). 

#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_; 
};

U ovom primjeru, prvo želim da se riješim datoteka zaglavlja vanjskih biblioteka - ovo će se kompajlirati brže, a možete se zaštititi od mogućih sukoba imena i drugih sličnih grešaka. 

U redu, premjestili smo #include u .cpp datoteku: potrebna nam je prosljedna deklaracija omotanog API-ja, kao i std::unique_ptr. Sada imamo dinamičke alokacije i druge neugodne stvari kao što su podaci razbacani po hrpi podataka i smanjene garancije. std::aligned_storage može pomoći u svemu ovome. 

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

Jedini problem: morate odrediti veličinu i poravnanje za svaki omotač - napravimo naš pimpl predložak s parametrima , upotrijebite neke proizvoljne vrijednosti i dodajte provjeru destruktoru da je sve ispravno: 

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

Budući da je T već definiran prilikom obrade destruktora, ovaj kod će biti ispravno raščlanjen i u fazi kompilacije će ispisati potrebnu veličinu i vrijednosti poravnanja koje je potrebno unijeti kao greške. Tako, po cenu jednog dodatnog izvođenja kompilacije, oslobađamo se dinamičke alokacije umotanih klasa, sakrivamo API u .cpp fajl sa implementacijom, a takođe dobijamo dizajn koji je pogodniji za keširanje od strane procesora.

Zapisivanje i raščlanjivanje djelovali su manje impresivno i stoga neće biti spomenuti u ovoj recenziji.

Slajdovi izvještaja dostupni su na sljedećem linku: ["C++ trikovi iz taksija"]

Moderne tehnike za održavanje vašeg koda SUVI, Björn Fahller

U ovom govoru, Björn Fahller pokazuje nekoliko različitih načina za borbu protiv stilske greške ponovljenih provjera stanja:

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

Zvuči poznato? Koristeći nekoliko moćnih C++ tehnika koje su uvedene u nedavnim standardima, možete elegantno implementirati istu funkcionalnost bez ikakvih smanjenja performansi. uporedi:   

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

Za rukovanje nefiksiranim brojem provjera, odmah morate koristiti promjenjive šablone i izraze za preklapanje. Pretpostavimo da želimo provjeriti jednakost nekoliko varijabli sa elementom state_type enuma. Prva stvar koja vam pada na pamet je napisati pomoćnu funkciju 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) || ...); 
}

Ovaj međurezultat je razočaravajući. Za sada kod nije postao čitljiviji:

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

Parametri predloška koji nisu tipa pomoći će da se malo poboljša situacija. Uz njihovu pomoć, mi ćemo prenijeti nabrojive elemente enuma na listu parametara šablona: 

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

Koristeći auto u parametru predloška bez tipa (C++17), pristup se jednostavno generalizira na poređenje ne samo s elementima tipa_state, već i s primitivnim tipovima koji se mogu koristiti kao parametri predloška bez tipa:


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

Kroz ova uzastopna poboljšanja postiže se željena tečna sintaksa za provjere:


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);

U ovom primjeru, vodič za dedukciju služi da predloži željene parametre predloška strukture kompajleru, koji poznaje tipove argumenata konstruktora. 

Dalje - zanimljivije. Bjorn uči kako generalizirati rezultujući kod za operatore poređenja izvan ==, a zatim za proizvoljne operacije. Usput, takve karakteristike kao što su atribut no_unique_address (C++20) i parametri šablona u lambda funkcijama (C++20) su objašnjene koristeći primjere korištenja. (Da, sada je lambda sintaksu još lakše zapamtiti – ovo su četiri uzastopna para zagrada svih vrsta.) Konačno rješenje koje koristi funkcije kao detalje konstruktora zaista mi grije dušu, a da ne spominjem izraz tuple u najboljim tradicijama lambda računica.

Na kraju, ne zaboravite ga uglancati:

  • Zapamtite da su lambda-ovi constexpr besplatni; 
  • Dodajmo savršeno prosljeđivanje i pogledajmo njegovu ružnu sintaksu u odnosu na paket parametara u lambda zatvaranju;
  • Dajmo kompajleru više mogućnosti za optimizaciju sa uslovnim noexcept; 
  • Pobrinimo se za razumljiviji izlaz greške u predlošcima zahvaljujući eksplicitnim povratnim vrijednostima lambda. Ovo će prisiliti kompajler da izvrši više provjera prije nego što se funkcija predloška zapravo pozove - u fazi provjere tipa. 

Za detalje pogledajte materijale predavanja: 

Naši utisci

Naše prvo učešće na C++ Russia bilo je nezaboravno po svom intenzitetu. O C++ Rusiji stekao sam utisak kao o iskrenom događaju, gde je granica između treninga i komunikacije uživo gotovo neprimjetna. Sve, od raspoloženja govornika do takmičenja partnera na događaju, pogoduje žustrim diskusijama. Sadržaj konferencije, koji se sastoji od izvještaja, pokriva prilično širok spektar tema uključujući C++ inovacije, studije slučaja velikih projekata i ideološka arhitektonska razmatranja. Ali bilo bi nepravedno zanemariti društvenu komponentu događaja, koja pomaže u prevazilaženju jezičkih barijera u odnosu ne samo na C++.

Zahvaljujemo organizatorima konferencije na prilici da učestvujemo u ovakvom događaju!
Možda ste vidjeli objavu organizatora o prošlosti, sadašnjosti i budućnosti C++ Rusije na blogu JUG Ru.

Hvala na čitanju i nadamo se da je naše prepričavanje događaja bilo od pomoći!

izvor: www.habr.com

Dodajte komentar