For some time (in the fall of 2016), during the development of the next version of the 1C:Enterprise technology platform, the question arose within the development team about supporting the new standard in our code. Transitioning to the new standard, as we assumed, would allow us to write many things more elegantly, simply, and reliably, making code support and maintenance easier. And while there seems to be nothing extraordinary in the translation, the scale of the codebase and the specific features of our code make it different.
For those who don't know, 1C:Enterprise is an environment for rapid development of cross-platform business applications and a runtime for executing them in various operating systems and DBMS. Broadly speaking, the product includes:
- , operates on Windows and Linux
- , working with the server via http(s) or through its own binary protocol, runs on Windows, Linux, macOS
- , works in browsers like Chrome, Internet Explorer, Microsoft Edge, Firefox, Safari (written in JavaScript)
- Development environment (), operates on Windows, Linux, macOS
- for application servers, run on Windows, Linux, macOS
- , connecting to the server via http(s), operates on mobile devices running Android, iOS, Windows
- — a framework for creating offline mobile applications with synchronization capabilities, working on Android, iOS, Windows
- Development Environment , written in Java
- Server
We try as much as possible to write a single code for different operating systems — the server codebase is 99% shared, the client — about 95%. The 1C:Enterprise technology platform is predominantly written in C++, and below are approximate characteristics of the code:
- 10 million lines of C++ code,
- 14 thousand files,
- 60 thousand classes,
- half a million methods.
And all this needed to be transitioned to C++14. Today we will tell you how we did it and what challenges we faced in the process.

Fraskrivelse
Alt, at der er nævnt hurtig/langsom ydelse, (ikke) stort hukommelsesforbrug i de standardklasser, der findes i forskellige biblioteker, betyder én ting: dette gælder FOR OS. Det er helt muligt, at standardimplementeringer fungerer bedst til dine opgaver. Vi har dog taget udgangspunkt i vores egne behov: vi arbejdede med typiske data fra vores kunder, kørte dem gennem standard scenarier, så på hastigheden, hukommelsesforbruget osv., og analyserede – om disse resultater tilfredsstiller os og vores kunder eller ej. Og vi handlede derefter.
Hvad vi havde
Oprindeligt skrev vi koden til platformen 1C:Enterprise 8 i Microsoft Visual Studio. Projektet startede i begyndelsen af 2000'erne, og vi havde kun en version til Windows. Naturligvis er koden siden da blevet kraftigt udviklet, mange mekanismer er blevet genkodet helt. Men koden blev skrevet efter standarden fra 1998, og for eksempel var de højre vinkelparenteser adskilt af mellemrum for at kunne kompileres, sådan her:
vector<vector > IntV;I 2006, med udgivelsen af version 8.1 af platformen, begyndte vi at støtte Linux og skiftede til et tredjeparts standardbibliotek . En af grundene til overgangen var arbejdet med brede strenge. I vores kode bruger vi udbredt std::wstring, baseret på typen wchar_t. Dens størrelse er 2 byte i Windows, mens den i Linux som standard er 4 byte. Dette førte til inkompatibilitet mellem vores binære protokoller mellem klient og server, samt forskellige persistente data. Med gcc's indstillinger kan man angive, at størrelsen på wchar_t ved kompilering også skal være 2 byte, men så kan man glemme at bruge standardbiblioteket fra compileren, da det bruger glibc, som er kompileret til 4-byte wchar_t. Andre grunde var en bedre implementering af standardklasser, support til hash-tabeller og endda emulering af flytningens semantik inden for containere, som vi brugte aktivt. Og en anden grund, som man siger, sidst men ikke mindst, var ydeevnen af strenge. Vi havde vores egen klasse til strenge, da string operationer i vores software er meget udbredte og kritiske for os.
Vores streng er baseret på ideerne om optimering af strenge, fremsat allerede i begyndelsen af 2000'erne . Senere, da Alexandrescu arbejdede hos Facebook, blev en lignende streng, der fungerede på tilsvarende principper, implementeret i Facebooks motor (se biblioteket ).
I vores streng brugte vi to primære optimeringsteknologier:
- Til korte værdier bruges en intern buffer i selve strengobjektet (som ikke kræver yderligere hukommelsestildeling).
- For alle andre bruges mekanikken . Strengens værdi opbevares ét sted, og ved tildeling/modifikation benyttes en referenceoptælling.
For at fremskynde platformens kompilation udelukkede vi stream-implementeringen fra vores version af STLPort (som vi ikke brugte), hvilket gav os en kompilationseffektivisering på cirka 20%. Senere måtte vi begrænset bruge . Boost bruger aktivt stream, særligt i sine service-API'er (f.eks. til logning), og derfor var vi nødt til at modificere det og ekskludere brugen af stream. Dette gjorde igen overgangen til nye versioner af Boost vanskelig for os.
Den tredje vej
Ved overgangen til standard C++14 overvejede vi sådanne muligheder:
- At opgradere den modificerede STLPort til standard C++14. Denne mulighed er meget udfordrende, da støtten til STLPort blev stoppet i 2010, og vi ville skulle opgradere hele dens kode selv.
- Overgang til en anden STL-implementering, der er kompatibel med C++14. Det er stærkt ønskeligt, at denne implementering fungerer på Windows og Linux.
- At bruge den indbyggede biblioteksfunktion fra den relevante compiler under kompilering for hver OS.
Den første mulighed blev straks afvist på grund af det for store arbejdsomfang.
Vi overvejede længe den anden mulighed; som kandidat overvejede vi , men den fungerede ikke på det tidspunkt på Windows. For at portere libc++ til Windows ville vi skulle udføre en betydelig amount arbejde - for eksempel skrive alt, der relaterede til tråde, tråd-synkronisering og atomaritet selv, da libc++ brugte .
Og vi valgte den tredje vej.
Overgang
Så vi skulle erstatte brugen af STLPort med bibliotekerne fra de relevante compilere (Visual Studio 2015 til Windows, gcc 7 til Linux, clang 8 til macOS).
Heldigvis blev vores kode hovedsageligt skrevet i henhold til retningslinjerne og brugte ikke forskellige snedige tricks, så migreringen til de nye biblioteker forløb relativt glat ved hjælp af scripts, der erstattede type-, klasse-, navnerums- og include-navne i kildefilerne. Migreringen omfattede 10.000 kildefiler (ud af 14.000). wchar_t blev erstattet med char16_t; vi besluttede at opgive brugen af wchar_t, da char16_t på alle operativsystemer optager 2 byte og ikke ødelægger kodekompatibiliteten mellem Windows og Linux.
Det blev ikke uden lidt eventyr. For eksempel kunne en iterator i STLPort implicit castes til en pointer til et element, og dette blev brugt nogle steder i vores kode. I de nye biblioteker var det ikke længere muligt, og disse steder måtte analyseres og skrives om manuelt.
Så migreringen af koden er afsluttet, og koden kompilere for alle operativsystemer. Det er tid til tests.
Testene efter overgangen viste et fald i ydeevnen (nogle steder op til 20-30%) og en stigning i det forbrugte hukommelse (op til 10-15%) sammenlignet med den gamle version af koden. Dette hængte blandt andet sammen med den suboptimale funktionalitet af standardstrenge. Derfor måtte vi igen bruge vores egen, lidt tilpassede streng.
Der opstod også en interessant egenskab ved implementeringen af containere i indbyggede biblioteker: tomme (uden elementer) std::map og std::set fra de indbyggede biblioteker allokerer hukommelse. I vores kode, på grund af implementeringsspecifikationer, blev der oprettet en del tomme containere af denne type. De standardcontainere tildeler lidt hukommelse, for et rod element, men for os viste det sig at være kritisk – i en række scenarier faldt vores ydeevne betydeligt, og hukommelsesforbruget steg (sammenlignet med STLPort). Derfor erstattede vi i vores kode disse to typer containere fra de indbyggede biblioteker med deres implementering fra Boost, hvor disse containere ikke havde denne egenskab, og det løste problemet med forsinkelse og øget hukommelsesforbrug.
Som det ofte sker efter omfattende ændringer i store projekter, fungerede den første iteration af koden ikke uden problemer, og her var støtte fra de debugger-iteratorer i Windows-implementeringen særligt nyttig. Skridt for skridt bevægede vi os fremad, og i foråret 2017 (version 8.3.11 1C:Enterprise) var migreringen afsluttet.
Resumé
Overgangen til standard C++14 tog os omkring 6 måneder. Størstedelen af tiden arbejdede en (meget kvalificeret) udvikler alene på projektet, men i den endelige fase blev repræsentanter fra de teams, der var ansvarlige for specifikke områder — UI, klynger af servere, udviklings- og administrationsværktøjer osv., involveret.
Overgangen gjorde det betydeligt lettere for os at migrere til de nyeste versioner af standarden. Således er version 1C:Enterprise 8.3.14 (under udvikling, release planlagt til starten af næste år) allerede oversat til standarden. .
Efter migreringen har udviklerne fået flere muligheder. Tidligere havde vi vores egen tilpassede version af STL og et namespace std, men nu indeholder namespace std standardklasser fra compilerens indbyggede biblioteker, i namespace stdx — vores, optimerede strenge og containere til vores opgaver, og i boost — den nyeste version af boost. Udvikleren bruger de klasser, der passer bedst til løsning af hans opgaver.
Det hjælper også i udviklingen med den 'native' implementering af flyttekonstruktører () for en række klasser. Hvis en klasse har en flyttekonstruktor, og denne klasse placeres i en container, så optimerer STL kopieringen af elementer inden for containeren (for eksempel når containeren udvides, og der skal ændres capacity og genallokeres hukommelse).
Et skævt element
Det mest ubehagelige (men ikke kritiske) resultat af migreringen er, at vi har oplevet en stigning i mængden af , og den samlede build-output med alle mellemfilerne begyndte at fylde 60 – 70 GB. Denne adfærd skyldes moderne standardbibliotekers karakteristika, der er blevet mindre kritiske over for mængden af genererede servicefiler. Det påvirker ikke driften af den kompilerede applikation, men medfører en række ulemper i udviklingen, især forlænger det kompileringstiden. Kravene til ledig plads på disken både på build-servere og udviklernes maskiner er også steget. Vores udviklere arbejder parallelt på flere versioner af platformen, og hundredvis af gigabyte mellemfiler kan nogle gange skabe vanskeligheder i arbejdet. Problemet er ubehageligt, men ikke kritisk; vi har indtil videre udskudt løsningen. Som en af muligheden for at løse det betragter vi teknikken (det bruges blandt andet af Google til at udvikle browseren Chrome).
Kilde: habr.com
