Pred časom (na jeseň 2016) pri vývoji ďalšej verzie technologickej platformy 1C:Enterprise vyvstala vo vývojovom tíme otázka podpory nového štandardu. v našom kóde. Prechod na nový štandard, ako sme predpokladali, by nám umožnil písať mnohé veci elegantnejšie, jednoduchšie a spoľahlivejšie a zjednodušil by podporu a údržbu kódu. A zdá sa, že v preklade nie je nič mimoriadne, ak nie pre rozsah kódovej základne a špecifické vlastnosti nášho kódu.
Pre tých, ktorí nevedia, 1C:Enterprise je prostredie pre rýchly vývoj multiplatformových podnikových aplikácií a runtime na ich spúšťanie na rôznych OS a DBMS. Vo všeobecnosti produkt obsahuje:
- , pracuje na Windows и Linux
- , spolupracuje so serverom cez http(s) alebo cez vlastný binárny protokol, funguje na Windows, Linux, macOS
- , spustený v prehliadačoch Chrome, Internet Explorer, Microsoft Edge, Firefox, Safari (napísaný v JavaScripte)
- Vývojové prostredie (), pracuje na Windows, Linux, macOS
- aplikačné servery bežiace na Windows, Linux, macOS
- , pripojenie k serveru cez http(s), funguje na mobilných zariadeniach so systémom Android, IOS, Windows
- — framework na vytváranie offline mobilných aplikácií s možnosťou synchronizácie, bežiacich na Android, IOS, Windows
- Vývojové prostredie , napísaný v jazyku Java
- Server
Snažíme sa čo najviac písať rovnaký kód pre rôzne operačné systémy – základňa kódu servera je na 99 % spoločná, základňa kódu klienta je približne 95 %. Technologická platforma 1C:Enterprise je primárne napísaná v C++ a približné charakteristiky kódu sú uvedené nižšie:
- 10 miliónov riadkov kódu C++,
- 14 tisíc súborov,
- 60 tisíc tried,
- pol milióna metód.
A všetky tieto veci museli byť preložené do C++14. Dnes vám povieme, ako sme to urobili a s čím sme sa pri tom stretli.

Vylúčenie zodpovednosti
Všetko napísané nižšie o pomalej/rýchlej práci, (ne)veľkej spotrebe pamäte implementáciou štandardných tried v rôznych knižniciach znamená jedno: platí to PRE NÁS. Je celkom možné, že pre vaše úlohy budú najlepšie vyhovovať štandardné implementácie. Vychádzali sme z vlastných úloh: zobrali sme údaje, ktoré boli typické pre našich klientov, spustili sme na nich typické scenáre, pozreli sme sa na výkon, množstvo spotrebovanej pamäte atď. a analyzovali sme, či sme my a naši klienti s takýmito výsledkami spokojní alebo nie. . A konali v závislosti od.
Čo sme mali
Spočiatku sme kód platformy 1C:Enterprise 8 písali v Microsoft Visual Studio. Projekt sa začal začiatkom roku 2000 a mali sme iba verziu pre WindowsPrirodzene, kód sa odvtedy rýchlo vyvíjal a mnoho mechanizmov bolo úplne prepísaných. Kód bol však napísaný podľa štandardu z roku 1998 a napríklad sme pravouhlé zátvorky oddelili medzerami, aby sme zabezpečili úspešnú kompiláciu, takto:
vector<vector<int> > IntV;V roku 2006, s vydaním platformy verzie 8.1, sme začali podporovať Linux a prešiel na štandardnú knižnicu tretej strany Jedným z dôvodov prechodu bola práca so širokými reťazcami. V našom kóde používame std::wstring, ktorý je založený na type wchar_t. Jeho veľkosť je Windows 2 bajty a v Linux Predvolená hodnota je 4 bajty. To viedlo k nekompatibilite medzi našimi binárnymi protokolmi medzi klientom a serverom, ako aj k rôznym perzistentným dátam. Pomocou volieb gcc môžete počas kompilácie vynútiť veľkosť wchar_t na 2 bajty, ale potom môžete zabudnúť na používanie štandardnej knižnice kompilátora, pretože používa glibc, ktorý je zase kompilovaný pre 4-bajtový wchar_t. Medzi ďalšie dôvody patrila kvalitnejšia implementácia štandardných tried, podpora hašovacích tabuliek a dokonca aj emulácia sémantiky pohybu kontajnerov, ktorú sme aktívne využívali. A v neposlednom rade, ako sa hovorí, bol výkon reťazcov. Mali sme vlastnú triedu reťazcov, pretože operácie s reťazcami sa v našom softvéri hojne používajú, a to je pre nás kritické.
Náš reťazec je založený na nápadoch na optimalizáciu reťazcov vyjadrených na začiatku 2000. storočia . Neskôr, keď Alexandrescu pracoval na Facebooku, na jeho návrh bola v motore Facebooku použitá linka, ktorá fungovala na podobných princípoch (pozri knižnicu ).
Naša linka využívala dve hlavné optimalizačné technológie:
- Pre krátke hodnoty sa používa vnútorná vyrovnávacia pamäť v samotnom reťazci (nevyžaduje dodatočné pridelenie pamäte).
- Na všetky ostatné sa používa mechanika . Hodnota reťazca je uložená na jednom mieste a pri priraďovaní/úprave sa používa referenčné počítadlo.
Aby sme urýchlili kompiláciu platformy, vylúčili sme implementáciu streamu z nášho variantu STLPort (ktorý sme nepoužili), čo nám poskytlo asi o 20% rýchlejšiu kompiláciu. Následne sme ich museli využiť v obmedzenom rozsahu . Boost intenzívne využíva stream, najmä vo svojich rozhraniach API (napríklad na protokolovanie), takže sme ho museli upraviť, aby sme odstránili používanie streamu. To nám zase sťažilo migráciu na nové verzie Boost.
tretí spôsob
Pri prechode na štandard C++14 sme zvážili nasledujúce možnosti:
- Aktualizujte nami upravený STLPort na štandard C++14. Táto možnosť je veľmi ťažká, pretože... podpora pre STLPort bola ukončená v roku 2010 a celý jeho kód by sme si museli vytvoriť sami.
- Prechod na inú implementáciu STL kompatibilnú s C++14. Je veľmi žiaduce, aby táto implementácia bola kompatibilná s Windows и Linux.
- Pri kompilácii pre každý OS použite knižnicu zabudovanú do príslušného kompilátora.
Prvá možnosť bola pre príliš veľa práce zamietnutá.
Chvíľu sme uvažovali o druhej možnosti; považovaný za kandidáta ale v tom čase nepracoval pod WindowsPortovať libc++ do Windows, museli by sme urobiť veľa práce - napríklad napísať všetko, čo súvisí s vláknami, synchronizáciou vlákien a atomicitou, sami, keďže libc++ používa .
A vybrali sme si tretiu cestu.
Prechod
Takže sme museli nahradiť používanie STLPortu knižnicami príslušných kompilátorov (Visual Studio 2015 pre Windows, gcc 7 pre Linux, cvaknutie 8 pre macOS).
Našťastie, náš kód bol napísaný prevažne podľa pokynov a nepoužíval žiadne šikovné triky, takže migrácia na nové knižnice prebehla relatívne hladko, s použitím skriptov, ktoré nahradili názvy typov, tried, menných priestorov a zahrnutí v zdrojových súboroch. Migrácia ovplyvnila 10 000 zdrojových súborov (z celkového počtu 14 000). wchar_t bol nahradený char16_t; rozhodli sme sa upustiť od používania wchar_t, pretože char16_t zaberá 2 bajty na všetkých operačných systémoch a neovplyvňuje kompatibilitu kódu medzi... Windows и Linux.
Bolo tam niekoľko malých dobrodružstiev. Napríklad v STLPort mohol byť iterátor implicitne pretypovaný na ukazovateľ na prvok a na niektorých miestach v našom kóde sa to použilo. V nových knižniciach to už nebolo možné a tieto pasáže sa museli analyzovať a prepisovať ručne.
Takže migrácia kódu je dokončená, kód je skompilovaný pre všetky operačné systémy. Je čas na testy.
Testy po prechode ukázali pokles výkonu (miestami až o 20 – 30 %) a zvýšenie spotreby pamäte (až o 10 – 15 %) v porovnaní so starou verziou kódu. Bolo to spôsobené najmä neoptimálnym výkonom štandardných strún. Preto sme opäť museli použiť vlastnú, mierne upravenú linku.
Odhalila sa aj zaujímavá vlastnosť implementácie kontajnerov vo vstavaných knižniciach: prázdne (bez prvkov) std::map a std::set zo vstavaných knižníc alokujú pamäť. A vďaka implementačným funkciám sa na niektorých miestach v kóde vytvára pomerne veľa prázdnych kontajnerov tohto typu. Štandardné pamäťové kontajnery sú pridelené málo, pre jeden koreňový prvok, ale pre nás sa to ukázalo ako kritické - v mnohých scenároch sa náš výkon výrazne znížil a spotreba pamäte sa zvýšila (v porovnaní s STLPort). Preto sme v našom kóde nahradili tieto dva typy kontajnerov zo vstavaných knižníc ich implementáciou z Boost, kde tieto kontajnery túto vlastnosť nemali a tým sa vyriešil problém so spomalením a zvýšenou spotrebou pamäte.
Ako sa často stáva po rozsiahlych zmenách vo veľkých projektoch, prvá iterácia zdrojového kódu nebola bez problémov a tu najmä podpora ladiacích iterátorov v Windows-implementácia. Postupovali sme krok za krokom a na jar 2017 (verzia 8.3.11 platformy 1C:Enterprise) bola migrácia dokončená.
Výsledky
Prechod na štandard C++14 nám trval približne 6 mesiacov. Na projekte väčšinou pracoval jeden (ale veľmi vysokokvalifikovaný) vývojár a v záverečnej fáze sa pridali zástupcovia tímov zodpovedných za konkrétne oblasti – UI, serverový klaster, vývojové a administračné nástroje atď.
Prechod nám výrazne zjednodušil prácu na prechode na najnovšie verzie štandardu. Verzia 1C:Enterprise 8.3.14 (vo vývoji, vydanie naplánované na začiatok budúceho roka) už bola teda prevedená na štandard .
Po migrácii majú vývojári viac možností. Ak sme predtým mali vlastnú upravenú verziu STL a jeden std namespace, teraz máme štandardné triedy zo vstavaných knižníc kompilátorov v std namespace, v stdx namespace – naše riadky a kontajnery optimalizované pre naše úlohy, v boost – the najnovšia verzia boost. A vývojár používa tie triedy, ktoré sú optimálne vhodné na riešenie jeho problémov.
Pri vývoji pomáha aj „natívna“ implementácia konštruktorov pohybu () pre niekoľko tried. Ak má trieda konštruktor presunu a táto trieda je umiestnená v kontajneri, potom STL optimalizuje kopírovanie prvkov vo vnútri kontajnera (napríklad keď je kontajner rozšírený a je potrebné zmeniť kapacitu a prerozdeliť pamäť).
Pozrite sa na masť
Snáď najnepríjemnejším (ale nie kritickým) dôsledkom migrácie je, že čelíme nárastu objemu a úplný výsledok zostavenia so všetkými prechodnými súbormi začal zaberať 60–70 GB. Toto správanie je spôsobené zvláštnosťami moderných štandardných knižníc, ktoré sa stali menej kritickými voči veľkosti generovaných súborov služieb. Nemá to vplyv na fungovanie skompilovanej aplikácie, ale spôsobuje to množstvo nepríjemností pri vývoji, najmä to zvyšuje čas kompilácie. Zvyšujú sa aj požiadavky na voľný diskový priestor na zostavovacích serveroch a na vývojárskych počítačoch. Naši vývojári pracujú paralelne na niekoľkých verziách platformy a stovky gigabajtov prechodných súborov niekedy spôsobujú ťažkosti pri ich práci. Problém je nepríjemný, ale nie kritický, jeho riešenie sme zatiaľ odložili. Technológiu zvažujeme ako jednu z možností riešenia (Google ho používa najmä pri vývoji prehliadača Chrome).
Zdroj: hab.com
