Ինչպես մենք թարգմանեցինք C++ կոդի 10 միլիոն տող դեպի C++14 ստանդարտ (և այնուհետև C++17)

Որոշ ժամանակ առաջ (2016 թվականի աշնանը), 1C:Enterprise տեխնոլոգիական հարթակի հաջորդ տարբերակի մշակման ժամանակ, մշակողների թիմում հարց ծագեց նոր ստանդարտին աջակցելու մասին: C ++ 14 մեր օրենսգրքում։ Անցումը նոր ստանդարտի, ինչպես մենք ենթադրում էինք, թույլ կտա մեզ գրել շատ բաներ ավելի նրբագեղ, պարզ և հուսալի, և կհեշտացներ կոդի աջակցությունն ու պահպանումը: Իսկ թարգմանության մեջ արտառոց բան կարծես թե չկա, եթե չասենք կոդերի բազայի մասշտաբները և մեր կոդի յուրահատկությունները։

Նրանց համար, ովքեր չգիտեն, 1C:Enterprise-ը միջպլատֆորմային բիզնես հավելվածների արագ զարգացման միջավայր է և տարբեր ՕՀ-ներում և DBMS-ներում դրանց կատարման համար: Ընդհանուր առմամբ, արտադրանքը պարունակում է.

Մենք փորձում ենք հնարավորինս նույն կոդը գրել տարբեր օպերացիոն համակարգերի համար՝ սերվերի կոդերի բազան 99%-ով տարածված է, հաճախորդի կոդը՝ մոտ 95%: 1C:Enterprise տեխնոլոգիական հարթակը հիմնականում գրված է C++-ով, իսկ կոդի մոտավոր բնութագրերը տրված են ստորև.

  • 10 միլիոն տող C++ կոդը,
  • 14 հազար ֆայլ,
  • 60 հազար դասարան,
  • կես միլիոն մեթոդներ.

Եվ այս ամենը պետք է թարգմանվեր C++14: Այսօր մենք ձեզ կպատմենք, թե ինչպես մենք դա արեցինք և ինչի հանդիպեցինք ընթացքում:

Ինչպես մենք թարգմանեցինք C++ կոդի 10 միլիոն տող դեպի C++14 ստանդարտ (և այնուհետև C++17)

Հրաժարում պատասխանատվությունից

Ստորև գրված ամեն ինչ դանդաղ/արագ աշխատանքի, (ոչ) մեծ հիշողության սպառման մասին տարբեր գրադարաններում ստանդարտ դասերի ներդրման միջոցով նշանակում է մեկ բան. սա ճիշտ է ՄԵԶ ՀԱՄԱՐ: Միանգամայն հնարավոր է, որ ստանդարտ իրականացումները լավագույնս համապատասխանեն ձեր առաջադրանքներին: Մենք սկսեցինք մեր սեփական առաջադրանքներից. վերցրեցինք տվյալներ, որոնք բնորոշ էին մեր հաճախորդներին, գործարկեցինք նրանց վրա բնորոշ սցենարներ, նայեցինք կատարողականին, սպառված հիշողության քանակին և այլն, և վերլուծեցինք՝ արդյոք մենք և մեր հաճախորդները գոհ են նման արդյունքներից, թե ոչ: . Եվ նրանք գործել են կախված.

Այն, ինչ ունեինք

Սկզբում մենք գրել էինք կոդը 1C:Enterprise 8 պլատֆորմի համար Microsoft Visual Studio-ում: Նախագիծը սկսվեց 2000-ականների սկզբին, և մենք ունեինք միայն Windows-ի տարբերակ: Բնականաբար, դրանից հետո օրենսգիրքն ակտիվորեն մշակվել է, բազմաթիվ մեխանիզմներ ամբողջությամբ վերաշարադրվել են։ Բայց կոդը գրվել է 1998 թվականի ստանդարտի համաձայն, և, օրինակ, մեր աջ անկյունային փակագծերը բաժանվել են բացատներով, որպեսզի կոմպիլյացիան հաջողվի, այսպես.

vector<vector<int> > IntV;

2006 թվականին, պլատֆորմի 8.1 տարբերակի թողարկմամբ, մենք սկսեցինք աջակցել Linux-ին և անցանք երրորդ կողմի ստանդարտ գրադարանի։ STLPort. Անցման պատճառներից մեկն էլ լայն գծերով աշխատելն էր։ Մեր կոդում մենք օգտագործում ենք std::wstring, որը հիմնված է wchar_t տեսակի վրա, ամբողջ ընթացքում: Windows-ում դրա չափը 2 բայթ է, իսկ Linux-ում լռելյայնը 4 բայթ է։ Սա հանգեցրեց հաճախորդի և սերվերի միջև մեր երկուական արձանագրությունների անհամատեղելիությանը, ինչպես նաև տարբեր մշտական ​​տվյալների: Օգտագործելով gcc տարբերակները, կարող եք նշել, որ wchar_t-ի չափը կոմպիլյացիայի ժամանակ նույնպես 2 բայթ է, բայց հետո կարող եք մոռանալ կոմպիլյատորից ստանդարտ գրադարան օգտագործելու մասին, քանի որ այն օգտագործում է glibc, որն իր հերթին կազմվում է 4 բայթ wchar_t-ի համար: Մյուս պատճառներն էին ստանդարտ դասերի ավելի լավ իրականացումը, հեշ աղյուսակների աջակցությունը և նույնիսկ կոնտեյներների ներսում տեղաշարժվելու իմաստաբանության նմանակումը, որոնք մենք ակտիվորեն օգտագործում էինք: Եվ ևս մեկ պատճառ, ինչպես ասում են վերջին, բայց ոչ պակաս կարևորը, լարային կատարումն էր: Մենք ունեինք լարերի մեր դասարանը, քանի որ... Մեր ծրագրաշարի առանձնահատկությունների պատճառով լարային գործողությունները շատ լայնորեն օգտագործվում են, և մեզ համար դա կարևոր է:

Մեր տողը հիմնված է լարերի օպտիմալացման գաղափարների վրա, որոնք արտահայտվել են դեռևս 2000-ականների սկզբին Անդրեյ Ալեքսանդրեսկու. Ավելի ուշ, երբ Ալեքսանդրեսկուն աշխատում էր Facebook-ում, նրա առաջարկով Facebook-ի շարժիչում կիրառվեց մի տող, որն աշխատում էր նմանատիպ սկզբունքներով (տես գրադարան անհեթեթություն).

Մեր գիծն օգտագործում էր երկու հիմնական օպտիմալացման տեխնոլոգիա.

  1. Կարճ արժեքների համար օգտագործվում է լարային օբյեկտի ներքին բուֆեր (չի պահանջում լրացուցիչ հիշողության բաշխում):
  2. Մնացած բոլորի համար օգտագործվում է մեխանիկա Պատճենել Գրելու վրա. Լարի արժեքը պահվում է մեկ տեղում, և նշանակման/փոփոխման ժամանակ օգտագործվում է հղման հաշվիչը:

Պլատֆորմի հավաքումն արագացնելու համար մենք բացառեցինք հոսքի իրականացումը մեր STLPort տարբերակից (որը մենք չօգտագործեցինք), դա մեզ մոտ 20%-ով ավելի արագ կազմեց: Հետագայում մենք ստիպված էինք սահմանափակ օգտագործել Բարձրացնել. Boost-ը մեծապես օգտագործում է հոսքը, հատկապես իր սպասարկման API-ներում (օրինակ՝ գրանցման համար), ուստի մենք ստիպված էինք այն փոփոխել հոսքի օգտագործումը հեռացնելու համար: Սա, իր հերթին, դժվարացրեց մեզ տեղափոխել Boost-ի նոր տարբերակներ:

Երրորդ ճանապարհ

C++14 ստանդարտին անցնելիս մենք դիտարկել ենք հետևյալ տարբերակները.

  1. Թարմացրեք STLPort-ը, որը մենք փոփոխել ենք C++14 ստանդարտին: Տարբերակը շատ բարդ է, քանի որ... STLPort-ի աջակցությունը դադարեցվել է 2010 թվականին, և մենք պետք է ինքներս ստեղծեինք դրա ամբողջ ծածկագիրը:
  2. Անցում դեպի մեկ այլ STL ներդրում, որը համատեղելի է C++14-ի հետ: Շատ ցանկալի է, որ այս իրականացումը լինի Windows-ի և Linux-ի համար:
  3. Յուրաքանչյուր ՕՀ-ի համար կոմպիլյացիա կատարելիս օգտագործեք համապատասխան կոմպիլյատորում ներկառուցված գրադարանը:

Առաջին տարբերակը ուղղակիորեն մերժվել է չափազանց շատ աշխատանքի պատճառով։

Մենք որոշ ժամանակ մտածում էինք երկրորդ տարբերակի մասին. համարվում է թեկնածու libc++, բայց այն ժամանակ Windows-ի տակ այն չէր աշխատում։ libc++-ը Windows-ում տեղափոխելու համար դուք պետք է շատ աշխատանք կատարեք, օրինակ՝ ինքներդ գրեք այն ամենը, ինչ կապված է թելերի, թելի համաժամացման և ատոմականության հետ, քանի որ libc++-ն օգտագործվում է այս տարածքներում։ POSIX API.

Եվ մենք ընտրեցինք երրորդ ճանապարհը.

Անցումը

Այսպիսով, մենք ստիպված էինք փոխարինել STLPort-ի օգտագործումը համապատասխան կոմպիլյատորների գրադարաններով (Visual Studio 2015 Windows-ի համար, gcc 7 Linux-ի համար, clang 8-ը macOS-ի համար):

Բարեբախտաբար, մեր ծածկագիրը գրվել է հիմնականում ուղեցույցների համաձայն և չի օգտագործել բոլոր տեսակի խելացի հնարքներ, ուստի միգրացիան դեպի նոր գրադարաններ ընթացել է համեմատաբար սահուն՝ սկրիպտների օգնությամբ, որոնք փոխարինել են տեսակների, դասերի, անվանատարածքների անունները և ներառում են աղբյուրում: ֆայլեր։ Միգրացիան ազդել է 10 սկզբնաղբյուր ֆայլերի վրա (000-ից): wchar_t-ը փոխարինվեց char14_t-ով; մենք որոշեցինք հրաժարվել wchar_t-ի օգտագործումից, քանի որ char000_t-ը վերցնում է 16 բայթ բոլոր ՕՀ-ներում և չի փչացնում կոդերի համատեղելիությունը Windows-ի և Linux-ի միջև:

Եղան մի քանի փոքրիկ արկածներ։ Օրինակ, STLPort-ում կրկնվողը կարող էր անուղղակիորեն փոխանցվել տարրի ցուցիչին, և մեր կոդի որոշ տեղերում դա օգտագործվել է: Նոր գրադարաններում դա այլևս հնարավոր չէր անել, և այդ հատվածները պետք է վերլուծվեին և վերաշարադրվեին ձեռքով:

Այսպիսով, կոդի միգրացիան ավարտված է, կոդը կազմվում է բոլոր օպերացիոն համակարգերի համար։ Թեստերի ժամանակն է։

Անցումից հետո թեստերը ցույց են տվել կատարողականի անկում (որոշ տեղերում՝ մինչև 20-30%) և հիշողության սպառման աճ (մինչև 10-15%)՝ համեմատած կոդերի հին տարբերակի։ Սա, մասնավորապես, պայմանավորված էր ստանդարտ լարերի ոչ օպտիմալ կատարմամբ: Հետեւաբար, մենք կրկին ստիպված եղանք օգտագործել մեր սեփական, մի փոքր փոփոխված գիծը:

Բացահայտվել է նաև ներկառուցված գրադարաններում կոնտեյներների ներդրման հետաքրքիր առանձնահատկությունը՝ դատարկ (առանց տարրերի) std::map և std::set ներկառուցված գրադարաններից հատկացնում է հիշողություն: Իսկ ներդրման առանձնահատկությունների շնորհիվ կոդի որոշ տեղերում ստեղծվում են այս տեսակի բավականին դատարկ բեռնարկղեր։ Ստանդարտ հիշողության տարաները մի փոքր հատկացվում են, մեկ արմատային տարրի համար, բայց մեզ համար դա շատ կարևոր է. Հետևաբար, մեր կոդում մենք ներկառուցված գրադարաններից այս երկու տեսակի կոնտեյներները փոխարինեցինք Boost-ից, որտեղ այդ կոնտեյներները չունեին նման հատկություն, և դա լուծեց խնդիրը դանդաղեցման և հիշողության սպառման ավելացման հետ:

Ինչպես հաճախ է պատահում խոշոր նախագծերում լայնածավալ փոփոխություններից հետո, սկզբնական կոդի առաջին կրկնումը առանց խնդիրների չաշխատեց, և այստեղ, մասնավորապես, Windows-ի ներդրման մեջ կրկնող սարքերի վրիպազերծման աջակցությունը հարմար եկավ: Քայլ առ քայլ առաջ շարժվեցինք, և մինչև 2017 թվականի գարուն (տարբերակ 8.3.11 1C:Enterprise) միգրացիան ավարտվեց։

Արդյունքները

Անցումը C++14 ստանդարտին մեզ տևեց մոտ 6 ամիս: Ժամանակի մեծ մասը նախագծի վրա աշխատել է մեկ (բայց շատ բարձր որակավորում ունեցող) ծրագրավորող, իսկ վերջնական փուլում միացել են կոնկրետ ոլորտների համար պատասխանատու թիմերի ներկայացուցիչներ՝ UI, սերվերի կլաստեր, զարգացման և կառավարման գործիքներ և այլն:

Անցումը զգալիորեն պարզեցրեց ստանդարտի վերջին տարբերակներին անցնելու մեր աշխատանքը: Այսպիսով, 1C:Enterprise 8.3.14 տարբերակը (մշակման փուլում է, թողարկումը նախատեսված է հաջորդ տարվա սկզբին) արդեն փոխանցվել է ստանդարտին: C++17.

Միգրացիայից հետո մշակողները ավելի շատ տարբերակներ ունեն: Եթե ​​նախկինում մենք ունեինք STL-ի մեր սեփական փոփոխված տարբերակը և մեկ std անվանատարածք, ապա այժմ մենք ունենք ստանդարտ դասեր ներկառուցված կոմպիլյատորների գրադարաններից std անվանումների տարածքում, stdx անվանատարածքում՝ մեր տողերն ու կոնտեյներները՝ օպտիմիզացված մեր առաջադրանքների համար, իսկ boost-ում՝ boost-ի վերջին տարբերակը: Եվ մշակողը օգտագործում է այն դասերը, որոնք օպտիմալ կերպով հարմար են իր խնդիրները լուծելու համար:

Շարժվող կոնստրուկտորների «հայրենի» իրականացումը նույնպես օգնում է զարգացմանը (տեղափոխել կոնստրուկտորներ) մի շարք դասերի համար. Եթե ​​դասը ունի շարժման կոնստրուկտոր, և այս դասը տեղադրվում է կոնտեյների մեջ, ապա STL-ն օպտիմիզացնում է տարրերի պատճենումը կոնտեյների ներսում (օրինակ, երբ կոնտեյները ընդլայնվում է, և անհրաժեշտ է փոխել հզորությունը և վերաբաշխել հիշողությունը):

Fly է քսուք

Միգրացիայի ամենատհաճ (բայց ոչ կրիտիկական) հետևանքն այն է, որ մենք բախվում ենք ծավալների աճի. obj ֆայլեր, և կառուցման ամբողջական արդյունքը բոլոր միջանկյալ ֆայլերով սկսեց զբաղեցնել 60–70 ԳԲ։ Այս վարքագիծը պայմանավորված է ժամանակակից ստանդարտ գրադարանների առանձնահատկություններով, որոնք ավելի քիչ են քննադատում գեներացված սպասարկման ֆայլերի չափը: Սա չի ազդում կազմված հավելվածի աշխատանքի վրա, սակայն ստեղծում է մի շարք անհարմարություններ մշակման մեջ, մասնավորապես, ավելացնում է կոմպիլյացիայի ժամանակը։ Աճում են նաև build սերվերների և ծրագրավորող մեքենաների սկավառակի ազատ տարածության պահանջները: Մեր մշակողները զուգահեռաբար աշխատում են հարթակի մի քանի տարբերակների վրա, և հարյուրավոր գիգաբայթ միջանկյալ ֆայլեր երբեմն դժվարություններ են ստեղծում նրանց աշխատանքում։ Խնդիրը տհաճ է, բայց ոչ կրիտիկական, մենք առայժմ հետաձգել ենք դրա լուծումը։ Մենք տեխնոլոգիան դիտարկում ենք որպես դրա լուծման տարբերակներից մեկը միասնության ձևավորում (մասնավորապես, Google-ն օգտագործում է այն Chrome բրաուզերը մշակելիս):

Source: www.habr.com

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