Paano namin isinalin ang 10 milyong linya ng C++ code sa pamantayang C++14 (at pagkatapos ay sa C++17)

Ilang oras na ang nakalipas (sa taglagas ng 2016), sa panahon ng pagbuo ng susunod na bersyon ng platform ng teknolohiyang 1C:Enterprise, lumitaw ang tanong sa loob ng development team tungkol sa pagsuporta sa bagong pamantayan. C ++ 14 sa aming code. Ang paglipat sa isang bagong pamantayan, tulad ng ipinapalagay namin, ay magbibigay-daan sa amin na magsulat ng maraming bagay nang mas elegante, simple at mapagkakatiwalaan, at magpapasimple sa suporta at pagpapanatili ng code. At tila walang kakaiba sa pagsasalin, kung hindi para sa sukat ng base ng code at sa mga partikular na tampok ng aming code.

Para sa mga hindi nakakaalam, ang 1C:Enterprise ay isang kapaligiran para sa mabilis na pag-unlad ng mga cross-platform na application ng negosyo at runtime para sa kanilang pagpapatupad sa iba't ibang OS at DBMS. Sa pangkalahatan, ang produkto ay naglalaman ng:

Sinusubukan naming isulat ang parehong code para sa iba't ibang mga operating system hangga't maaari - ang server code base ay 99% karaniwan, ang client code base ay tungkol sa 95%. Ang 1C:Enterprise technology platform ay pangunahing nakasulat sa C++ at ang tinatayang mga katangian ng code ay ibinigay sa ibaba:

  • 10 milyong linya ng C++ code,
  • 14 libong mga file,
  • 60 libong mga klase,
  • kalahating milyong pamamaraan.

At lahat ng bagay na ito ay kailangang isalin sa C++14. Ngayon ay sasabihin namin sa iyo kung paano namin ito ginawa at kung ano ang aming naranasan sa proseso.

Paano namin isinalin ang 10 milyong linya ng C++ code sa pamantayang C++14 (at pagkatapos ay sa C++17)

Disclaimer

Lahat ng nakasulat sa ibaba tungkol sa mabagal/mabilis na trabaho, (hindi) malaking pagkonsumo ng memory sa pamamagitan ng mga pagpapatupad ng mga karaniwang klase sa iba't ibang mga aklatan ay nangangahulugan ng isang bagay: ito ay totoo PARA SA ATIN. Posible na ang mga karaniwang pagpapatupad ay pinakaangkop para sa iyong mga gawain. Nagsimula kami sa sarili naming mga gawain: kumuha kami ng data na karaniwan para sa aming mga kliyente, nagpatakbo ng mga tipikal na senaryo sa kanila, tumingin sa pagganap, dami ng memory na natupok, atbp., at sinuri kung kami at ang aming mga kliyente ay nasiyahan sa mga naturang resulta o hindi. . At kumilos sila depende sa.

Kung ano ang mayroon kami

Sa una, isinulat namin ang code para sa 1C:Enterprise 8 platform sa Microsoft Visual Studio. Nagsimula ang proyekto noong unang bahagi ng 2000s at mayroon kaming Windows-only na bersyon. Naturally, mula noon ang code ay aktibong binuo, maraming mga mekanismo ang ganap na muling naisulat. Ngunit ang code ay isinulat ayon sa pamantayan ng 1998, at, halimbawa, ang aming mga kanang anggulo na bracket ay pinaghihiwalay ng mga puwang upang ang pagsasama-sama ay magtagumpay, tulad nito:

vector<vector<int> > IntV;

Noong 2006, sa paglabas ng platform version 8.1, sinimulan naming suportahan ang Linux at lumipat sa isang third-party na karaniwang library STLPort. Ang isa sa mga dahilan para sa paglipat ay upang gumana sa malalawak na linya. Sa aming code, ginagamit namin ang std::wstring, na batay sa uri ng wchar_t, sa kabuuan. Ang laki nito sa Windows ay 2 bytes, at sa Linux ang default ay 4 bytes. Nagdulot ito ng hindi pagkakatugma ng aming mga binary na protocol sa pagitan ng kliyente at server, pati na rin ang iba't ibang patuloy na data. Gamit ang mga pagpipilian sa gcc, maaari mong tukuyin na ang laki ng wchar_t sa panahon ng compilation ay 2 bytes din, ngunit pagkatapos ay maaari mong kalimutan ang tungkol sa paggamit ng karaniwang library mula sa compiler, dahil gumagamit ito ng glibc, na kung saan ay pinagsama-sama para sa isang 4-byte na wchar_t. Ang iba pang mga dahilan ay mas mahusay na pagpapatupad ng mga karaniwang klase, suporta para sa mga hash table, at maging ang pagtulad sa mga semantika ng paglipat sa loob ng mga container, na aktibong ginagamit namin. At isa pang dahilan, gaya ng huling sinabi nila, ay ang pagganap ng string. Nagkaroon kami ng sarili naming klase para sa mga string, dahil... Dahil sa mga detalye ng aming software, ang mga pagpapatakbo ng string ay ginagamit nang napakalawak at para sa amin ito ay kritikal.

Ang aming string ay batay sa mga ideya sa pag-optimize ng string na ipinahayag noong unang bahagi ng 2000s Andrei Alexandrescu. Nang maglaon, nang magtrabaho si Alexandrescu sa Facebook, sa kanyang mungkahi, isang linya ang ginamit sa makina ng Facebook na gumagana sa katulad na mga prinsipyo (tingnan ang library kabaliwan).

Gumamit ang aming linya ng dalawang pangunahing teknolohiya sa pag-optimize:

  1. Para sa mga maiikling halaga, ginagamit ang isang panloob na buffer sa string object (hindi nangangailangan ng karagdagang paglalaan ng memorya).
  2. Para sa lahat ng iba pa, ginagamit ang mekanika Kopyahin Sa Isulat. Ang string na halaga ay naka-imbak sa isang lugar, at isang reference counter ay ginagamit sa panahon ng pagtatalaga/pagbabago.

Para mapabilis ang pag-compile ng platform, hindi namin isinama ang pagpapatupad ng stream mula sa aming variant ng STLPort (na hindi namin ginamit), nagbigay ito sa amin ng humigit-kumulang 20% ​​na mas mabilis na compilation. Sa dakong huli kailangan naming gumawa ng limitadong paggamit Magbunsod. Ginagamit ng Boost ang stream, lalo na sa mga service API nito (halimbawa, para sa pag-log), kaya kinailangan naming baguhin ito para alisin ang paggamit ng stream. Dahil dito, naging mahirap para sa amin na lumipat sa mga bagong bersyon ng Boost.

Pangatlong paraan

Kapag lumipat sa pamantayang C++14, isinasaalang-alang namin ang mga sumusunod na opsyon:

  1. I-upgrade ang STLPort na binago namin sa pamantayang C++14. Ang pagpipilian ay napakahirap, dahil... ang suporta para sa STLPort ay hindi na ipinagpatuloy noong 2010, at kailangan naming bumuo ng lahat ng code nito sa aming sarili.
  2. Ang paglipat sa isa pang pagpapatupad ng STL na katugma sa C++14. Lubhang kanais-nais na ang pagpapatupad na ito ay para sa Windows at Linux.
  3. Kapag nag-compile para sa bawat OS, gamitin ang library na nakapaloob sa kaukulang compiler.

Ang unang opsyon ay tahasang tinanggihan dahil sa sobrang trabaho.

Naisip namin ang tungkol sa pangalawang opsyon sa loob ng ilang panahon; itinuturing na isang kandidato libc++, ngunit sa oras na iyon ay hindi ito gumana sa ilalim ng Windows. Upang mai-port ang libc++ sa Windows, kailangan mong gumawa ng maraming trabaho - halimbawa, isulat ang lahat ng iyong sarili na may kinalaman sa mga thread, pag-synchronize ng thread at atomicity, dahil ginagamit ang libc++ sa mga lugar na ito POSIX API.

At pinili namin ang ikatlong paraan.

Transition

Kaya, kinailangan naming palitan ang paggamit ng STLPort ng mga library ng kaukulang compiler (Visual Studio 2015 para sa Windows, gcc 7 para sa Linux, clang 8 para sa macOS).

Sa kabutihang palad, ang aming code ay isinulat pangunahin ayon sa mga alituntunin at hindi gumamit ng lahat ng uri ng matalinong mga trick, kaya ang paglipat sa mga bagong aklatan ay natuloy nang medyo maayos, sa tulong ng mga script na pumalit sa mga pangalan ng mga uri, klase, namespace at kasama sa pinagmulan. mga file. Naapektuhan ng paglipat ang 10 source file (sa 000). Ang wchar_t ay pinalitan ng char14_t; nagpasya kaming iwanan ang paggamit ng wchar_t, dahil Ang char000_t ay tumatagal ng 16 byte sa lahat ng OS at hindi sinisira ang code compatibility sa pagitan ng Windows at Linux.

Nagkaroon ng ilang maliliit na pakikipagsapalaran. Halimbawa, sa STLPort ang isang iterator ay maaaring implicit na i-cast sa isang pointer sa isang elemento, at sa ilang mga lugar sa aming code ito ay ginamit. Sa mga bagong aklatan ay hindi na posible na gawin ito, at ang mga talatang ito ay kailangang suriin at muling isulat nang manu-mano.

Kaya, ang paglipat ng code ay kumpleto, ang code ay pinagsama-sama para sa lahat ng mga operating system. Oras na para sa mga pagsubok.

Ang mga pagsubok pagkatapos ng paglipat ay nagpakita ng pagbaba sa pagganap (sa ilang mga lugar hanggang 20-30%) at isang pagtaas sa pagkonsumo ng memorya (hanggang sa 10-15%) kumpara sa lumang bersyon ng code. Ito ay, sa partikular, dahil sa suboptimal na pagganap ng mga karaniwang string. Samakatuwid, muli naming kinailangan na gumamit ng sarili naming, bahagyang binagong linya.

Ang isang kawili-wiling tampok ng pagpapatupad ng mga lalagyan sa mga naka-embed na aklatan ay inihayag din: walang laman (walang mga elemento) std::map at std::set mula sa mga built-in na aklatan ay naglalaan ng memorya. At dahil sa mga tampok ng pagpapatupad, sa ilang mga lugar sa code ay napakaraming walang laman na mga lalagyan ng ganitong uri ang nilikha. Ang mga karaniwang lalagyan ng memorya ay inilalaan ng kaunti, para sa isang elemento ng ugat, ngunit para sa amin ito ay naging kritikal - sa isang bilang ng mga senaryo, ang aming pagganap ay bumaba nang malaki at ang pagkonsumo ng memorya ay tumaas (kumpara sa STLPort). Samakatuwid, sa aming code, pinalitan namin ang dalawang uri ng mga lalagyan na ito mula sa mga built-in na aklatan sa kanilang pagpapatupad mula sa Boost, kung saan ang mga lalagyan na ito ay walang tampok na ito, at nalutas nito ang problema sa pagbagal at pagtaas ng pagkonsumo ng memorya.

Tulad ng madalas na nangyayari pagkatapos ng malalaking pagbabago sa malalaking proyekto, ang unang pag-ulit ng source code ay hindi gumana nang walang mga problema, at dito, lalo na, ang suporta para sa pag-debug ng mga iterator sa pagpapatupad ng Windows ay madaling gamitin. Hakbang-hakbang kaming sumulong, at noong tagsibol ng 2017 (bersyon 8.3.11 1C:Enterprise) natapos ang paglipat.

Mga resulta ng

Ang paglipat sa pamantayan ng C++14 ay tumagal nang humigit-kumulang 6 na buwan. Kadalasan, isang (ngunit napakataas na kwalipikado) na developer ang nagtrabaho sa proyekto, at sa huling yugto ay kinatawan ng mga koponan na responsable para sa mga partikular na lugar na sinalihan - UI, server cluster, development at administration tools, atbp.

Ang paglipat ay lubos na pinasimple ang aming trabaho sa paglipat sa pinakabagong mga bersyon ng pamantayan. Kaya, ang bersyon 1C:Enterprise 8.3.14 (in development, release na naka-iskedyul para sa unang bahagi ng susunod na taon) ay nailipat na sa standard C++17.

Pagkatapos ng paglipat, mas maraming opsyon ang mga developer. Kung kanina ay mayroon kaming sariling binagong bersyon ng STL at isang std namespace, ngayon ay mayroon na kaming mga karaniwang klase mula sa mga built-in na compiler library sa std namespace, sa stdx namespace - ang aming mga linya at container na na-optimize para sa aming mga gawain, sa boost - ang pinakabagong bersyon ng boost. At ginagamit ng developer ang mga klaseng iyon na pinakaangkop upang malutas ang kanyang mga problema.

Ang "katutubong" pagpapatupad ng mga move constructor ay nakakatulong din sa pag-unlad (ilipat ang mga konstruktor) para sa isang bilang ng mga klase. Kung ang isang klase ay may move constructor at ang klase na ito ay inilagay sa isang lalagyan, pagkatapos ay ino-optimize ng STL ang pagkopya ng mga elemento sa loob ng lalagyan (halimbawa, kapag ang lalagyan ay pinalawak at ito ay kinakailangan upang baguhin ang kapasidad at muling italaga ang memorya).

Lumipad sa ang ungguwento

Marahil ang pinaka-hindi kasiya-siya (ngunit hindi kritikal) na kahihinatnan ng paglipat ay nahaharap tayo sa pagtaas ng dami obj file, at ang buong resulta ng build kasama ang lahat ng mga intermediate na file ay nagsimulang umabot ng 60–70 GB. Ang pag-uugali na ito ay dahil sa mga kakaibang uri ng modernong karaniwang mga aklatan, na naging hindi gaanong kritikal sa laki ng mga nabuong file ng serbisyo. Hindi ito nakakaapekto sa pagpapatakbo ng pinagsama-samang aplikasyon, ngunit nagdudulot ito ng ilang mga abala sa pag-unlad, lalo na, pinatataas nito ang oras ng compilation. Ang mga kinakailangan para sa libreng puwang sa disk sa mga build server at sa mga developer machine ay tumataas din. Gumagana ang aming mga developer sa ilang mga bersyon ng platform nang magkatulad, at daan-daang gigabytes ng mga intermediate na file kung minsan ay nagdudulot ng mga kahirapan sa kanilang trabaho. Ang problema ay hindi kasiya-siya, ngunit hindi kritikal; ipinagpaliban namin ang solusyon nito sa ngayon. Isinasaalang-alang namin ang teknolohiya bilang isa sa mga opsyon para sa paglutas nito pagbuo ng pagkakaisa (sa partikular, ginagamit ito ng Google kapag binubuo ang Chrome browser).

Pinagmulan: www.habr.com

Magdagdag ng komento