Dikur (në vjeshtën e vitit 2016), gjatë zhvillimit të një versioni të ri të platformës teknologjike 1C:Enterprise, ekipi i zhvillimit u ballafaqua me çështjen e mbështetjes së standardit të ri në kodin tonë. Kalimi në standardin e ri, siç e parashikuam, do të na lejonte të shkruanim shumë gjëra më eleganta, më të thjeshta dhe më të besueshme, duke e thjeshtuar mbështetje dhe mirëmbajtjen e kodit. Duket se nuk ka asgjë të jashtëzakonshme në përkthim, përveç përmasave të kodit dhe veçorive specifike të kodit tonë.
Për ata që nuk e dinë, 1C:Enterprise është një mjedis për zhvillimin e shpejtë të aplikacioneve biznesore ndër-platformë dhe runtime për ekzekutimin e tyre në sisteme të ndryshme operative dhe DBMS. Në përmbledhje, produkti përfshin:
- , punon në Windows dhe Linux
- , që komunikon me serverin përmes http(s) ose me protokollin tonë binar, punon në Windows, Linux, macOS
- , që funksionon në shfletuesit Chrome, Internet Explorer, Microsoft Edge, Firefox, Safari (shkruar në JavaScript)
- Mjedisi i zhvillimit (), punon në Windows, Linux, macOS
- të serverave të aplikacioneve, punojnë në Windows, Linux, macOS
- , që lidhet me serverin përmes http(s), punon në pajisje mobile me sistemin operativ Android, iOS, Windows
- â njĂ« kornizĂ« pĂ«r krijimin e aplikacioneve mobile offline me mundĂ«si sinkronizimi, punon nĂ« Android, iOS, Windows
- Mjedisi i zhvillimit , shkruar në Java
- ĐĄĐ”ŃĐČĐ”Ń
Ne pĂ«rpiqemi tĂ« shkruajmĂ« njĂ« kod tĂ« vetĂ«m pĂ«r sisteme tĂ« ndryshme operative â baza e kodit tĂ« serverit Ă«shtĂ« e pĂ«rbashkĂ«t nĂ« 99%, e klientit â rreth 95%. Platforma teknologjike 1C:Enterprise Ă«shtĂ« kryesisht e shkruar nĂ« C++ dhe mĂ« poshtĂ« janĂ« karakteristikat pĂ«rkatĂ«se tĂ« kodit:
- 10 milion rreshta kodi C++,
- 14 mijë skedarë,
- 60 mijë klasa,
- një gjysmë milioni metoda.
Dhe të gjithë këto gjëra duhej të përfitoheshin në C++14. Ne do të tregojmë sot se si e bëmë këtë dhe me çfarë sfidash u përballëm në proces.

Kufizimi
Ădo gjĂ« e shkruar mĂ« poshtĂ« rreth punĂ«s sĂ« ngadaltĂ«/shpejtĂ«, (jo) konsumi i vogĂ«l tĂ« memories nga implementimet e klasave standarde nĂ« biblioteka tĂ« ndryshme Ă«shtĂ« e vĂ«rtetĂ«: kjo Ă«shtĂ« e saktĂ« PĂR NE. Mund tĂ« jetĂ« qĂ« pĂ«r detyrat tuaja implementimet standarde tĂ« ndihmojnĂ« mĂ« sĂ« miri. Ne u bazuam nĂ« nevojat tona: morĂ«m tĂ« dhĂ«nat tipike pĂ«r klientĂ«t tanĂ«, i testuam nĂ« skenarĂ« tipikĂ«, vlerĂ«suam shpejtĂ«sinĂ« e punĂ«s, sasinĂ« e memories sĂ« konsumuar, etj., dhe analizuam â a na kĂ«naqin rezultatat e tilla ne dhe klientĂ«t tanĂ« apo jo. Dhe vepruam pĂ«rkatĂ«sisht.
ĂfarĂ« kishim
Fillimisht, ne shkruam kodin e platformës 1C:Enterprise 8 në Microsoft Visual Studio. Projekti filloi në fillim të viteve 2000 dhe ne kishim vetëm një version për Windows. Natyrisht, që atëherë kodi u zhvillua aktivisht, shumë mekanizma u rishkruan plotësisht. Por kodi ishte shkruar sipas standardit të vitit 1998, dhe për shembull, këndet e djathta ishin ndarë me hapësira për të kaluar me sukses kompilimin, kështu:
vector<vector > IntV;Në vitin 2006, me daljen e versionit 8.1 të platformës, ne filluam të mbështesim Linux dhe kaluam në një bibliotekë standarde të jashtme . Një nga arsyet për kalimin ishte puna me vargje të gjera. Ne përdorim gjithashtu std::wstring, i bazuar në tipin wchar_t. Madhësia e tij në Windows është 2 byte, ndërsa në Linux për default është 4 byte. Kjo çoi në papajtueshmëri të protokolleve tona binarë midis klientit dhe serverit, si dhe të dhënave të ndryshme të qëndrueshme. Me opsionet e gcc mund të specifikohet që madhësia e wchar_t gjatë kompilimit të jetë gjithashtu 2 byte, por atëherë harrojmë për përdorimin e bibliotekës standarde nga kompajleri, pasi ajo përdor glibc, e cila është e kompajluar për wchar_t 4-byte. Arsyet e tjera ishin implementimi më cilësor i klasave standarde, mbështetje e tabelave të hash dhe madje edhe imitim i semantikës së lëvizjes brenda konteinerëve, të cilat ne i përdorim aktivisht. Dhe një arsye tjetër, siç thuhet është e fundit por jo më pak e rëndësishme, ishte performanca e vargjeve. Ne kishim klasën tonë për vargje, pasi për shkak të natyrës së softuerit tonë, operacionet mbi vargje përdoren shumë gjerësisht dhe për ne kjo është kritike.
Vargu ynë është i bazuar në idetë e optimizimit të vargjeve, të paraqitura në fillim të viteve 2000 . Më vonë, kur Alexandrescu punonte në Facebook, në iniciativën e tij në motorin e Facebook u përdor një varg që funksiononte sipas parimeve të ngjashme (shih bibliotekën ).
. Në vargun tonë ishin përdorur dy teknologji kryesore optimizimi:
- Për vlera të shkurtra, përdoret një tampon i brendshëm në objektin e vargut (që nuk kërkon alokim të shtesë të memories).
- Për të gjitha të tjerat përdoret mekanika . Vlera e vargut ruhet në një vend, kur i atribuohet/modifikohet përdoret numri i referencave.
Për të përshpejtuar kompilimin e platformës, ne përjashtuam nga versioni ynë STLPort implementimin e stream (të cilin nuk e përdorim), çka na dha një përshpejtim të kompilimit afërsisht 20%. Më vonë, na duhej të përdornim me kufizime. . Boost përdor aktivisht stream, sidomos në API-të e tij të shërbimeve (p.sh. për logimin), prandaj na duhej të modifikonim atë, duke përjashtuar përdorimin e stream. Kjo, nga ana tjetër, e bëri të vështirë kalimin në versionet e reja të Boost.
Rruga e tretë
Kur kaluam në standardin C++14, shqyrtuam këto mundësi:
- Të çonim STLPortin e modifikuar nga ana jonë në standardin C++14. Kjo opsion ishte shumë e vështirë, pasi mbështetja për STLPort ishte ndalur në vitin 2010, dhe na duhej ta ngrisnim të gjithë kodin vetë.
- Të kalojmë në një implementim tjetër të STL, të kompatibilshëm me C++14. Preferohej që ky implementim të ishte për Windows dhe Linux.
- Të përdorim bibliotekën e integruar të përkatshëm për çdo OS gjatë kompilimit.
Opsioni i parë u refuzua menjëherë për shkak të volumit të madh të punës.
NĂ« fillim menduam pĂ«r opsionin e dytĂ«; si kandidat kishim , por nĂ« atĂ« kohĂ« nuk punonte pĂ«r Windows. PĂ«r tĂ« portuar libc++ nĂ« Windows, do tĂ« duhej tĂ« bĂ«nim shumĂ« punĂ« â pĂ«r shembull, tĂ« shkruanim vetĂ« gjithçka qĂ« lidhet me rrjedhat, sinkronizimin e rrjedhave dhe atomikĂ«n, pasi nĂ« libc++ ishin pĂ«rdorur .
Dhe zgjodhëm rrugën e tretë.
Kalimi
Pra, na duhej të zëvendësonim përdorimin e STLPort me bibliotekat përkatëse të kompilerëve (Visual Studio 2015 për Windows, gcc 7 për Linux, clang 8 për macOS).
Fatmirësisht, kodi ynë ishte kryesisht i shkruar sipas udhëzimeve dhe nuk përdorte truket e ndryshme, kështu që migraimi në bibliotekat e reja kaloi mjaft pa probleme, duke përdorur skriptet që zëvendësuan në skedarët e burimit emrat e tipave, klasave, hapësirave të emrave dhe përfshirjeve. Migrimi përfshiu 10,000 skedarë burimorë (nga 14,000). wchar_t u zëvendësua me char16_t; ne vendosëm të hiqnim përdorimin e wchar_t, pasi char16_t në të gjitha OS-të merr 2 byte dhe nuk prish kompatibilitetin e kodit midis Windows dhe Linux.
Nuk kaluam pa aventura të vogla. Për shembull, në STLPort një iterator mund të konvertohej në mënyrë të pakët në një tregues për elementin, dhe në disa vende në kodin tonë kjo ishte përdorur. Në bibliotekat e reja nuk ishte më e mundur të bëhej kështu, dhe këto vende duhej të analizohej dhe të ri-shkruheshin manualisht.
Pra, migrimi i kodit përfundoi, kodi kompilon për të gjitha OS-të. Ka ardhur koha për testet.
Testet pas kalimit treguan një rënie të performancës (në disa raste deri në 20-30%) dhe rritje të konsumit të memories (deri në 10-15%) krahasuar me versionin e vjetër të kodit. Kjo ishte, përveç të tjerash, e lidhur me punën jo optimale të vargjeve standarde. Prandaj na duhej sërish të përdornim vargje të tona, të përmirësuara pak.
Gjithashtu u zbulua një karakteristikë interesante e implementimit të kontejnerëve në bibliotekat e integruara: std::map dhe std::set të zbrazët (pa elemente) nga bibliotekat e integruara alokojnë memorie. Dhe në disa vende në kodin tonë krijohej një numër mjaft i madh kontejnerësh të tilla. Standardet e kontejnerëve alokojnë pak memorie, për një element rrënjë, por për ne kjo u bë kritike - në disa skenarë kemi vënë re një rënie të ndjeshme të performancës dhe një rritje të konsumit të memories (krahasuar me STLPort). Prandaj ne zëvendësuam në kodin tonë këto dy tipe kontejnerësh nga bibliotekat e integruara me implementimin e tyre nga Boost, ku këto kontejnerë nuk kishin këtë karakteristikë dhe kjo zgjidhi problemin me ngadalësimin dhe rritjen e konsumit të memories.
Siç ndodh shpesh pas ndryshimeve masive në projekte të mëdha, iteracioni i parë i burimeve nuk funksionoi pa probleme, dhe këtu na ndihmoi shumë, në veçanti, mbështetja për iteratorët për-debug në implementimin e Windows. Hap pas hapi ne po lëviznim përpara, dhe në pranverën e vitit 2017 (versioni 8.3.11 të 1C:Enterprise) migraimi përfundoi.
Përfundimet
Kalimi në standardin C++14 na mori rreth 6 muaj. Kryesisht një zhvillues (por shumë i kualifikuar) punoi mbi projektin, dhe në fazën përfundimtare u përfshinë përfaqësues të ekipeve që ishin përgjegjës për fusha të caktuara - UI, serverin e klasterëve, mjetet e zhvillimit dhe administratës, etj.
Kalimi e thjeshtoi shumë punën tonë për migrimin në versionet më të fundit të standardit. Në këtë mënyrë, versioni 1C:Enterprise 8.3.14 (në zhvillim, publikimi është planifikuar për fillimin e vitit të ardhshëm) tashmë është përkthyer në standard .
Pas migros, zhvilluesit fituan mĂ« shumĂ« mundĂ«si. NĂ«se mĂ« parĂ« kishim versionin tonĂ« tĂ« pĂ«rmirĂ«suar tĂ« STL dhe njĂ« hapĂ«sirĂ« emrave std, tani hapĂ«sira jonĂ« e emrave std pĂ«rmban klasat standarde nga bibliotekat e integruara tĂ« kompilatorit, nĂ« hapĂ«sirĂ«n e emrave stdx â klasat tona tĂ« optimizuara pĂ«r ne, si dhe vargje dhe kontejnerĂ«, ndĂ«rsa nĂ« boost Ă«shtĂ« versioni mĂ« i ri i boost. Dhe zhvilluesi pĂ«rdor ato klasa, tĂ« cilat pĂ«rputhen mĂ« mirĂ« me zgjidhjen e problemeve tĂ« tij.
Po ashtu ndihmon edhe implementimi 'i natyrshëm' i konstruktorëve të lëvizjes () për disa klasa. Nëse një klasë ka një konstruktor lëvizjeje dhe kjo klasë vihet në një kontejner, STL optimizon kopjimin e elementeve brenda kontejnerit (p.sh., kur kontejneri zgjerohet dhe duhet të ndryshojë kapacitetin dhe të rialokojë memorjen).
Një lugë katran
Mund tĂ« jetĂ«, ndoshta, pasoja mĂ« e pakĂ«ndshme (por jo fatale) e migros â u pĂ«rballĂ«m me rritjen e volumit , dhe rezultati pĂ«rfundimtar i ndĂ«rtimit me tĂ« gjitha skedarĂ«t e ndĂ«rmjetĂ«m tani zĂ« 60 â 70 GB. Ky sjellje Ă«shtĂ« e lidhur me karakteristikat e bibliotekave standarde moderne, tĂ« cilat janĂ« bĂ«rĂ« mĂ« pak kritike pĂ«r volumin e skedarĂ«ve shĂ«rbyes qĂ« gjenerohen. Kjo nuk ndikon nĂ« funksionimin e aplikacionit tĂ« kompiluar, por krijon disa shqetĂ«sime nĂ« zhvillim, veçanĂ«risht rrit kohĂ«n e kompilimit. KĂ«rkesat pĂ«r hapĂ«sirĂ«n e lirĂ« nĂ« disqe nĂ« serverĂ«t e ndĂ«rtimit dhe nĂ« makinat e zhvilluesve po ashtu janĂ« rritur. Zhvilluesit tanĂ« punojnĂ« paralelisht mbi disa versionet e platformĂ«s, dhe qindra gigabajt skedarĂ«sh tĂ« ndĂ«rmjetĂ«m ndonjĂ«herĂ« krijojnĂ« vĂ«shtirĂ«si nĂ« punĂ«. Problemi Ă«shtĂ« i pakĂ«ndshĂ«m, por jo kritik, ndihmĂ«n pĂ«r tĂ« zgjidhur atĂ« e kemi shtyrĂ« pĂ«r momentin. NjĂ« nga opsionet e zgjidhjes sĂ« saj po shqyrtojmĂ« Ă«shtĂ« teknika (e cila, veçanĂ«risht, pĂ«rdoret nga Google gjatĂ« zhvillimit tĂ« shfletuesit Chrome).
Burimi: habr.com
