Ինչո՞ւ է C++ (մ) առաջին հատկացումը միշտ 72 ԿԲ: | Mewayz Blog Skip to main content
Hacker News

Ինչո՞ւ է C++ (մ) առաջին հատկացումը միշտ 72 ԿԲ:

Մեկնաբանություններ

1 min read Via joelsiks.com

Mewayz Team

Editorial Team

Hacker News

Ձեր առաջին C++ հատկացման առեղծվածը

Դուք գրում եք պարզ C++ ծրագիր: Մեկ նոր ինտ: Չորս բայթ. Դուք բացում եք strace կամ ձեր նախընտրած հիշողության պրոֆիլը, և ահա այն. ձեր գործընթացը պարզապես պահանջել է մոտավորապես 72 ԿԲ օպերացիոն համակարգից: Ոչ 4 բայթ: Ոչ 64 բայթ: Ամբողջական 72 ԿԲ: Եթե ​​երբևէ նայել եք այդ թվին և մտածել եք, թե արդյոք ձեր գործիքավորումը խաբում է ձեզ, դուք միայնակ չեք: Այս տարօրինակ թվացող վարքագիծը C++-ի ծրագրավորողների մոտ, որոնք առաջին անգամ բացահայտում են հիշողության ներքին մասերը, ամենահաճախ տրվող հարցերից մեկն է, և պատասխանը մեզ տանում է հետաքրքիր ճանապարհորդության այն շերտերով, որոնք գտնվում են ձեր կոդի և իրական սարքաշարի միջև:

Ինչ է պատահում, երբ զանգում եք նոր

72 ԿԲ ցուցանիշը հասկանալու համար հարկավոր է հետևել բաշխման ամբողջական շղթան: Երբ ձեր C++ կոդը կատարում է new int, կոմպիլյատորը դա թարգմանում է որպես զանգ օպերատոր նոր, որը Linux համակարգերի մեծ մասում պատվիրակում է malloc-ին glibc-ից: Բայց mallocմիջուկից ուղղակիորեն չի խնդրում 4 բայթ հիշողություն: Միջուկը գործում է էջերում՝ սովորաբար 4 ԿԲ x86_64-ում, և համակարգային զանգի արժեքը հսկայական է հիշողության պարզ հասանելիության համեմատ: Յուրաքանչյուր առանձին տեղաբաշխման համար brk() կամ mmap() կանչելը կարող է դադարեցնել ցանկացած ոչ տրիվիալ ծրագիր:

Փոխարենը, glibc-ի հիշողության բաշխիչը, որը կոչվում է ptmalloc2, որն ինքնին առաջացել է Դագ Լիի դասական dlmalloc-ից, գործում է որպես միջնորդ: Այն նախօրոք պահանջում է հիշողության մեծ բլոկներ միջուկից, այնուհետև դրանք ավելի փոքր կտորների է փորում, քանի որ դրանք անհրաժեշտ են ձեր ծրագրին: Սա է այն հիմնական պատճառը, որ ձեր առաջին 4 բայթ հատկացումը գործառնական համակարգին շատ ավելի մեծ հարցում է առաջացնում: Հատկացողը վատնող չէ. Այն ռազմավարական է:

72 ԿԲ-ի մասնատում. ուր են գնում բայթերը

Նախնական տեղաբաշխման վերադիր ծախսերը գալիս են մի քանի տարբեր բաղադրիչներից, որոնք գործարկման ժամանակը պետք է սկզբնավորի նախքան ձեզ տրամադրի նույնիսկ մեկ բայթ օգտագործելի հիշողություն: Յուրաքանչյուր բաղադրիչ հասկանալը բացատրում է, թե ինչու է թիվը հասնում այնտեղ:

Նախ, glibc-ի malloc-ը սկզբնավորում է հիմնական ասպարեզը՝ հիմնական հաշվապահական կառուցվածքը, որը հետևում է հիմնական շղթայի բոլոր հատկացումներին: Այս ասպարեզը ներառում է մետատվյալներ կույտի համար, ազատ ցուցակի ցուցիչներ և աղբարկղային կառուցվածքներ տարբեր տեղաբաշխման չափերի համար: Բաշխիչը երկարացնում է ծրագրի ընդմիջումը sbrk()-ի միջոցով, իսկ սկզբնական ընդլայնումը կառավարվում է ներքին պարամետրով, որը կոչվում է M_TOP_PAD, որը լռելյայն սահմանում է 128 ԿԲ լիցք: Այնուամենայնիվ, իրական սկզբնական հարցումը ճշգրտվում է էջի հավասարեցման և առկա ընդմիջման դիրքի համար, ինչը հաճախ հանգեցնում է ավելի փոքր առաջին հարցումի, որը սովորաբար հայտնվում է այդ 72 ԿԲ ցուցանիշի մոտ նոր սկսված գործընթացում:

Երկրորդ, glibc 2.26-ից ի վեր, հատկացնողը սկզբնավորում է թելային-տեղական քեշը (tcache) առաջին օգտագործման ժամանակ: tcache-ը պարունակում է 64 աղբարկղ (մեկը փոքր տեղաբաշխման չափի դասի համար), որոնցից յուրաքանչյուրը կարող է պահել մինչև 7 քեշավորված կտոր: tcache_perthread_struct-ն ինքնին սպառում է մոտ 1 ԿԲ, սակայն դրա սկզբնավորման գործողությունը գործարկում է ավելի լայն ասպարեզի կարգավորում: Երրորդ, C++-ի գործարկման ժամանակն արդեն կատարել է հատկացումներ մինչև ձեր main() նույնիսկ գործարկումը՝ ստատիկ կոնստրուկտորներ, iostream բուֆերային սկզբնավորումը std::cout-ի և ընկերների համար, և տեղայնացման կարգավորումները բոլորն էլ նպաստում են այդ սկզբնական կույտի հետքին:

Արենայի համակարգը և ինչու է նախնական հատկացումը խելացի

Հիշողության զգալի հատվածը նախապես հատկացնելու որոշումը, այլ ոչ թե մասնակի պահանջելը, իրագործման պատահականություն չէ: Դա կանխամտածված ինժեներական փոխզիջում է, որը արմատավորված է համակարգերի ծրագրավորման տասնամյակների փորձով: brk() կամ mmap()-ի յուրաքանչյուր զանգ ներառում է կոնտեքստի անցում օգտագործողի տարածությունից միջուկի տարածք, գործընթացի վիրտուալ հիշողության քարտեզագրումների փոփոխում և էջի աղյուսակի հնարավոր թարմացումներ: Ժամանակակից սարքավորումների վրա մեկ համակարգային զանգի արժեքը մոտավորապես 100-200 նանվայրկյան է.

Դիտարկենք մի ծրագիր, որը սկզբնավորման ընթացքում կատարում է 10000 փոքր հատկացում: Առանց նախնական տեղաբաշխման, դա կնշանակի 10,000 համակարգային զանգ, որը կարժենա մոտավորապես 1-2 միլիվայրկյան մաքուր ծախս: Արենայի վրա հիմնված տեղաբաշխիչով առաջին տեղաբաշխումը գործարկում է մեկ համակարգային զանգ, իսկ հաջորդող 9999 հատկացումներն ամբողջությամբ սպասարկվում են օգտագործողի տարածքում՝ սլաքի թվաբանական և կապակցված ցուցակի գործողությունների միջոցով, որոնցից յուրաքանչյուրը տևում է մոտավորապես 10-50 նանվայրկյան: Մաթեմատիկան միանշանակ է՝ նախաբաշխումը հաղթում է մեծության կարգերով:

72 ԿԲ-ը, որը դուք տեսնում եք ձեր առաջին բաշխման ժամանակ, վատնված հիշողություն չէ, դա կատարողական ներդրում է: Բաշխողը խաղադրույք է կատարում, որ ձեր ծրագիրը շուտով ավելի շատ հատկացումներ կանի, և իրական աշխարհի գրեթե բոլոր սցենարներում այդ խաղադրույքը մեծ արդյունք է տալիս: Չօգտագործված վիրտուալ հասցեների տարածքի արժեքը ըստ էության զրոյական է ժամանակակից 64-բիթանոց համակարգերում:

Վիրտուալ հիշողությունն ընդդեմ ֆիզիկական հիշողության. ինչու դա նշանակություն չունի

Առաջին անգամ նման վարքագծի հանդիպող ծրագրավորողների ընդհանուր մտահոգությունը ռեսուրսների վատնումն է: Եթե ​​ինձ անհրաժեշտ է ընդամենը 4 բայթ, ինչո՞ւ է իմ ծրագիրը սպառում 72 ԿԲ: Քննադատական ​​պատկերացումն այն է, որ վիրտուալ հիշողությունը ֆիզիկական հիշողություն չէ: Երբ glibc-ը երկարացնում է ծրագրի ընդմիջումը 72 ԿԲ-ով, միջուկը թարմացնում է գործընթացի վիրտուալ հիշողության քարտեզագրումները, բայց այն անմիջապես չի ապահովում այդ էջերը ֆիզիկական RAM-ով: Փաստացի ֆիզիկական էջերը բաշխվում են ըստ պահանջի էջի անսարքությունների միջոցով. միայն այն ժամանակ, երբ ձեր ծրագիրը գրում է որոշակի հասցեով, միջուկը դրան վերագրում է հիշողության իրական էջ:

💡 DID YOU KNOW?

Mewayz replaces 8+ business tools in one platform

CRM · Invoicing · HR · Projects · Booking · eCommerce · POS · Analytics. Free forever plan available.

Start Free →

Սա նշանակում է, որ չնայած ձեր գործընթացի վիրտուալ չափը մեծանում է 72 ԿԲ-ով, նրա ռեզիդենտ հավաքածուի չափը (RSS)՝ իրականում սպառված ֆիզիկական RAM-ի քանակը, ավելանում է միայն այն էջերով, որոնց դուք իրականում շոշափում եք: Մեկ նոր ինտի համար դա սովորաբար մեկ 4 ԿԲ էջ է, գումարած այն էջերը, որոնք զբաղեցնում են արենայի մետատվյալները: Մնացած վիրտուալ տարածքը գտնվում է այնտեղ, պատրաստ է օգտագործման համար, որը ոչինչ չի արժենա, բացի հասցեների տարածությունից, որից դուք ունեք 128 ՏԲ 64-բիթանոց Linux համակարգում:

Այս տարբերակումը չափազանց կարևոր է, երբ պրոֆիլավորումն ու արտադրական կիրառությունները մոնիտորինգ են անում: Եթե ​​դուք ծրագրավորում եք կառուցում, որը պետք է հետևի ռեսուրսների իրական սպառմանը, լինի դա SaaS-ի հետին պլան, միկրոսերվիս կամ վերլուծական խողովակաշար, ինչպիսին է Mewayz-ի նման հարթակներում բիզնես գործառնությունների համար, դուք միշտ պետք է վերահսկեք RSS-ը, այլ ոչ թե վիրտուալ չափը: Գործիքները, ինչպիսիք են /proc/[pid]/smaps, valgrind --tool=massif և pmap-ը, կարող են ձեզ տալ ճշգրիտ ֆիզիկական հիշողության հետքեր, այլ ոչ թե մոլորեցնող վիրտուալ հիշողության թվեր:

Ինչպես են տարբեր հատկացնողները վարում առաջին հատկացումը

72 ԿԲ ցուցանիշը հատուկ է glibc-ի ptmalloc2-ին: Այլ տեղաբաշխողները կատարում են տարբեր փոխզիջումներ, և նախնական բաշխման վերադիր ծախսերը համապատասխանաբար տատանվում են: Այս տարբերությունների ըմբռնումը արժեքավոր է արդյունավետության նկատմամբ զգայուն հավելվածների համար հատկացուցիչ ընտրելիս:

  • jemalloc (օգտագործվում է Facebook-ի, FreeBSD-ի կողմից) — Օգտագործում է ավելի հատիկավոր ասպարեզի կառուցվածք՝ թելային-տեղական քեշերով: Սկզբնական գերավճարը հակված է ավելի բարձր լինել (հաճախ 200+ ԿԲ), սակայն ապահովում է ավելի լավ բազմաթելային կատարում՝ կողպեքի նվազման պատճառով:
  • tcmalloc (Google-ի Thread-Caching Malloc) — լռելյայնորեն հատկացնում է յուրաքանչյուր շղթայի քեշը մոտավորապես 2 ՄԲ չափով, ագրեսիվ նախնական տեղաբաշխմամբ: Սկզբնական ծախսերը ավելի բարձր են, բայց հետագա փոքր հատկացումները չափազանց արագ են:
  • musl libc's malloc — Օգտագործում է շատ ավելի պարզ դիզայն՝ հիմնված mmap-ի վրա բոլոր հատկացումների համար: Սկզբնական գերավճարը նվազագույն է (հաճախ՝ ընդամենը 4 ԿԲ մեկ հատկացման համար), սակայն մեկ տեղաբաշխման արժեքը ավելի բարձր է՝ ավելի հաճախակի համակարգային զանգերի պատճառով:
  • mimalloc (Microsoft) — Օգտագործում է հատվածի վրա հիմնված տեղաբաշխում 64 ՄԲ հատվածներով: Առաջին տեղաբաշխումը առաջացնում է 64 ՄԲ վիրտուալ ամրագրում (նվազագույն ֆիզիկական պարտավորությամբ), առևտրի հասցեների տարածք բացառիկ տեղայնության և թողունակության համար:

Այս հատկացուցիչների միջև ընտրությունն ամբողջությամբ կախված է ձեր ծանրաբեռնվածությունից: Երկարատև սերվերային հավելվածների համար, որոնք ունեն ծանր բազմաշերտ տեղաբաշխում, jemalloc-ը կամ tcmalloc-ը սովորաբար գերազանցում են glibc-ի լռելյայն ցուցանիշը: Հիշողությամբ սահմանափակված ներկառուցված համակարգերի համար musl-ի ավելի պարզ մոտեցումը կարող է նախընտրելի լինել՝ չնայած ցածր թողունակությանը: Ընդհանուր նշանակության աշխատասեղանի և սերվերի հավելվածների մեծ մասի համար ptmalloc2-ի 72 ԿԲ սկզբնական վերադիրքը ներկայացնում է ողջամիտ լռելյայն, որը լավ է աշխատում առանց թյունինգի:

Առաջնային տեղաբաշխման վարքագծի կարգավորում

Եթե լռելյայն 72 ԿԲ նախնական վերադիր ծախսերը իսկապես խնդրահարույց են ձեր օգտագործման դեպքում, գուցե դուք ստեղծում եք հազարավոր կարճատև գործընթացներ, որոնցից յուրաքանչյուրը կատարում է միայն մի քանի տեղաբաշխումներ, glibc-ն ապահովում է մի քանի կարգավորելի mallopt() և MALLOC_ միջավայրի փոփոխականների ընտանիքի միջոցով:

M_TOP_PAD պարամետրը վերահսկում է, թե որքան հավելյալ հիշողություն է պահանջվում հատկացնողը անմիջապես անհրաժեշտից ավելին: Սահմանելով այն 0-ի mallopt(M_TOP_PAD, 0)-ի միջոցով բաշխողին հուշում է պահանջել միայն այն, ինչ անհրաժեշտ է՝ զգալիորեն նվազեցնելով սկզբնական ծախսերը: M_MMAP_THRESHOLD պարամետրը վերահսկում է չափը, որից վեր հատկացումներն օգտագործում են mmap ասպարեզի փոխարեն: M_TRIM_THRESHOLD-ը վերահսկում է, երբ ազատված հիշողությունը վերադարձվում է ՕՀ: Եվ քանի որ glibc 2.26, glibc.malloc.tcache_count և glibc.malloc.tcache_max կարգավորիչները թույլ են տալիս վերահսկել շղթայի քեշի վարքը:

Այնուամենայնիվ, մի զգուշություն. այս պարամետրերի կարգավորումն առանց մանրակրկիտ չափորոշիչների գրեթե միշտ վատթարացնում է իրավիճակը: Լռելյայններն ընտրվել են իրական աշխարհի լայնածավալ պրոֆիլավորման հիման վրա, և դրանք հիանալի տեղ են աշխատանքային ծանրաբեռնվածության ճնշող մեծամասնության համար: Եթե ​​դուք չունեք ամուր ապացույցներ արտադրության պրոֆիլավորումից, որ malloc-ի գլխավերեւը խոչընդոտ է, և դուք չափել եք ձեր փոփոխությունների ազդեցությունը, թողեք կանխադրվածները: Բաշխիչի վաղաժամ օպտիմիզացումն առանձնահատուկ սափրվելու նենգ ձև է, որն անհամար ինժեներական ժամեր է խլել աննշան օգուտների համար:

Ինչ է սա մեզ սովորեցնում համակարգերի ծրագրավորման մասին

72 ԿԲ առաջին տեղաբաշխման առեղծվածը իր հիմքում դաս է աբստրակցիոն շերտերի մասին: C++-ը ձեզ պատրանք է տալիս, որ new int հատկացնում է 4 բայթ: Լեզվի ստանդարտն այդպես է ասում։ Ձեր մտավոր մոդելն այդպես է ասում: Բայց ձեր կոդի և սարքաշարի միջև կա բարդ համակարգերի մի կույտ՝ C++ գործարկման ժամանակը, C գրադարանի բաշխիչը, միջուկի վիրտուալ հիշողության ենթահամակարգը և ապարատային MMU-ն ու TLB-ն, որոնցից յուրաքանչյուրն ավելացնում է իր սեփական վարքագիծը, օպտիմիզացումները և ընդհանուր ծախսերը:

Սա թերություն չէ: Դա համակարգերի ծրագրային ապահովման ամբողջ կետն է: Յուրաքանչյուր շերտ գոյություն ունի իրական խնդիր լուծելու համար. հատկացնողը գոյություն ունի, այնպես որ դուք ստիպված չեք լինի կատարել համակարգային զանգեր յուրաքանչյուր տեղաբաշխման համար: Վիրտուալ հիշողության համակարգը գոյություն ունի, այնպես որ դուք ստիպված չեք լինի ուղղակիորեն կառավարել ֆիզիկական հիշողությունը: Էջի անսարքության կարգավորիչը գոյություն ունի, ուստի հիշողությունը ծույլ է և արդյունավետ: Յուրաքանչյուր շերտ փոխանակում է փոքր քանակությամբ թափանցիկություն մեծ քանակությամբ կատարողականության և հարմարավետության համար:

Ամենահուսալի, ամենաբարձր արդյունավետությամբ համակարգերը կառուցող մշակողները նրանք են, ովքեր հասկանում են այս շերտերը, ոչ թե այն պատճառով, որ պետք է անընդհատ մտածել դրանց մասին, այլ որովհետև երբ ինչ-որ անսպասելի բան է տեղի ունենում (ինչպես առեղծվածային 72 ԿԲ հատկացում), նրանք ունեն մտավոր մոդել՝ հասկանալու, թե ինչու: Անկախ նրանից, թե դուք իրական ժամանակի առևտրային համակարգ եք կառուցում, խաղային շարժիչ կամ բիզնես հարթակ, որը սպասարկում է հազարավոր օգտատերերի, այն կարող է պատճառաբանել, թե իրականում ինչ է անում ձեր կոդը համակարգի մակարդակում, այն է, ինչը տարբերում է իրավասու մշակողներին բացառիկներից: 72 ԿԲ-ն վրիպակ չէ: Դա ձեր հատկացնողն է փայլուն կերպով կատարում իր աշխատանքը:

Կառուցեք ձեր բիզնեսի OS այսօր

Ֆրիլանսերներից մինչև գործակալություններ, Mewayz-ը 207 ինտեգրված մոդուլներով ապահովում է 138000+ բիզնես: Սկսեք անվճար, նորացրեք, երբ աճեք:

Անվճար ստեղծել