10 milyon sətir C++ kodunu C++14 standartına necə tərcümə etdik (sonra C++17)

Bir müddət əvvəl (2016-cı ilin payızında) 1C: Enterprise texnologiya platformasının növbəti versiyasının hazırlanması zamanı inkişaf qrupunda yeni standartı dəstəkləmək barədə sual yarandı. C ++ 14 kodumuzda. Yeni standarta keçid, güman etdiyimiz kimi, bir çox şeyi daha qəşəng, sadə və etibarlı şəkildə yazmağa imkan verəcək və kodun dəstəklənməsini və saxlanmasını sadələşdirəcək. Və kod bazasının miqyası və kodumuzun spesifik xüsusiyyətləri olmasa, tərcümədə qeyri-adi heç nə yoxdur.

Bilməyənlər üçün deyim ki, 1C: Enterprise platformalararası biznes proqramlarının sürətli inkişafı və onların müxtəlif ƏS və DBMS-lərdə icrası üçün icra müddəti üçün mühitdir. Ümumiyyətlə, məhsulun tərkibinə aşağıdakılar daxildir:

Müxtəlif əməliyyat sistemləri üçün mümkün qədər eyni kodu yazmağa çalışırıq - server kod bazası 99% ümumi, müştəri kod bazası təxminən 95% təşkil edir. 1C: Enterprise texnologiya platforması əsasən C++ dilində yazılmışdır və təxmini kod xüsusiyyətləri aşağıda verilmişdir:

  • 10 milyon sətir C++ kodu,
  • 14 min fayl,
  • 60 min sinif,
  • yarım milyon üsul.

Və bütün bunlar C++14 dilinə tərcümə edilməli idi. Bu gün sizə bunu necə etdiyimizi və prosesdə nələrlə qarşılaşdığımızı izah edəcəyik.

10 milyon sətir C++ kodunu C++14 standartına necə tərcümə etdik (sonra C++17)

İmtina

Müxtəlif kitabxanalarda standart siniflərin tətbiqi ilə yavaş/sürətli iş, (yox) böyük yaddaş sərfiyyatı haqqında aşağıda yazılan hər şey bir şeyi ifadə edir: bu, BİZƏ doğrudur. Tamamilə mümkündür ki, standart tətbiqlər tapşırıqlarınız üçün ən uyğun olacaq. Biz öz vəzifələrimizdən başladıq: müştərilərimiz üçün xarakterik olan məlumatları götürdük, onlar üzrə tipik ssenariləri icra etdik, performansa, istehlak edilən yaddaşın miqdarına və s. baxdıq və bizim və müştərilərimizin bu cür nəticələrdən razı olub-olmadığını təhlil etdik. . Və asılı olaraq hərəkət etdilər.

Nəyimiz var idi

Əvvəlcə Microsoft Visual Studio istifadə edərək 1C: Enterprise 8 platformasının kodunu yazdıq. Layihə 2000-ci illərin əvvəllərində başladı və bizdə yalnız Windows versiyası var idi. Təbii ki, o vaxtdan bəri kod aktiv şəkildə inkişaf etdirildi, bir çox mexanizmlər tamamilə yenidən yazılmışdır. Lakin kod 1998 standartına uyğun olaraq yazılmışdı və məsələn, düzgün bucaqlı mötərizələrimiz boşluqlarla ayrıldı ki, kompilyasiya uğurlu olsun, məsələn:

vector<vector<int> > IntV;

2006-cı ildə 8.1 platforma versiyasının buraxılması ilə biz Linux-u dəstəkləməyə başladıq və üçüncü tərəf standart kitabxanasına keçdik. STLPort. Keçidin səbəblərindən biri geniş xətlərlə işləmək idi. Kodumuzda wchar_t tipinə əsaslanan std::wstring-dən istifadə edirik. Windows-da onun ölçüsü 2 baytdır, Linux-da isə standart olaraq 4 baytdır. Bu, müştəri və server arasında binar protokollarımızın uyğunsuzluğuna, eləcə də müxtəlif davamlı məlumatlara səbəb oldu. Gcc seçimlərindən istifadə edərək tərtib zamanı wchar_t ölçüsünün də 2 bayt olduğunu təyin edə bilərsiniz, lakin sonra kompilyatordan standart kitabxanadan istifadə etməyi unuda bilərsiniz, çünki glibc-dən istifadə edir, bu da öz növbəsində 4 baytlıq wchar_t üçün tərtib edilir. Digər səbəblər standart siniflərin daha yaxşı tətbiqi, hash cədvəllərinin dəstəklənməsi və hətta aktiv istifadə etdiyimiz konteynerlərin içərisində hərəkət semantikasının emulyasiyası idi. Və daha bir səbəb, necə deyərlər, sonuncu, lakin ən azı, simli performans idi. Bizim simlər üçün öz sinifimiz var idi, çünki... Proqram təminatımızın xüsusiyyətlərinə görə sətir əməliyyatları çox geniş istifadə olunur və bu bizim üçün çox vacibdir.

Bizim sətirimiz 2000-ci illərin əvvəllərində ifadə edilmiş simli optimallaşdırma ideyalarına əsaslanır Andrey Aleksandresku. Sonralar, Aleksandresku Facebook-da işləyərkən, onun təklifi ilə, Facebook mühərrikində oxşar prinsiplər üzərində işləyən bir xətt istifadə edildi (bax: kitabxana axmaqlıq).

Xəttimiz iki əsas optimallaşdırma texnologiyasından istifadə etdi:

  1. Qısa dəyərlər üçün sətir obyektinin özündə daxili bufer istifadə olunur (əlavə yaddaş ayrılması tələb olunmur).
  2. Bütün digərləri üçün mexanika istifadə olunur Kopyala Yaz. Sətir dəyəri bir yerdə saxlanılır və təyinat/dəyişiklik zamanı istinad sayğacı istifadə olunur.

Platformanın tərtibini sürətləndirmək üçün biz STLPort variantımızdan axın tətbiqini istisna etdik (istifadə etmədik), bu, bizə təxminən 20% daha sürətli kompilyasiya verdi. Sonradan biz məhdud istifadə etməli olduq Kömək. Boost, xüsusən də xidmət API-lərində (məsələn, giriş üçün) axından çox istifadə edir, ona görə də axın istifadəsini aradan qaldırmaq üçün onu dəyişdirməli olduq. Bu da öz növbəsində Boost-un yeni versiyalarına keçməmizi çətinləşdirdi.

Üçüncü yol

C++ 14 standartına keçərkən aşağıdakı variantları nəzərdən keçirdik:

  1. Dəyişdirdiyimiz STLPort-u C++ 14 standartına təkmilləşdirin. Seçim çox çətindir, çünki... STLPort dəstəyi 2010-cu ildə dayandırıldı və biz onun bütün kodunu özümüz yaratmalı olacaqdıq.
  2. C++ 14 ilə uyğun gələn başqa STL tətbiqinə keçid. Bu tətbiqin Windows və Linux üçün olması çox arzu edilir.
  3. Hər bir OS üçün tərtib edərkən, müvafiq tərtibçiyə quraşdırılmış kitabxanadan istifadə edin.

Birinci variant həddən artıq iş olduğuna görə tamamilə rədd edildi.

Bir müddət ikinci variantı düşündük; namizəd hesab edilir libc++, lakin o zaman Windows altında işləmirdi. libc++-ı Windows-a köçürmək üçün çox iş görməli olacaqsınız - məsələn, iplər, ip sinxronizasiyası və atomiklik ilə əlaqəli hər şeyi özünüz yazın, çünki bu sahələrdə libc++ istifadə olunur. POSIX API.

Biz isə üçüncü yolu seçdik.

Keçid

Beləliklə, STLPort istifadəsini müvafiq tərtibçilərin kitabxanaları ilə əvəz etməli olduq (Windows üçün Visual Studio 2015, Linux üçün gcc 7, macOS üçün clang 8).

Xoşbəxtlikdən, kodumuz əsasən təlimatlara uyğun yazılmışdır və hər cür ağıllı hiylələrdən istifadə etməmişdir, buna görə növlərin, siniflərin, ad boşluqlarının adlarını əvəz edən və mənbəyə daxil olan skriptlərin köməyi ilə yeni kitabxanalara keçid nisbətən rəvan davam etdi. fayllar. Miqrasiya 10 mənbə faylına (000-dən) təsir etdi. wchar_t char14_t ilə əvəz olundu; wchar_t istifadəsindən imtina etmək qərarına gəldik, çünki char000_t bütün əməliyyat sistemlərində 16 bayt alır və Windows və Linux arasında kod uyğunluğunu pozmur.

Kiçik sərgüzəştlər oldu. Məsələn, STLPort-da iterator dolayısı ilə elementin göstəricisinə ötürülə bilərdi və kodumuzda bəzi yerlərdə bundan istifadə edilmişdir. Yeni kitabxanalarda bunu etmək artıq mümkün deyildi və bu keçidlər əl ilə təhlil edilməli və yenidən yazılmalı idi.

Beləliklə, kodun miqrasiyası tamamlandı, kod bütün əməliyyat sistemləri üçün tərtib edildi. Testlərin vaxtıdır.

Keçiddən sonrakı testlər kodun köhnə versiyası ilə müqayisədə performansın aşağı düşməsini (bəzi yerlərdə 20-30%-ə qədər) və yaddaş istehlakının (10-15%-ə qədər) artdığını göstərib. Bu, xüsusilə, standart simlərin optimal olmayan performansı ilə əlaqədar idi. Ona görə də biz yenə öz, bir qədər dəyişdirilmiş xəttimizdən istifadə etməli olduq.

Daxil edilmiş kitabxanalarda konteynerlərin həyata keçirilməsinin maraqlı bir xüsusiyyəti də aşkar edilmişdir: boş (elementlər olmadan) daxili kitabxanalardan std::map və std::set yaddaş ayırır. Tətbiq xüsusiyyətlərinə görə kodun bəzi yerlərində bu tip kifayət qədər çox boş konteynerlər yaradılır. Standart yaddaş konteynerləri bir kök element üçün bir az ayrılır, lakin bizim üçün bu kritik oldu - bir sıra ssenarilərdə performansımız əhəmiyyətli dərəcədə azaldı və yaddaş istehlakı artdı (STLPort ilə müqayisədə). Buna görə kodumuzda daxili kitabxanalardan bu iki növ konteynerləri Boost-dan tətbiq etməklə əvəz etdik, burada bu konteynerlərdə bu xüsusiyyət yox idi və bu, yavaşlama və yaddaş istehlakının artması problemini həll etdi.

Böyük layihələrdə genişmiqyaslı dəyişikliklərdən sonra tez-tez baş verdiyi kimi, mənbə kodunun ilk iterasiyası problemsiz işləmədi və burada, xüsusən də Windows tətbiqində iteratorların sazlanması üçün dəstək lazımlı oldu. Addım-addım irəlilədik və 2017-ci ilin yazına qədər (versiya 8.3.11 1C: Enterprise) miqrasiya başa çatdı.

Nəticələri

C++ 14 standartına keçid bizə təxminən 6 ay çəkdi. Çox vaxt layihə üzərində bir (lakin çox yüksək ixtisaslı) tərtibatçı işləyirdi və son mərhələdə xüsusi sahələrə cavabdeh olan komandaların nümayəndələri - UI, server klasteri, inkişaf və idarəetmə alətləri və s.

Keçid standartın ən son versiyalarına keçidlə bağlı işimizi xeyli sadələşdirdi. Beləliklə, 1C: Enterprise 8.3.14 versiyası (inkişaf mərhələsində, buraxılışı gələn ilin əvvəlinə planlaşdırılır) artıq standarta köçürülüb. C++ 17.

Miqrasiyadan sonra tərtibatçıların daha çox seçimləri var. Əgər əvvəllər bizim STL-nin öz dəyişdirilmiş versiyası və bir std ad sahəsi var idisə, indi std ad məkanında, stdx ad məkanında quraşdırılmış kompilyator kitabxanalarından standart siniflərimiz var - tapşırıqlarımız üçün optimallaşdırılmış xətlərimiz və konteynerlərimiz, gücləndirilmiş şəkildə - Boost son versiyası. Və tərtibatçı problemlərini həll etmək üçün optimal şəkildə uyğun gələn siniflərdən istifadə edir.

Hərəkət konstruktorlarının “doğma” tətbiqi də inkişafa kömək edir (konstruktorları hərəkət etdirin) bir sıra siniflər üçün. Əgər sinfin hərəkət konstruktoru varsa və bu sinif konteynerə yerləşdirilibsə, onda STL konteyner daxilində elementlərin surətinin çıxarılmasını optimallaşdırır (məsələn, konteyner genişləndirildikdə və tutumun dəyişdirilməsi və yaddaşın yenidən bölüşdürülməsi lazım olduqda).

Ki, Məlhəm uçmaq

Miqrasiyanın bəlkə də ən xoşagəlməz (lakin kritik olmayan) nəticəsi odur ki, biz həcmin artması ilə üzləşirik. obj faylları, və bütün aralıq fayllarla qurulmanın tam nəticəsi 60-70 GB tutmağa başladı. Bu davranış yaradılan xidmət fayllarının ölçüsünə daha az tənqidi yanaşan müasir standart kitabxanaların xüsusiyyətləri ilə əlaqədardır. Bu, tərtib edilmiş tətbiqin işinə təsir göstərmir, lakin inkişafda bir sıra narahatlıqlara səbəb olur, xüsusən də tərtib müddətini artırır. Quraşdırma serverlərində və developer maşınlarında boş disk sahəsinə tələblər də artır. Tərtibatçılarımız paralel olaraq platformanın bir neçə versiyası üzərində işləyirlər və yüzlərlə giqabayt aralıq fayl bəzən onların işində çətinliklər yaradır. Problem xoşagəlməzdir, lakin kritik deyil, onun həllini hələlik təxirə salmışıq. Biz texnologiyanı onun həlli variantlarından biri kimi nəzərdən keçiririk birlik qurmaq (xüsusilə, Google Chrome brauzerini inkişaf etdirərkən ondan istifadə edir).

Mənbə: www.habr.com

Добавить комментарий