Nola itzuli genituen C++ kodearen 10 milioi lerro C++ 14 estandarrera (eta gero C++ 17ra)

Duela denbora pixka bat (2016ko udazkenean), 1C:Enterprise plataforma teknologikoaren hurrengo bertsioaren garapenean, garapen taldean galdera sortu zen estandar berriari eustea. C ++ 14 gure kodean. Estandar berri baterako trantsizioak, suposatu genuen bezala, gauza asko dotoreago, sinpleago eta fidagarriagoak idazteko aukera emango liguke, eta kodearen euskarria eta mantentze-lanak erraztuko lituzke. Eta badirudi ez dagoela ezer aparteko itzulpenean, kode-oinarriaren eskalagatik eta gure kodearen ezaugarri zehatzengatik ez bada.

Ezagutzen ez dutenentzat, 1C:Enterprise plataforma anitzeko negozio-aplikazioak eta exekuzio-denbora azkar garatzeko ingurune bat da OS eta DBMS desberdinetan exekutatzeko. Orokorrean, produktuak honako hauek ditu:

  • Aplikazio Zerbitzarien Klusterra, Windows eta Linux-en exekutatzen da
  • bezero, zerbitzariarekin http(s) edo bere protokolo bitarren bidez lan eginez, Windows, Linux, macOS-en funtzionatzen du
  • Web bezeroa, Chrome, Internet Explorer, Microsoft Edge, Firefox, Safari nabigatzaileetan exekutatzen (JavaScript-en idatzita)
  • Garapen ingurunea (Konfiguratzailea), Windows, Linux, macOS-en funtzionatzen du
  • Administrazio-tresnak aplikazio zerbitzariak, exekutatu Windows, Linux, macOS
  • Bezero mugikorra, zerbitzarira http(s) bidez konektatuz, Android, iOS, Windows exekutatzen dituzten gailu mugikorretan funtzionatzen du
  • Plataforma mugikorra β€” sinkronizatzeko gaitasuna duten lineaz kanpoko aplikazio mugikorrak sortzeko esparru bat, Android, iOS, Windows-en exekutatzen duena
  • Garapen ingurunea 1C:Enpresa garatzeko tresnak, Javan idatzia
  • zerbitzaria Interakzio Sistemak

Sistema eragile desberdinetarako kode bera idazten saiatzen gara ahalik eta gehien - zerbitzariaren kodearen oinarria % 99 ohikoa da, bezeroaren kodearen oinarria % 95 ingurukoa da. 1C:Enterprise plataforma teknologikoa batez ere C++-n idatzita dago eta kode gutxi gorabeherako ezaugarriak jarraian azaltzen dira:

  • 10 milioi C++ kode lerro,
  • 14 mila fitxategi,
  • 60 mila klase,
  • milioi erdi metodo.

Eta gauza hauek guztiak C++14ra itzuli behar izan ziren. Gaur hau nola egin dugun eta prozesuan zer topatu dugun kontatuko dizuegu.

Nola itzuli genituen C++ kodearen 10 milioi lerro C++ 14 estandarrera (eta gero C++ 17ra)

Erantzukizuna

Behean lan motel/bizkorrari buruz idatzitako guztiak, (ez) memoria-kontsumo handiak hainbat liburutegitan klase estandarrak ezartzeak gauza bat esan nahi du: hori egia da GURETZAT. Baliteke inplementazio estandarrak zure zereginetarako egokienak izatea. Gure zereginetatik abiatu ginen: gure bezeroentzako ohikoak ziren datuak hartu, eszenatoki tipikoak exekutatu genituen, errendimendua aztertu genuen, kontsumitutako memoria kopurua, etab., eta gu eta gure bezeroak horrelako emaitzekin pozik geunden edo ez aztertu genuen. . Eta horren arabera jokatzen zuten.

Guk genuena

Hasieran, 1C:Enterprise 8 plataformarako kodea idatzi genuen Microsoft Visual Studio-n. Proiektua 2000ko hamarkadaren hasieran hasi zen eta Windows-en soilik bertsio bat genuen. Jakina, harrezkero kodea aktiboki garatu da, mekanismo asko guztiz berridatzi dira. Baina kodea 1998ko estandarraren arabera idatzi zen, eta, adibidez, gure parentesi angelu zuzenak zuriunez bereizten ziren, konpilazioak arrakasta izan zezan, honela:

vector<vector<int> > IntV;

2006an, plataformaren 8.1 bertsioa kaleratuta, Linux onartzen hasi ginen eta hirugarrenen liburutegi estandarrera aldatu ginen. STLPorta. Trantsizioaren arrazoietako bat marra zabalekin lan egitea izan zen. Gure kodean, std::wstring erabiltzen dugu, hau da, wchar_t motan oinarritzen dena. Windows-en bere tamaina 2 byte-koa da, eta Linux-en lehenetsia 4 byte-koa. Horrek bezeroaren eta zerbitzariaren arteko gure protokolo bitarren bateraezintasuna ekarri zuen, baita hainbat datu iraunkor ere. Gcc aukerak erabiliz, konpilatzean wchar_t-en tamaina ere 2 bytekoa dela zehaztu dezakezu, baina gero ahaztu dezakezu konpilagailuko liburutegi estandarra erabiltzeaz, izan ere glibc erabiltzen du, aldi berean 4 byteko wchar_t baterako konpilatuta dagoena. Beste arrazoi batzuk klase estandarrak hobeto inplementatzea, hash-taulen laguntza eta edukiontzien barruan mugitzeko semantikaren emulazioa ere izan ziren, aktiboki erabiltzen genuena. Eta beste arrazoi bat, azkenik, esan bezala, hari-jarduera izan zen. Soketarako klase propioa genuen, zeren... Gure softwarearen berezitasunak direla eta, kateen eragiketak oso zabal erabiltzen dira eta guretzat hori ezinbestekoa da.

Gure katea 2000ko hamarkadaren hasieran adierazitako kateen optimizazio ideietan oinarritzen da Andrei Alexandrescu. Geroago, Alexandrescuk Facebooken lan egin zuenean, bere iradokiz, antzeko printzipioekin funtzionatzen zuen Facebook-eko motorean lerro bat erabili zen (ikusi liburutegia zentzugabekeria).

Gure lineak bi optimizazio teknologia nagusi erabili zituen:

  1. Balio laburretarako, kate-objektuan bertan barne-buffer bat erabiltzen da (ez du memoria esleipen gehigarririk behar).
  2. Beste guztientzat, mekanika erabiltzen da Kopiatu Idazketan. Katearen balioa leku bakarrean gordetzen da, eta erreferentzia-kontagailua erabiltzen da esleipenean/aldaketan.

Plataformaren konpilazioa bizkortzeko, korrontearen inplementazioa baztertu genuen gure STLPort aldaeratik (erabili ez genuena), honek % 20 inguruko konpilazio azkarragoa eman zigun. Gerora erabilera mugatua egin behar izan genuen Boost. Boost-ek korrontearen erabilera handia egiten du, bereziki bere zerbitzu-APIetan (adibidez, erregistroa egiteko), beraz, korrontearen erabilera kentzeko aldatu behar izan dugu. Horrek, aldi berean, zaila egin zitzaigun Boost-en bertsio berrietara migratzea.

Hirugarren bidea

C++14 estandarrera pasatzean, aukera hauek kontuan hartu genituen:

  1. Berritu aldatu dugun STLPort C++ 14 estandarrera. Aukera oso zaila da, zeren... STLPort-erako laguntza 2010ean eten zen, eta bere kode guztia geuk eraiki beharko genuke.
  2. C++14-rekin bateragarria den STL inplementazio batera igarotzea. Oso desiragarria da ezarpen hau Windows eta Linuxentzat izatea.
  3. OS bakoitzerako konpilatzean, erabili dagokion konpilatzailean integratutako liburutegia.

Lehen aukera erabat baztertu zuten lan gehiegi zela eta.

Bigarren aukera pentsatu genuen denbora batez; hautagaitzat hartzen da libc++, baina garai hartan ez zuen funtzionatu Windows-en. libc++ Windows-era eramateko, lan asko egin beharko zenuke; adibidez, idatzi zuk zeuk hariekin, harien sinkronizazioarekin eta atomismoarekin zerikusia duen guztia, libc++ eremu hauetan erabiltzen baita. POSIX APIa.

Eta hirugarren bidea aukeratu genuen.

Trantsizioa

Beraz, STLPort-en erabilera dagozkien konpilatzaileen liburutegiekin ordezkatu behar izan genuen (Visual Studio 2015 Windows-erako, gcc 7 Linux-erako, clang 8 macOS-erako).

Zorionez, gure kodea, batez ere, jarraibideen arabera idatzi zen eta ez zuen mota guztietako trikimailu burutsurik erabili, beraz, liburutegi berrietara migrazioa nahiko erraz egin zen, mota, klase, izen-espazio eta iturburuan barne hartzen dituzten scripten laguntzarekin. fitxategiak. Migrazioak 10 iturburu fitxategiri eragin zien (000tik). wchar_t char14_t-rekin ordezkatu zen; wchar_t-en erabilera alde batera uztea erabaki dugu, zeren char000_t-ek 16 byte hartzen ditu sistema eragile guztietan eta ez du Windows eta Linuxen arteko kodearen bateragarritasuna hondatzen.

Abentura txiki batzuk egon ziren. Esate baterako, STLPort-en iterador bat inplizituki bota liteke elementu baten erakusle batera, eta gure kodeako zenbait lekutan hau erabili zen. Liburutegi berrietan jada ezin zen hori egin, eta pasarte horiek eskuz aztertu eta berridatzi behar ziren.

Beraz, kodearen migrazioa osatu da, kodea sistema eragile guztietarako konpilatuta dago. Probak egiteko garaia da.

Trantsizio osteko probek errendimenduaren jaitsiera (zenbait tokitan % 20-30 arte) eta memoria-kontsumoaren igoera (% 10-15 arte) erakutsi zuten kodearen bertsio zaharrarekin alderatuta. Hau, batez ere, kate estandarren errendimendu ezin hobea izan zen. Hori dela eta, berriro ere gure lerro propioa erabili behar izan dugu, pixka bat aldatua.

Liburutegi txertatuetan edukiontzien ezarpenaren ezaugarri interesgarri bat ere agerian geratu zen: hutsik (elementurik gabe) std::map eta std::set of built-in biblioteken memoria esleitu. Eta inplementazio-ezaugarriengatik, kodeko leku batzuetan mota honetako ontzi huts dezente sortzen dira. Memoria-edukiontzi estandarrak apur bat esleitzen dira, erro-elementu baterako, baina guretzat hori kritikoa izan zen - hainbat eszenatokitan, gure errendimendua nabarmen jaitsi zen eta memoria-kontsumoa handitu zen (STLPort-ekin alderatuta). Hori dela eta, gure kodean barne-liburutegietako bi edukiontzi mota hauek Boost-eko inplementazioarekin ordezkatu ditugu, non edukiontzi horiek ezaugarri hori ez zuten, eta honek moteltze eta memoria-kontsumo handitzearen arazoa konpondu zuen.

Proiektu handietan eskala handiko aldaketen ondoren gertatu ohi den bezala, iturburu-kodearen lehen errepikapenak ez zuen arazorik gabe funtzionatu, eta hemen, bereziki, Windows-en inplementazioan iteratzaileak arazketarako laguntza ondo etorri zen. Pausoz pauso aurrera egin genuen, eta 2017ko udaberrirako (8.3.11 1C:Enterprise bertsioa) migrazioa amaitu zen.

Emaitzak

C++14 estandarrerako trantsizioak 6 hilabete inguru behar izan genituen. Gehienetan, garatzaile bat (baina oso kualifikatua) lanean aritu zen proiektuan, eta azken fasean arlo zehatz batzuetako arduradunen taldeetako ordezkariak batu ziren - UI, zerbitzari-kluster, garapen- eta administrazio-tresnak, etab.

Trantsizioak asko erraztu zuen gure lana estandarraren azken bertsioetara migratzeko. Horrela, 1C:Enterprise 8.3.14 bertsioa (garapenean, datorren urtearen hasieran kaleratzea aurreikusita) estandarrera transferitu da dagoeneko. C++17.

Migrazioaren ondoren, garatzaileek aukera gehiago dituzte. Lehenago STL-ren bertsio aldatua eta std izen-espazio bat bagenuen, orain konpiladore-liburutegietako klase estandarrak ditugu std izen-espazioan, stdx izen-espazioan - gure lerroak eta edukiontziak gure zereginetarako optimizatuta, boost-ean -. boost-en azken bertsioa. Eta garatzaileak bere arazoak konpontzeko egokienak diren klaseak erabiltzen ditu.

Mugimendu-eraikitzaileen ezarpen "jatorrizkoak" garapenean ere laguntzen du (eraikitzaileak mugitu) hainbat klasetarako. Klase batek mugitzeko konstruktore bat badu eta klase hori edukiontzi batean jartzen bada, STLk edukiontzi barruko elementuen kopia optimizatzen du (adibidez, edukiontzia zabaltzen denean eta ahalmena aldatu eta memoria birlokatu behar denean).

Pomada hegan

Beharbada, migrazioaren ondorio desatseginena (baina ez kritikoena) bolumenaren igoeraren aurrean gaudela da. obj fitxategiak, eta bitarteko fitxategi guztiekin eraikitzearen emaitza osoa 60-70 GB hartzen hasi zen. Jokaera hori liburutegi estandar modernoen berezitasunei dagokie, sortutako zerbitzu-fitxategien tamainarekin hain kritiko bihurtu baitira. Horrek ez du konpilatutako aplikazioaren funtzionamenduan eragiten, baina garapenean hainbat eragozpen sortzen ditu, batez ere, konpilazio denbora handitzen du. Eraikitako zerbitzarietan eta garatzaileen makinetan disko libreko espazioaren eskakizunak ere handitzen ari dira. Gure garatzaileek plataformaren hainbat bertsiotan lan egiten dute paraleloki, eta tarteko fitxategien ehunka gigabytek batzuetan zailtasunak sortzen dituzte euren lanean. Arazoa desatsegina da, baina ez da kritikoa; oraingoz bere konponbidea atzeratu dugu. Teknologia hau konpontzeko aukeretako bat bezala hartzen ari gara batasuna eraikitzea (bereziki, Googlek erabiltzen du Chrome nabigatzailea garatzerakoan).

Iturria: www.habr.com

Gehitu iruzkin berria