Hoe't wy 10 miljoen rigels fan C++-koade oersette nei de C++14-standert (en dan nei C++17)

In skoft lyn (yn 'e hjerst fan 2016), by de ûntwikkeling fan' e folgjende ferzje fan it technologyplatfoarm 1C: Enterprise, ûntstie de fraach binnen it ûntwikkelteam oer it stypjen fan 'e nije standert C ++ 14 yn ús koade. De oergong nei in nije standert, lykas wy oannommen, soe tastean ús te skriuwen in protte dingen mear elegant, ienfâldich en betrouber, en soe ferienfâldigje de stipe en ûnderhâld fan de koade. En d'r liket neat bûtengewoan te wêzen yn 'e oersetting, as net foar de skaal fan' e koadebasis en de spesifike skaaimerken fan ús koade.

Foar dyjingen dy't it net witte, is 1C: Enterprise in omjouwing foar de rappe ûntwikkeling fan cross-platform saaklike applikaasjes en runtime foar har útfiering op ferskate OS's en DBMS's. Yn algemiene termen befettet it produkt:

  • Application Server Cluster, rint op Windows en Linux
  • Klant, wurkje mei de tsjinner fia http(s) of syn eigen binêre protokol, wurket op Windows, Linux, macOS
  • Web client, rint yn Chrome, Internet Explorer, Microsoft Edge, Firefox, Safari-browsers (skreaun yn JavaScript)
  • Untwikkelingsomjouwing (Konfigurator), wurket op Windows, Linux, macOS
  • Administration Tools applikaasje tsjinners, rinne op Windows, Linux, macOS
  • Mobile klant, ferbining mei de tsjinner fia http(s), wurket op mobile apparaten mei Android, iOS, Windows
  • Mobyl platfoarm - in ramt foar it meitsjen fan offline mobile applikaasjes mei de mooglikheid om te syngronisearjen, rinnend op Android, iOS, Windows
  • Untwikkeling omjouwing 1C: Tools foar bedriuwûntwikkeling, skreaun yn Java
  • Tsjinner Ynteraksje systemen

Wy besykje safolle mooglik deselde koade foar ferskate bestjoeringssystemen te skriuwen - de tsjinnerkoadebasis is 99% gewoan, de clientkoadebasis is sawat 95%. It 1C: Enterprise technologyplatfoarm is primêr skreaun yn C ++ en sawat koade-kenmerken wurde hjirûnder jûn:

  • 10 miljoen rigels fan C++-koade,
  • 14 tûzen bestannen,
  • 60 tûzen klassen,
  • heal miljoen metoaden.

En al dit guod moast oerset wurde yn C++14. Hjoed sille wy jo fertelle hoe't wy dit dien hawwe en wat wy yn it proses tsjinkamen.

Hoe't wy 10 miljoen rigels fan C++-koade oersette nei de C++14-standert (en dan nei C++17)

Disclaimer

Alles skreaun hjirûnder oer stadich / fluch wurk, (net) grut ûnthâld konsumpsje troch ymplemintaasjes fan standert klassen yn ferskate biblioteken betsjut ien ding: dit is wier FOAR US. It is goed mooglik dat standert ymplemintaasjes it bêste geskikt binne foar jo taken. Wy binne útgien fan ús eigen taken: wy namen gegevens dy't typysk wiene foar ús kliïnten, rûnen typyske senario's derop út, seagen nei prestaasjes, de hoemannichte ûnthâld konsumearre, ensfh., en analysearren oft wy en ús kliïnten tefreden wiene mei sokke resultaten of net . En se diene ôfhinklik fan.

Wat wy hiene

Yn it earstoan hawwe wy de koade skreaun foar it 1C: Enterprise 8-platfoarm yn Microsoft Visual Studio. It projekt begon yn 'e iere 2000's en wy hienen in Windows-allinich ferzje. Fansels, sûnt doe is de koade aktyf ûntwikkele, in protte meganismen binne folslein herskreaun. Mar de koade is skreaun neffens de 1998 standert, en bygelyks ús rjochte hoeke heakjes waarden skieden troch spaasjes sadat kompilaasje soe slagje, lykas dit:

vector<vector<int> > IntV;

Yn 2006, mei de frijlitting fan platfoarmferzje 8.1, begûnen wy Linux te stypjen en oerstapten nei in standertbibleteek fan tredden STLPort. Ien fan de redenen foar de oergong wie om mei brede linen te wurkjen. Yn ús koade brûke wy std :: wstring, dy't basearre is op it wchar_t-type, troch. De grutte yn Windows is 2 bytes, en yn Linux is de standert 4 bytes. Dit late ta inkompatibiliteit fan ús binêre protokollen tusken client en server, lykas ferskate persistente gegevens. Mei de gcc-opsjes kinne jo opjaan dat de grutte fan wchar_t by kompilaasje ek 2 bytes is, mar dan kinne jo ferjitte oer it brûken fan de standertbibleteek fan 'e kompiler, om't it brûkt glibc, dy't op syn beurt is kompilearre foar in 4-byte wchar_t. Oare redenen wiene bettere ymplemintaasje fan standertklassen, stipe foar hash-tabellen, en sels emulaasje fan 'e semantyk fan ferpleatse binnen konteners, dy't wy aktyf brûkten. En noch ien reden, sa't se sizze last but not least, wie snaarprestaasjes. Wy hienen in eigen klasse foar snaren, want... Fanwegen de spesifikaasjes fan ús software wurde stringoperaasjes heul breed brûkt en foar ús is dit kritysk.

Us snaar is basearre op snaaroptimalisaasjeideeën útdrukt yn 'e iere 2000's Andrei Alexandrescu. Letter, doe't Alexandrescu by Facebook wurke, waard op syn suggestje in line brûkt yn 'e Facebook-motor dy't wurke op ferlykbere prinsipes (sjoch bibleteek folly).

Us line brûkte twa wichtige optimisaasjetechnologyen:

  1. Foar koarte wearden wurdt in ynterne buffer yn 'e string foarwerp sels brûkt (net nedich ekstra ûnthâld tawizing).
  2. Foar alle oaren wurde meganika brûkt Kopiearje op skriuwen. De tekenrige wearde wurdt opslein op ien plak, en in referinsjeteller wurdt brûkt tidens opdracht / wiziging.

Om platfoarmkompilaasje te fersnellen, hawwe wy de stream-ymplemintaasje útsletten fan ús STLPort-fariant (dy't wy net hawwe brûkt), dit joech ús sawat 20% rapper kompilaasje. Neitiid moasten wy beheind gebrûk meitsje Boost. Boost makket swier gebrûk fan stream, benammen yn har tsjinst API's (bygelyks foar logging), dus wy moasten it oanpasse om it gebrûk fan stream te ferwiderjen. Dit, op syn beurt, makke it foar ús lestich om te migrearjen nei nije ferzjes fan Boost.

Tredde manier

By it ferpleatsen nei de C ++14-standert hawwe wy de folgjende opsjes beskôge:

  1. Upgrade de STLPort dy't wy hawwe wizige nei de C ++ 14-standert. De opsje is heul lestich, om't ... stipe foar STLPort waard staakt yn 2010, en wy moatte bouwe al syn koade sels.
  2. Oergong nei in oare STL ymplemintaasje kompatibel mei C ++ 14. It is heul winsklik dat dizze ymplemintaasje foar Windows en Linux is.
  3. By it kompilearjen foar elk OS, brûk de bibleteek ynboud yn 'e oerienkommende kompilator.

De earste opsje waard troch tefolle wurk perfoarst ôfwiisd.

Wy tochten in skoft oer de twadde opsje; beskôge as in kandidaat libc++, mar op dat stuit wurke it net ûnder Windows. Om libc++ nei Windows te portearjen, soene jo in protte wurk moatte dwaan - bygelyks alles sels skriuwe dat te krijen hat mei threads, threadsyngronisaasje en atomisiteit, om't libc++ yn dizze gebieten brûkt wurdt POSIX API.

En wy hawwe de tredde manier keazen.

Oergong

Dat, wy moasten it gebrûk fan STLPort ferfange troch de biblioteken fan 'e oerienkommende kompilatoren (Visual Studio 2015 foar Windows, gcc 7 foar Linux, clang 8 foar macOS).

Gelokkich is ús koade foaral neffens rjochtlinen skreaun en brûkte net allerhande tûke trúkjes, sadat de migraasje nei nije bibleteken relatyf flot ferrûn, mei help fan skripts dy't de nammen fan typen, klassen, nammeromten ferfongen en yn 'e boarne opnimme triemmen. De migraasje hat ynfloed op 10 boarnebestannen (fan 000). wchar_t waard ferfongen troch char14_t; wy besletten it gebrûk fan wchar_t te ferlitten, om't char000_t nimt 16 bytes op alle OS's en bedjert gjin koadekompatibiliteit tusken Windows en Linux.

Der wiene wat lytse aventoeren. Bygelyks, yn STLPort koe in iterator ymplisyt wurde cast nei in oanwizer nei in elemint, en op guon plakken yn ús koade waard dit brûkt. Yn nije bibleteken wie it net mear mooglik om te dwaan, en dizze passaazjes moasten wurde analysearre en op 'e nij skreaun.

Dat, de koademigraasje is kompleet, de koade is kompilearre foar alle bestjoeringssystemen. It is tiid foar testen.

Tests nei de oergong lieten in drop yn prestaasjes sjen (op guon plakken oant 20-30%) en in tanimming fan ûnthâldferbrûk (oant 10-15%) yn ferliking mei de âlde ferzje fan 'e koade. Dit wie benammen troch de suboptimale prestaasjes fan standert snaren. Dêrom moasten wy wer ús eigen, wat oanpaste line brûke.

In nijsgjirrich skaaimerk fan de ymplemintaasje fan konteners yn ynbêde biblioteken waard ek iepenbiere: lege (sûnder eleminten) std :: map en std :: set út ynboude biblioteken allocate ûnthâld. En troch de ymplemintaasjefunksjes wurde op guon plakken yn 'e koade in soad lege konteners fan dit type makke. Standert ûnthâld containers wurde tawiisd in bytsje, foar ien root elemint, mar foar ús dit blykte te wêzen kritysk - yn in oantal senario, ús prestaasjes sakke signifikant en ûnthâld konsumpsje tanommen (ferlike mei STLPort). Dêrom hawwe wy yn ús koade dizze twa soarten konteners fan 'e ynboude bibleteken ferfongen mei har ymplemintaasje fan Boost, wêr't dizze konteners dizze funksje net hawwe, en dit lost it probleem mei fertraging en ferhege ûnthâldferbrûk.

Lykas faaks bart nei grutskalige feroarings yn grutte projekten, wurke de earste iteraasje fan 'e boarnekoade net sûnder problemen, en hjir kaam benammen de stipe foar it debuggen fan iterators yn' e Windows-ymplemintaasje fan pas. Stap foar stap stapten wy foarút, en troch de maitiid fan 2017 (ferzje 8.3.11 1C: Enterprise) wie de migraasje foltôge.

Resultaten

De oergong nei de C ++ 14-standert naam ús sawat 6 moannen. Meast fan 'e tiid wurke ien (mar heul kwalifisearre) ûntwikkelder oan it projekt, en yn' e lêste etappe kamen fertsjintwurdigers fan teams ferantwurdlik foar spesifike gebieten by - UI, serverkluster, ark foar ûntwikkeling en administraasje, ensfh.

De oergong hat ús wurk by it migrearjen nei de lêste ferzjes fan 'e standert gâns ferienfâldige. Sa is ferzje 1C: Enterprise 8.3.14 (yn ûntwikkeling, release pland foar begjin takom jier) al oerbrocht nei de standert C++17.

Nei de migraasje hawwe ûntwikkelders mear opsjes. As wy earder ús eigen wizige ferzje fan STL en ien std-nammeromte hiene, hawwe wy no standertklassen fan 'e ynboude kompilatorbiblioteken yn' e std-nammeromte, yn 'e stdx-nammeromte - ús rigels en konteners optimalisearre foar ús taken, yn ympuls - de lêste ferzje fan boost. En de ûntwikkelder brûkt dy klassen dy't optimaal geskikt te lossen syn problemen.

De "native" ymplemintaasje fan move constructors helpt ek by ûntwikkeling (move constructors) foar in oantal klassen. As in klasse hat in move constructor en dizze klasse wurdt pleatst yn in kontener, dan de STL optimalisearret it kopiearjen fan eleminten binnen de kontener (Bygelyks, as de container wurdt útwreide en it is nedich om te feroarjen kapasiteit en reallocate ûnthâld).

Fly yn 'e sied

Miskien is it meast onaangename (mar net krityske) gefolch fan migraasje dat wy te krijen hawwe mei in ferheging fan it folume obj triemmen, en it folsleine resultaat fan 'e bou mei alle tuskenlizzende bestannen begon 60-70 GB op te nimmen. Dit gedrach komt troch de eigenaardichheden fan moderne standertbiblioteken, dy't minder kritysk wurden binne oer de grutte fan generearre tsjinstbestannen. Dit hat gjin ynfloed op 'e wurking fan' e kompilearre applikaasje, mar it feroarsaket in oantal ûngemak yn 'e ûntwikkeling, benammen it fergruttet de kompilaasjetiid. De easken foar frije skiifromte op buildservers en op ûntwikkelmasines wurde ek tanimmend. Us ûntwikkelders wurkje parallel oan ferskate ferzjes fan it platfoarm, en hûnderten gigabytes fan tuskenbestannen meitsje soms swierrichheden yn har wurk. It probleem is onaangenaam, mar net kritysk; wy hawwe de oplossing foar no útsteld. Wy beskôgje technology as ien fan 'e opsjes om it op te lossen ienheid bouwe (benammen Google brûkt it by it ûntwikkeljen fan de Chrome-blêder).

Boarne: www.habr.com

Add a comment