Kaip išvertėme 10 milijonų eilučių C++ kodo į C++14 standartą (o paskui į C++17)

Prieš kurį laiką (2016 m. rudenį), kuriant kitą 1C:Enterprise technologijos platformos versiją, kūrėjų komandoje iškilo klausimas dėl naujojo standarto palaikymo. C ++ 14 mūsų kode. Perėjimas prie naujo standarto, kaip manėme, leistų daug ką parašyti elegantiškiau, paprasčiau ir patikimiau, supaprastintų kodo palaikymą ir priežiūrą. Ir atrodo, kad vertime nėra nieko nepaprasto, jei ne kodo bazės mastai ir specifinės mūsų kodo savybės.

Tiems, kurie nežino, 1C: Enterprise yra aplinka, skirta sparčiai plėtoti kelių platformų verslo programas ir jų vykdymo laiką įvairiose OS ir DBVS. Apskritai gaminyje yra:

  • Programų serverių klasteris, veikia „Windows“ ir „Linux“.
  • Klientas, dirba su serveriu per http (-ius) arba savo dvejetainį protokolą, veikia „Windows“, „Linux“, „MacOS“
  • Interneto klientas, veikia „Chrome“, „Internet Explorer“, „Microsoft Edge“, „Firefox“, „Safari“ naršyklėse (parašyta „JavaScript“)
  • Kūrimo aplinka (Konfigūratorius), veikia „Windows“, „Linux“, „MacOS“.
  • Administravimo įrankiai programų serveriai, veikia „Windows“, „Linux“, „MacOS“.
  • Mobilus klientas, jungiantis prie serverio per http (-ius), veikia mobiliuosiuose įrenginiuose, kuriuose veikia Android, iOS, Windows
  • Mobilioji platforma – sistema, skirta kurti neprisijungus veikiančias mobiliąsias programas su galimybe sinchronizuoti, veikia „Android“, „iOS“, „Windows“.
  • Plėtros aplinka 1C: įmonės kūrimo įrankiai, parašyta Java
  • Serveris Sąveikos sistemos

Stengiamės kuo daugiau parašyti tą patį kodą skirtingoms operacinėms sistemoms – serverio kodų bazė yra 99% bendra, kliento kodų bazė yra apie 95%. Technologijų platforma 1C: Enterprise daugiausia parašyta C++, o apytikslės kodo charakteristikos pateiktos žemiau:

  • 10 milijonų eilučių C++ kodo,
  • 14 tūkstančių failų,
  • 60 tūkstančių klasių,
  • pusė milijono metodų.

Ir visa tai turėjo būti išversta į C++14. Šiandien mes jums pasakysime, kaip tai padarėme ir su kuo susidūrėme procese.

Kaip išvertėme 10 milijonų eilučių C++ kodo į C++14 standartą (o paskui į C++17)

Atsakomybės apribojimas

Viskas, kas parašyta žemiau apie lėtą/greitą darbą, (ne)didelį atminties suvartojimą diegiant standartines klases įvairiose bibliotekose, reiškia viena: tai tiesa MUMS. Gali būti, kad standartiniai diegimai geriausiai tiks jūsų užduotims. Pradėjome nuo savo užduočių: paėmėme klientams būdingus duomenis, vykdėme jiems tipiškus scenarijus, žiūrėjome į našumą, sunaudojamos atminties kiekį ir pan., analizavome, ar mes ir mūsų klientai esame patenkinti tokiais rezultatais, ar ne. . Ir jie veikė priklausomai nuo to.

Ką turėjome

Iš pradžių 1C:Enterprise 8 platformos kodą parašėme naudodami Microsoft Visual Studio. Projektas prasidėjo 2000-ųjų pradžioje ir turėjome tik Windows versiją. Natūralu, kad nuo tada kodas buvo aktyviai kuriamas, daugelis mechanizmų buvo visiškai perrašyti. Bet kodas buvo parašytas pagal 1998 m. standartą ir, pavyzdžiui, mūsų stačiakampiai skliaustai buvo atskirti tarpais, kad kompiliavimas būtų sėkmingas, pavyzdžiui:

vector<vector<int> > IntV;

2006 m., išleidę platformos 8.1 versiją, pradėjome palaikyti Linux ir perėjome prie trečiosios šalies standartinės bibliotekos STLPort. Viena iš perėjimo priežasčių buvo darbas su plačiomis linijomis. Savo kode mes naudojame std::wstring, pagrįstą wchar_t tipu. Jo dydis sistemoje Windows yra 2 baitai, o Linux sistemoje numatytasis dydis yra 4 baitai. Tai lėmė mūsų dvejetainių protokolų nesuderinamumą tarp kliento ir serverio bei įvairių nuolatinių duomenų. Naudodami gcc parinktis galite nurodyti, kad wchar_t dydis kompiliavimo metu taip pat yra 2 baitai, bet tada galite pamiršti naudoti standartinę biblioteką iš kompiliatoriaus, nes jis naudoja glibc, kuris savo ruožtu yra sukompiliuotas 4 baitų wchar_t. Kitos priežastys buvo geresnis standartinių klasių įdiegimas, maišos lentelių palaikymas ir netgi judėjimo konteineriuose semantikos emuliacija, kurią mes aktyviai naudojome. Ir dar viena priežastis, kaip sakoma paskutinė, bet ne mažiau svarbi, buvo styginių atlikimas. Turėjome savo styginių klasę, nes... Dėl mūsų programinės įrangos specifikos eilučių operacijos naudojamos labai plačiai ir mums tai yra labai svarbu.

Mūsų eilutė yra pagrįsta eilutės optimizavimo idėjomis, išreikštomis 2000-ųjų pradžioje Andrejus Aleksandresku. Vėliau, kai Alexandrescu dirbo „Facebook“, jo pasiūlymu „Facebook“ variklyje buvo panaudota linija, kuri veikė panašiais principais (žr. biblioteką kvailystė).

Mūsų linijoje buvo naudojamos dvi pagrindinės optimizavimo technologijos:

  1. Trumpoms reikšmėms naudojamas vidinis buferis pačiame eilutės objekte (nereikalaujantis papildomos atminties paskirstymo).
  2. Visiems kitiems naudojama mechanika Kopijuoti rašant. Eilutės reikšmė saugoma vienoje vietoje, o priskyrimo/keitimo metu naudojamas atskaitos skaitiklis.

Norėdami pagreitinti platformos kompiliavimą, iš STLPort varianto (kurio nenaudojome) išskyrėme srauto diegimą, todėl kompiliavimas buvo apie 20% greitesnis. Vėliau turėjome ribotai naudoti Padidinti. „Boost“ intensyviai naudoja srautą, ypač savo paslaugų API (pavyzdžiui, registravimui), todėl turėjome jį modifikuoti, kad pašalintume srauto naudojimą. Dėl to mums buvo sunku pereiti prie naujų „Boost“ versijų.

Trečias kelias

Pereinant prie C++14 standarto, apsvarstėme šias parinktis:

  1. Atnaujinkite mūsų modifikuotą STLPort į C++14 standartą. Variantas labai sunkus, nes... STLPort palaikymas buvo nutrauktas 2010 m., todėl visą jo kodą turėtume sukurti patys.
  2. Perėjimas prie kito STL diegimo, suderinamo su C++14. Labai pageidautina, kad šis diegimas būtų skirtas „Windows“ ir „Linux“.
  3. Kompiliuodami kiekvienai OS, naudokite biblioteką, integruotą į atitinkamą kompiliatorių.

Pirmasis variantas buvo visiškai atmestas dėl per daug darbo.

Kurį laiką galvojome apie antrąjį variantą; laikomas kandidatu libc++, bet tuo metu jis neveikė „Windows“. Norėdami perkelti libc++ į Windows, turėsite atlikti daug darbo – pavyzdžiui, patys parašyti viską, kas susiję su gijomis, gijų sinchronizavimu ir atomiškumu, nes šiose srityse naudojamas libc++ POSIX API.

Ir mes pasirinkome trečią kelią.

Perėjimas

Taigi, turėjome pakeisti STLPort naudojimą atitinkamų kompiliatorių bibliotekomis („Visual Studio 2015“, skirta „Windows“, „gcc 7“, skirta „Linux“, „clang 8“, skirta „macOS“).

Laimei, mūsų kodas buvo parašytas daugiausia pagal gaires ir nenaudojo visokių gudrių gudrybių, todėl perkėlimas į naujas bibliotekas vyko gana sklandžiai, naudojant scenarijus, kurie šaltinyje pakeitė tipų, klasių, vardų erdvių pavadinimus ir įtraukimus. failus. Perkėlimas paveikė 10 000 šaltinio failų (iš 14 000). wchar_t buvo pakeistas char16_t; nusprendėme atsisakyti wchar_t naudojimo, nes char16_t užima 2 baitus visose OS ir nesugadina kodo suderinamumo tarp Windows ir Linux.

Buvo ir nedidelių nuotykių. Pavyzdžiui, STLPort programoje iteratorius gali būti netiesiogiai nukreiptas į elemento žymeklį, o kai kuriose mūsų kodo vietose tai buvo naudojama. Naujose bibliotekose to padaryti nebebuvo įmanoma, o šias ištraukas teko analizuoti ir perrašyti rankiniu būdu.

Taigi, kodo perkėlimas baigtas, kodas sukompiliuotas visoms operacinėms sistemoms. Atėjo laikas testams.

Testai po perėjimo parodė, kad našumas sumažėjo (kai kuriose vietose iki 20-30%) ir padidėjo atminties suvartojimas (iki 10-15%), palyginti su senąja kodo versija. Tai visų pirma lėmė neoptimalus standartinių stygų veikimas. Todėl vėl teko naudoti savo, šiek tiek modifikuotą liniją.

Taip pat buvo atskleista įdomi konteinerių diegimo įterptosiose bibliotekose ypatybė: tuščios (be elementų) std::map ir std::set iš integruotų bibliotekų paskirsto atmintį. O dėl realizavimo ypatybių kai kur kode susidaro gana daug tuščių tokio tipo konteinerių. Standartiniai atminties konteineriai yra skirti šiek tiek vienam šakniniam elementui, tačiau mums tai pasirodė labai svarbu - daugelyje scenarijų mūsų našumas žymiai sumažėjo ir atminties suvartojimas padidėjo (palyginti su STLPort). Todėl savo kode šiuos du konteinerių tipus iš integruotų bibliotekų pakeitėme įdiegdami iš „Boost“, kur šie konteineriai neturėjo šios funkcijos, ir tai išsprendė sulėtėjimo ir padidėjusio atminties suvartojimo problemą.

Kaip dažnai nutinka po didelio masto pakeitimų dideliuose projektuose, pirmoji šaltinio kodo iteracija neveikė be problemų, ir čia ypač pravertė derinimo iteratorių palaikymas „Windows“ diegime. Žingsnis po žingsnio judėjome į priekį ir iki 2017 m. pavasario (8.3.11 1C:Enterprise versija) perkėlimas buvo baigtas.

rezultatai

Perėjimas prie C++14 standarto užtruko apie 6 mėnesius. Dažniausiai prie projekto dirbo vienas (bet labai aukštos kvalifikacijos) kūrėjas, o paskutiniame etape prisijungdavo už konkrečias sritis atsakingų komandų atstovai – UI, serverių klasterį, kūrimo ir administravimo priemones ir kt.

Perėjimas labai supaprastino mūsų darbą pereinant prie naujausių standarto versijų. Taigi versija 1C:Enterprise 8.3.14 (kuriama, išleista kitų metų pradžioje) jau buvo perkelta į standartą C++17.

Po perkėlimo kūrėjai turi daugiau galimybių. Jei anksčiau turėjome savo modifikuotą STL versiją ir vieną std vardų erdvę, tai dabar turime standartines klases iš integruotų kompiliatorių bibliotekų std vardų erdvėje, stdx vardų erdvėje – mūsų eilutes ir konteinerius, optimizuotus mūsų užduotims, o patobulinimui – naujausia „boost“ versija. Ir kūrėjas naudoja tas klases, kurios optimaliai tinka jo problemoms spręsti.

„Savasis“ judėjimo konstruktorių diegimas taip pat padeda kurti (perkelti konstruktorius) daugeliui klasių. Jei klasėje yra perkėlimo konstruktorius ir ši klasė yra talpinama konteineryje, tai STL optimizuoja elementų kopijavimą konteinerio viduje (pavyzdžiui, kai konteineris išplečiamas ir reikia keisti talpą bei perskirstyti atmintį).

Laidu kojų

Bene pati nemaloniausia (bet ne kritinė) migracijos pasekmė yra ta, kad susiduriame su apimčių didėjimu obj failus, o visas kūrimo rezultatas su visais tarpiniais failais pradėjo užimti 60–70 GB. Tokį elgesį lemia šiuolaikinių standartinių bibliotekų ypatumai, kurie tapo mažiau kritiški generuojamų paslaugų failų dydžiui. Tai neturi įtakos sudarytos programos veikimui, tačiau sukelia nemažai nepatogumų kuriant, ypač pailgina kompiliavimo laiką. Taip pat didėja laisvos vietos diske kūrimo serveriuose ir kūrėjų įrenginiuose. Mūsų kūrėjai lygiagrečiai dirba su keliomis platformos versijomis, o šimtai gigabaitų tarpinių failų kartais sukelia sunkumų jų darbe. Problema nemaloni, bet ne kritinė, jos sprendimą kol kas atidėjome. Technologiją svarstome kaip vieną iš jos sprendimo variantų vienybės kūrimas (ypač „Google“ tai naudoja kurdama „Chrome“ naršyklę).

Šaltinis: www.habr.com

Добавить комментарий