Kuidas tõlkisime 10 miljonit rida C++ koodi C++14 standardiks (ja seejärel C++17-sse)

Mõni aeg tagasi (sügisel 2016) tekkis 1C:Enterprise tehnoloogiaplatvormi järgmise versiooni arendamise käigus arendusmeeskonnas küsimus uue standardi toetamise kohta. C ++ 14 meie koodis. Üleminek uuele standardile, nagu me eeldasime, võimaldaks meil paljusid asju elegantsemalt, lihtsamalt ja usaldusväärsemalt kirjutada ning lihtsustaks koodi toetamist ja hooldamist. Ja tõlkes ei näi olevat midagi erakordset, kui mitte arvestada koodibaasi ulatust ja meie koodi eripärasid.

Neile, kes ei tea, on 1C:Enterprise keskkond platvormideüleste ärirakenduste kiireks arendamiseks ja nende käivitamiseks erinevates OS-ides ja DBMS-ides. Üldiselt sisaldab toode:

  • Rakendusserveri klaster, töötab Windowsis ja Linuxis
  • Klient, töötab serveriga http(de) või oma binaarprotokolli kaudu, töötab Windowsis, Linuxis, macOS-is
  • Veebiklient, töötab brauserites Chrome, Internet Explorer, Microsoft Edge, Firefox, Safari (kirjutatud JavaScriptis)
  • Arenduskeskkond (Konfigurator), töötab Windowsis, Linuxis ja macOS-is
  • Haldustööriistad rakendusserverid, töötavad operatsioonisüsteemides Windows, Linux, macOS
  • Mobiilne klient, ühendub serveriga http(de) kaudu, töötab mobiilseadmetes, kus töötab Android, iOS, Windows
  • Mobiilne platvorm — raamistik sünkroonimisvõimalusega võrguühenduseta mobiilirakenduste loomiseks, mis töötab Androidis, iOS-is ja Windowsis
  • Arenduskeskkond 1C: ettevõtte arendustööriistad, kirjutatud Java keeles
  • Server Interaktsioonisüsteemid

Püüame kirjutada erinevatele operatsioonisüsteemidele nii palju kui võimalik sama koodi - serveri koodibaas on 99% ühine, kliendi koodibaas ca 95%. 1C:Enterprise tehnoloogiaplatvorm on peamiselt kirjutatud C++ keeles ja ligikaudsed koodi omadused on toodud allpool:

  • 10 miljonit rida C++ koodi,
  • 14 tuhat faili,
  • 60 tuhat klassi,
  • pool miljonit meetodit.

Ja kõik see värk tuli tõlkida C++14 keelde. Täna räägime teile, kuidas me seda tegime ja millega selle käigus kokku puutusime.

Kuidas tõlkisime 10 miljonit rida C++ koodi C++14 standardiks (ja seejärel C++17-sse)

Vastutusest loobumine

Kõik, mis allpool on kirjutatud aeglase/kiire töö, (mitte) suure mälutarbimise kohta standardklasside juurutuste poolt erinevates teekides, tähendab üht: see kehtib MEIE KOHTA. On täiesti võimalik, et teie ülesannete jaoks sobivad kõige paremini standardrakendused. Lähtusime oma ülesannetest: võtsime oma klientidele tüüpilised andmed, käitasime nende peal tüüpilisi stsenaariume, vaatasime jõudlust, tarbitud mälumahtu jne ning analüüsisime, kas meie ja meie kliendid jäid selliste tulemustega rahule või mitte. . Ja nad tegutsesid sõltuvalt sellest.

Mis meil oli

Algselt kirjutasime 1C:Enterprise 8 platvormi koodi Microsoft Visual Studio abil. Projekt sai alguse 2000. aastate alguses ja meil oli ainult Windowsi versioon. Loomulikult on sellest ajast alates koodi aktiivselt arendatud, paljud mehhanismid on täielikult ümber kirjutatud. Kuid kood oli kirjutatud 1998. aasta standardi järgi ja näiteks meie täisnurksulud eraldati tühikutega, et kompileerimine õnnestuks, nii:

vector<vector<int> > IntV;

2006. aastal alustasime platvormi versiooni 8.1 väljalaskmisega Linuxi toetamist ja läksime üle kolmanda osapoole standardteegile STLPort. Ülemineku üheks põhjuseks oli töö laiade joontega. Oma koodis kasutame läbivalt std::wstring, mis põhineb tüübil wchar_t. Selle suurus Windowsis on 2 baiti ja Linuxis on vaikimisi 4 baiti. See tõi kaasa meie binaarprotokollide kokkusobimatuse kliendi ja serveri vahel, aga ka mitmesuguseid püsivaid andmeid. Kasutades gcc suvandeid saab määrata, et wchar_t suurus on kompileerimise ajal samuti 2 baiti, kuid siis võid unustada kompilaatori standardteegi kasutamise, sest see kasutab glibc-d, mis omakorda on kompileeritud 4-baidise wchar_t jaoks. Muud põhjused olid standardklasside parem rakendamine, räsitabelite tugi ja isegi konteinerite sees liikumise semantika emuleerimine, mida me aktiivselt kasutasime. Ja veel üks põhjus, nagu öeldakse last but not least, oli keelpillide esitus. Meil oli keelpillide jaoks oma klass, sest... Meie tarkvara spetsiifikast tulenevalt kasutatakse stringoperatsioone väga laialdaselt ja meie jaoks on see kriitilise tähtsusega.

Meie string põhineb stringide optimeerimise ideedel, mida väljendati 2000. aastate alguses Andrei Alexandrescu. Hiljem, kui Alexandrescu Facebookis töötas, kasutati tema ettepanekul Facebooki mootoris rida, mis töötas sarnastel põhimõtetel (vt raamatukogu rumalus).

Meie rida kasutas kahte peamist optimeerimistehnoloogiat:

  1. Lühikeste väärtuste jaoks kasutatakse stringobjekti enda sisemist puhvrit (ei vaja täiendavat mälu eraldamist).
  2. Kõigi teiste puhul kasutatakse mehaanikat Kopeeri kirjutamisel. Stringi väärtus salvestatakse ühte kohta ja määramise/muutmise ajal kasutatakse võrdlusloendurit.

Platvormi kompileerimise kiirendamiseks jätsime oma STLPorti variandist välja voo juurutamise (mida me ei kasutanud), see andis meile umbes 20% kiirema kompileerimise. Seejärel pidime kasutama piiratud arvu Tõstmine. Boost kasutab palju voogu, eriti oma teenuse API-des (näiteks logimiseks), nii et pidime seda muutma, et vookasutus eemaldada. See omakorda raskendas meie jaoks Boosti uutele versioonidele üleminekut.

Kolmas tee

C++14 standardile üleminekul kaalusime järgmisi võimalusi:

  1. Täiendage muudetud STLPort C++14 standardile. Valik on väga raske, sest... STLPorti tugi lõpetati 2010. aastal ja me peaksime kogu selle koodi ise ehitama.
  2. Üleminek teisele C++14-ga ühilduvale STL-i teostusele. On väga soovitav, et see rakendus oleks Windowsi ja Linuxi jaoks.
  3. Iga OS-i jaoks kompileerimisel kasutage vastavasse kompilaatorisse sisseehitatud teeki.

Esimene variant lükati liigse töö tõttu otse tagasi.

Mõtlesime mõnda aega teise variandi peale; peeti kandidaadiks libc++, kuid sel ajal see Windowsi all ei töötanud. Libc++ Windowsi portimiseks peaksite tegema palju tööd - näiteks kirjutage ise kõik, mis on seotud lõimede, lõimede sünkroonimise ja atomaalsusega, kuna nendes valdkondades kasutatakse libc++ POSIX API.

Ja me valisime kolmanda tee.

Üleminek

Seega pidime STLPorti kasutamise asendama vastavate kompilaatorite teekidega (Windowsi jaoks Visual Studio 2015, Linuxi jaoks gcc 7, macOS-i jaoks clang 8).

Õnneks oli meie kood kirjutatud põhiliselt juhiste järgi ega kasutanud igasuguseid nutikaid nippe, mistõttu liikumine uutesse teekidesse kulges suhteliselt sujuvalt, skriptide abil, mis asendasid allikas tüüpide, klasside, nimeruumide ja lisade nimesid. failid. Migreerimine mõjutas 10 000 lähtefaili (14 000-st). wchar_t asendati char16_t-ga; otsustasime wchar_t kasutamisest loobuda, kuna char16_t võtab kõigis operatsioonisüsteemides 2 baiti ega riku Windowsi ja Linuxi koodide ühilduvust.

Olid väikesed seiklused. Näiteks STLPortis saab iteraatori kaudselt suunata elemendile osutavale kursorile ja mõnes kohas meie koodis seda kasutati. Uutes raamatukogudes seda enam teha ei saanud ning neid lõike tuli käsitsi analüüsida ja ümber kirjutada.

Niisiis, koodi migreerimine on lõpule viidud, kood on kompileeritud kõigi operatsioonisüsteemide jaoks. On aeg katseteks.

Üleminekujärgsed testid näitasid võrreldes koodi vana versiooniga jõudluse langust (mõnes kohas kuni 20-30%) ja mälukulu suurenemist (kuni 10-15%). See oli eelkõige tingitud standardsete stringide ebaoptimaalsest jõudlusest. Seetõttu pidime taas kasutama oma, veidi muudetud liini.

Samuti ilmnes üks huvitav omadus konteinerite rakendamisel manustatud teekides: sisseehitatud teekide tühjad (ilma elementideta) std::map ja std::set eraldavad mälu. Ja juurutusfunktsioonide tõttu tekib koodis kohati päris palju seda tüüpi tühje konteinereid. Tavalisi mälukonteinereid eraldatakse vähe, ühe juurelemendi jaoks, kuid meie jaoks osutus see kriitiliseks - mitme stsenaariumi korral langes meie jõudlus märkimisväärselt ja mälutarbimine suurenes (võrreldes STLPortiga). Seetõttu asendasime oma koodis need kahte tüüpi sisseehitatud teekide konteinerid nende juurutusega Boostist, kus neil konteineritel seda funktsiooni polnud, ja see lahendas probleemi aeglustumise ja suurenenud mälutarbimisega.

Nagu sageli juhtub pärast suuremahulisi muudatusi suurtes projektides, ei toiminud lähtekoodi esimene iteratsioon probleemideta ja siin tuli eriti kasuks Windowsi juurutuse iteraatorite silumise tugi. Samm-sammult liikusime edasi ja 2017. aasta kevadeks (versioon 8.3.11 1C:Enterprise) sai migratsioon lõppenud.

Tulemused

Üleminek standardile C++14 võttis meil aega umbes 6 kuud. Enamasti töötas projekti kallal üks (kuid väga kõrge kvalifikatsiooniga) arendaja ning lõppfaasis liitusid konkreetsete valdkondade eest vastutavate meeskondade esindajad - kasutajaliides, serveriklaster, arendus- ja haldustööriistad jne.

Üleminek lihtsustas oluliselt meie tööd standardi uusimatele versioonidele üleminekul. Seega on versioon 1C:Enterprise 8.3.14 (arenduses, väljalaskmine on kavandatud järgmise aasta algusesse) juba üle viidud standardile C++17.

Pärast migratsiooni on arendajatel rohkem võimalusi. Kui varem oli meil STL-i muudetud versioon ja üks std-nimeruum, siis nüüd on meil standardklassid sisseehitatud kompilaatorite teekidest std-nimeruumis, stdx-nimeruumis - meie read ja konteinerid on optimeeritud meie ülesannete jaoks, võimenduses - boosti uusim versioon. Ja arendaja kasutab neid klasse, mis sobivad tema probleemide lahendamiseks optimaalselt.

Liikumiskonstruktorite “natiivne” juurutamine aitab samuti arendada (teisaldada konstruktoreid) mitme klassi jaoks. Kui klassil on teisaldamise konstruktor ja see klass on paigutatud konteinerisse, siis STL optimeerib konteineri sees olevate elementide kopeerimist (näiteks kui konteinerit laiendatakse ja on vaja muuta mahtu ja ümber paigutada mälu).

Fly salvaga

Võib-olla on rände kõige ebameeldivam (kuid mitte kriitilisem) tagajärg see, et me seisame silmitsi mahu kasvuga obj failid, ja kogu koostamise tulemus koos kõigi vahefailidega hakkas võtma 60–70 GB. Selline käitumine on tingitud tänapäevaste standardteekide iseärasustest, mis on muutunud vähem kriitiliseks loodud teenusefailide suuruse suhtes. See ei mõjuta koostatud rakenduse tööd, küll aga põhjustab arenduses mitmeid ebamugavusi, eelkõige pikendab kompileerimisaega. Kasvavad ka nõuded vaba kettaruumi järele ehitusserverites ja arendajamasinates. Meie arendajad töötavad paralleelselt mitme platvormi versiooniga ja sajad gigabaidid vahefailid tekitavad mõnikord nende töös raskusi. Probleem on ebameeldiv, kuid mitte kriitiline, oleme selle lahendamise praeguseks edasi lükanud. Kaalume selle lahendamise ühe võimalusena tehnoloogiat ühtsuse ehitamine (eelkõige kasutab Google seda Chrome'i brauseri arendamisel).

Allikas: www.habr.com

Lisa kommentaar