Disa para një kohe (në vjeshtën e 2016), gjatë zhvillimit të një versioni të ri të platformës teknologjike 1C:Enterprise, ekipi i zhvillimit u përball me çështjen e mbështetjes së standardit të ri në kodin tonë. Kalimi në standardin e ri, siç parashikuam, do të na lejonte të shkruante shumë gjëra më elegant, më thjesht dhe më të besueshëm, duke thjeshtuar mbështetje dhe mirëmbajtje të kodit. Dhe në përkthim nuk duket se ka ndonjë gjë të jashtëzakonshme, nëse nuk merr parasysh shkallët e bazës së kodit dhe karakteristikat 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ërplatformë dhe runtime për ekzekutimin e tyre në sisteme të ndryshme operative dhe DB. Në përmbledhje, produkti përfshin:
- , punon në Windows dhe Linux
- , i cili komunikon me serverin përmes http(s) ose me protokollin e tij binar, punon në Windows, Linux, macOS
- , punon në shfletuesit Chrome, Internet Explorer, Microsoft Edge, Firefox, Safari (i shkruar në JavaScript)
- Mjedisi i zhvillimit (), punon në Windows, Linux, macOS
- të serverave të aplikacioneve, punojnë në Windows, Linux, macOS
- , i cili lidhet me serverin përmes http(s), punon në pajisje mobile që përdorin Android, iOS, Windows
- â njĂ« kornizĂ« pĂ«r krijimin e aplikacioneve mobile offline me mundĂ«si sinkronizimi, punon nĂ« Android, iOS, Windows
- Mjedisi i zhvillimit , e shkruar në Java
- Server
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 e pĂ«rafĂ«rta tĂ« kodit:
- 10 milion rreshta kodi C++,
- 14 mijë skeda,
- 60 mijë klasa,
- për gjysmë milioni metoda.
Dhe e gjithë kjo duhej të kalonte në C++14. Sot do të tregojmë se si e bëmë këtë dhe me çfarë u përballëm gjatë procesit.

Kërcënimi
Gjithçka qĂ« Ă«shtĂ« shkruar mĂ« poshtĂ« pĂ«r punĂ«n e ngadalshme / tĂ« shpejtĂ«, (sĂ«)cila konsum tĂ« vogĂ«l tĂ« memories me implementimet e klasave standarde nĂ« biblioteka tĂ« ndryshme do tĂ« thotĂ« njĂ« gjĂ«: kjo Ă«shtĂ« e vĂ«rtetĂ« PĂR NE. Mbase, pĂ«r detyrat tuaja, implementimet standarde mund tĂ« jenĂ« mĂ« tĂ« pĂ«rshtatshme. Ne gjithashtu ishim tĂ« orientuar nga detyrat tona: morĂ«m tipare tipike tĂ« klientĂ«ve tanĂ«, i testuam ato me skenarĂ« tipikĂ«, shqyrtuam performancĂ«n, vĂ«llimin e memorizimit qĂ« pĂ«rdorej, etj., dhe analizuam nĂ«se rezultatet na plotĂ«sonin ne dhe klientĂ«t tanĂ« apo jo. Dhe vepruam nĂ« pĂ«rputhje me kĂ«tĂ«.
ĂfarĂ« kishim
Fillimisht e shkruam kodin e platformës 1C:Enterprise 8 në Microsoft Visual Studio. Projekti filloi në fillim të viteve 2000 dhe ne kishim vetëm versionin për Windows. Natyrisht, që nga atëherë kodi e përjetoi një zhvillim aktiv, shumë mekanizma u shkruan nga e para. Por kodi u shkrua sipas standardeve të vitit 1998, dhe, për shembull, këndet e djathta ishin të ndara me hapësira për të kaluar me sukses kompaktimin, kështu:
vector<vector > IntV;Në vitin 2006, me lançimin e versionit 8.1 të platformës, filluam të mbështesim Linux dhe kaluam në një bibliotekë standarde të tretë . Një nga arsyet për kalim ishte puna me stringjet e gjerë. Në kodin tonë ne përdorim gjithandej 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 shkaktonte mospërputhje të protokolleve tona binarë midis klientit dhe serverit, si dhe të të dhënave të ndryshme persistente. Me opsionet gcc mund të specifikohet që madhësia e wchar_t gjatë kompaktimit të jetë gjithashtu 2 byte, por atëherë mund të harrohet për përdorimin e bibliotekës standarde nga kompilatorët, sepse ajo përdor glibc, e cila gjithashtu është kompaktuar për wchar_t 4-byte. Arsyet e tjera ishin implementimi më cilësor i klasave standarde, mbështetje për tabelat hash dhe madje edhe simulimi i semantikës së zhvendosjes brenda kontejnerëve, nga e cila e përdorim aktivisht. Dhe një arsye tjetër, siç thonë, e fundit por jo më pak e rëndësishme, ishte performanca e stringjeve. Ne kishim klasën tonë për stringjet, sepse për shkak të specifikave të softuerit tonë operacionet me string janë përdorur shumë gjerësisht dhe për ne ajo është kritike.
Stringu ynë bazohet në idetë për optimizimin e stringjeve, të shprehura që në fillim të viteve 2000 . Më vonë, kur Aleksandresku punonte në Facebook, me nismën e tij, një rresht që punonte në parime të ngjashme u përdor në motorin e Facebook (shihni bibliotekën ).
. Në rreshtin tonë u përdorën dy teknologjitë kryesore të optimizimit:
- Për vlera të shkurtra, përdoret një buffer i brendshëm në vetë objektin e rreshtit (i cili nuk kërkon alokim të mëtejshëm të memorjes).
- Për të gjitha të tjerat përdoret mekanika . Vlera e rreshtit ruhet në një vend, dhe kur ndodhin caktime/modifikime, përdoret një numër i referencave.
Për të përshpejtuar kompaktimin e platformës, ne përjashtuam nga varianti ynë i STLPort zbatimin e stream (të cilin nuk e përdorëm), që na dha një përshpejtim të kompaktimit prej rreth 20%. Më pas na duhej të përdornim në mënyrë të kufizuar . Boost përdor aktivisht stream, sidomos në API-të e tij të shërbimit (për shembull, për logimin), kështu që na duhej ta modifikonim atë, duke përjashtuar përdorimin e stream. Kjo, nga ana e saj, e vështirësoi kalimin tonë në versionet e reja të Boost.
Mënyra e tretë
Në kalimin në standardin C++14, ne shqyrtuam këto opsione:
- Të ngrinim STLPort e modifikuar nga ne në standardin C++14. Kjo është një opsion shumë i komplikuar, sepse mbështetja për STLPort u ndërpre në 2010, dhe duhej të ngrinim të gjithë kodin e tij vetë.
- TĂ« kalonim nĂ« njĂ« implementim tjetĂ«r tĂ« STL, tĂ« pĂ«rputhshĂ«m me C++14. ĂshtĂ« shumĂ« e dĂ«shirueshme qĂ« kjo implementim tĂ« ishte pĂ«r Windows dhe Linux.
- Të përdorim bibliotekën e integruar në përkatësin kompjuter për secilën OS gjatë kompaktimit.
Opsioni i parë u hodh poshtë menjëherë për shkak të volumit të madh të punës.
Ne menduam pĂ«r njĂ« kohĂ« mbi opsionin e dytĂ«; si kandidat shqyrtuam , por nĂ« atĂ« kohĂ« nuk funksiononte nĂ«n 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++ nĂ« kĂ«to fusha u pĂ«rdor .
Dhe ne zgjodhëm mënyrë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).
Fatkeqësisht, kodi ynë u shkrua kryesisht sipas udhëzimeve dhe nuk përdori truqe të ndërlikuara, kështu që migrimi në bibliotekat e reja kaloi relativisht pa probleme, me ndihmën e skripteve që zëvendësonin emrat e tipeve, klasave, hapësirave të emrave dhe përfshirjeve në skedarët burimorë. Migrimi ndau 10,000 skedarë burimorë (nga 14,000). wchar_t u zëvendësua me char16_t; ne vendosëm të heqim dorë nga përdorimi i wchar_t, sepse char16_t në të gjitha sistemet operative merr 2 byte dhe nuk dëmton kompatibilitetin e kodit mes Windows dhe Linux.
Nuk munguan aventura të vogla. Për shembull, në STLPort, itereatori mund të kalohej në mënyrë të paqartë në një tregues ndaj elementit, dhe në disa vende të kodit tonë kjo ishte përdorur. Në bibliotekat e reja kjo nuk ishte më e mundur, dhe këto vende duhej të analizohej dhe rishkruheshin manualisht.
Pra, migrimi i kodit përfundoi, kodi kompilohet për të gjitha sistemet operative. Ka ardhur koha për testime.
Testet pas kalimit treguan një rënie të performancës (në disa raste deri në 20-30%) dhe një rritje të konsumit të memories (deri në 10-15%) krahasuar me versionin e vjetër të kodit. Kjo u lidhi, mes të tjerash, me punën jo optimale të vargjeve standarde. Prandaj, ata na duhej përsëri të përdornim vargun tonë, të modifikuar pak.
Gjithashtu, doli një veçori interesante e implementimit të enëve në bibliotekat e integruara: std::map dhe std::set të zbrazëta (pa elemente) nga bibliotekat e integruara alokojnë memorje. Në kodin tonë, për shkak të veçorive të implementimit, krijohen shumë enë të zbrazëta të këtij lloji në disa vende të kodit. Enët standarde alokojnë disi memorje, për një element themelor, por për ne kjo u krijua kritikisht - në disa skenarë, performanca jonë u ul ndjeshëm dhe rritja e konsumit të memories (krahasuar me STLPort). Prandaj, ne zëvendësuam këto dy lloje enësh nga bibliotekat e integruara me implementimin e tyre nga Boost, ku këto enë nuk kishin një veçori të tillë, dhe kjo zgjidhi problemin me ngadalësimin dhe rritjen e konsumit të memories.
Siç ndodh shpesh pas ndryshimeve të mëdha në projekte të mëdha, iteracioni i parë i burimeve punoi jo pa probleme, dhe këtu na ndihmoi shumë, sidomos mbështetja e itereatorëve për debugging në implementimin për Windows. Hapa-hapa ne shkuam përpara, dhe deri në pranverën e vitit 2017 (versioni 8.3.11 i 1C:Enterprise) migrimi u përfundua.
Përfundime
Kalimi në standardin C++14 mori rreth 6 muaj. Një zhvillues (por shumë i kualifikuar) punoi për pjesën më të madhe të projektit, ndërsa në fazën finale u bashkuan përfaqësuesit e grupeve përgjegjëse për fusha specifike - UI, klasterët e serverëve, mjetet e zhvillimit dhe administratës etj.
Kalimi na e thjeshtoi shumë punën e migrimit në versionet më të reja të standardit. Kështu, versioni 1C:Enterprise 8.3.14 (në zhvillim, lançimi është planifikuar për fillimin e vitit të ardhshëm) është tashmë i përkthyer në standardin .
Pas migrimit, zhvilluesit morën më shumë mundësi. Nëse më parë kishim versionin tonë të modifikuar të STL dhe një hapësirë emërtimi std, tani në hapësirën emërtuese std ndodhen klasat standarde nga bibliotekat e integruara të kompilatorit, në hapësirën emërtuese stdx - klasat tona, optimizuar për detyrat tona, dhe në boost - versioni i ri i boost. Zhvilluesi përdor ato klasa që përshtaten më mirë për zgjidhjen e detyrave të tij.
Kështu ndihmon në zhvillim 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ë vendoset në një konteiner, atëherë STL e optimizon kopjimin e elementeve brenda konteinerit (për shembull, kur konteneri zgjerohet dhe duhet të ndryshohet kapaciteti dhe të rialokohet memoria).
Një lugë katran
MĂ« e pakĂ«ndshmja (por jo kritike) pasojĂ« e migrimit - ne u pĂ«rballĂ«m me rritjen e volumit tĂ« , dhe rezultati i plotĂ« i ndĂ«rtimit me tĂ« gjitha skedarĂ«t ndĂ«rmjetĂ«s zuri 60 â 70 GB. Ky sjellje lidhet me karakteristikat e bibliotikave moderne standarde, tĂ« cilat janĂ« bĂ«rĂ« mĂ« pak kritike ndaj volumit tĂ« skedarĂ«ve shĂ«rbim qĂ« gjenerohen. Kjo nuk ndikon nĂ« funksionimin e aplikacioneve tĂ« komiluara, por krijon disa shqetĂ«sime nĂ« zhvillim, duke rritur kohĂ«n e kompilimit. Rriten gjithashtu kĂ«rkesat pĂ«r hapĂ«sirĂ« tĂ« lirĂ« nĂ« disqe nĂ« serverĂ«t e ndĂ«rtimit dhe nĂ« makinat e zhvilluesve. Zhvilluesit tanĂ« punojnĂ« paralelisht nĂ« disa versione tĂ« platformĂ«s, dhe qindra gigabajt tĂ« skedarĂ«ve ndĂ«rmjetĂ«s ndonjĂ«herĂ« krijojnĂ« vĂ«shtirĂ«si nĂ« punĂ«. Problemi Ă«shtĂ« i pakĂ«ndshĂ«m, por jo kritik; pĂ«r zgjidhjen e tij, pĂ«r momentin e kemi shtyrĂ«. Si njĂ« nga opsionet pĂ«r zgjidhjen e tij, po shqyrtojmĂ« teknikĂ«n (e.g., it is used by Google in the development of the Chrome browser).
Burimi: habr.com
