Неліктен бірінші C++ (m) бөлу әрқашан 72 Кбайт? | Mewayz Blog Skip to main content
Hacker News

Неліктен бірінші C++ (m) бөлу әрқашан 72 Кбайт?

Пікірлер

1 min read Via joelsiks.com

Mewayz Team

Editorial Team

Hacker News

Алғашқы C++ бөлуіңіздің артындағы құпия

Сіз қарапайым C++ бағдарламасын жазасыз. Жалғыз жаңа int. Төрт байт. Сіз strace немесе сүйікті жад профильдерін іске қосасыз және міне, сіздің процесс операциялық жүйеден шамамен 72 Кбайт сұрады. 4 байт емес. 64 байт емес. Толық 72 КБ. Егер сіз осы нөмірге қарап тұрып, құралыңыз сізге өтірік айтып жатыр ма деп ойласаңыз, сіз жалғыз емессіз. Бұл біртүрлі болып көрінетін мінез-құлық C++ әзірлеушілері арасында жадтың ішкі бөліктерін алғаш рет зерттеп жатқан ең жиі қойылатын сұрақтардың бірі болып табылады және бұл жауап бізді код пен нақты жабдықтың арасында орналасқан қабаттар арқылы қызықты саяхатқа апарады.

Сіз жаңа

қоңырау шалғанда не болады

72 КБ фигураны түсіну үшін толық бөлу тізбегін қадағалау керек. C++ коды new int орындаған кезде, компилятор оны оператор newға шақыруға аударады, ол көптеген Linux жүйелерінде glibc-тен malloc-ға өкілдік береді. Бірақ malloc ядродан 4 байт жадты тікелей сұрамайды. Ядро беттерде жұмыс істейді — әдетте x86_64 жүйесінде 4 КБ — және жүйелік қоңырау құны қарапайым жадқа қол жеткізуге қатысты өте үлкен. Әрбір жеке бөлу үшін brk() немесе mmap() шақыру кез келген маңызды емес бағдарламаның жұмысын тоқтатады.

Оның орнына glibc жад бөлгіші — ptmalloc2 деп аталатын іске асыру, өзі Дуг Лидің классикалық dlmalloc нұсқасынан шыққан — делдал ретінде әрекет етеді. Ол алдын ала ядродан үлкен жад блоктарын сұрайды, содан кейін бағдарламаға қажет болғандықтан оларды кішірек бөліктерге кеседі. Бұл сіздің бірінші 4 байтты бөлу операциялық жүйеге әлдеқайда үлкен сұранысты іске қосудың негізгі себебі. Бөлгіш ысырап емес. Бұл стратегиялық.

72 Кбайтты бөлу: Байттар қайда барады

Бастапқы бөлу қосымша шығындары бірнеше ерекше құрамдас бөліктерден келеді, оларды орындау уақыты сізге тіпті бір байтты қолдануға болатын жадты бере алмас бұрын инициализациялауы керек. Әрбір құрамдас бөлікті түсіну санның неліктен қайда түсетінін түсіндіреді.

Біріншіден, glibc's malloc негізгі аренаны инициализациялайды — негізгі ағындағы барлық бөлулерді бақылайтын негізгі бухгалтерлік құрылым. Бұл арена үймеге арналған метадеректерді, бос тізім көрсеткіштерін және әртүрлі бөлу өлшемдері үшін қалта құрылымдарын қамтиды. Бөлгіш бағдарлама үзілісін sbrk() арқылы ұзартады және бастапқы кеңейтім M_TOP_PAD деп аталатын ішкі параметрмен басқарылады, ол әдепкі бойынша 128 Кбайт толтыруға арналған. Дегенмен, нақты бастапқы сұрау бетті туралау және бар үзіліс орны үшін реттеледі, бұл көбінесе кішірек бірінші сұрауға әкеледі — әдетте жаңадан басталған процесте сол 72 КБ фигураның қасына түседі.

Екіншіден, glibc 2.26 нұсқасынан бастап бөлуші бірінші пайдаланған кезде жергілікті ағындық кэшті (tcache) инициализациялайды. Кэште 64 қалта (шағын бөлу өлшемдері класына бір), әрқайсысы 7 кэштелген бөлікке дейін сақтауға қабілетті. tcache_perthread_structөзі шамамен 1 КБ жұмсайды, бірақ оны инициализациялау актісі кеңірек арена орнатуын іске қосады. Үшіншіден, C++ орындалу уақыты main() тіпті іске қосылмай тұрып, бөлулерді орындап қойған — статикалық конструкторлар, std::cout және достар үшін iostream буферін инициализациялау және жергілікті тіл орнатуы осы бастапқы үйме ізіне ықпал етеді.

Арена жүйесі және неге алдын ала бөлу ақылды

Бөлініп сұраудың орнына, жадтың елеулі бөлігін алдын ала бөлу туралы шешім іске асыру кездейсоқ емес. Бұл жүйелік бағдарламалаудың ондаған жылдардағы тәжірибесіне негізделген әдейі жасалған инженерлік келісім. brk() немесе mmap() қолданбаларына әрбір қоңырау пайдаланушы кеңістігінен ядро ​​кеңістігіне контекстік ауысуды, процестің виртуалды жады салыстыруларын өзгертуді және ықтимал бет кестесін жаңартуды қамтиды. Заманауи жабдықта бір жүйелік қоңырау шамамен 100-200 наносекундты құрайды — оқшауланғанда тривиальды, масштабы бойынша апатты.

Бастандыру кезінде 10 000 шағын бөлуді жасайтын бағдарламаны қарастырайық. Алдын ала бөлінбесе, бұл шамамен 1-2 миллисекунд таза үстеме шығындарды талап ететін 10 000 жүйелік қоңырауды білдіреді. Аренаға негізделген бөлгішпен бірінші бөлу бір жүйелік шақыруды іске қосады, ал кейінгі 9 999 бөлу толығымен пайдаланушы кеңістігінде көрсеткіш арифметикасы және байланыстырылған тізім операциялары арқылы қызмет көрсетеді — әрқайсысы шамамен 10-50 наносекундты алады. Математика бір мәнді: алдын ала бөлу үлкен мәндер бойынша жеңеді.

Алғаш рет бөлінгенде көретін 72 Кбайт жад босқа кетпейді — бұл өнімділікке инвестиция. Бөлуші сіздің бағдарламаңыздың жақын арада көбірек бөлуді жасайтынына бәс тігуде және іс жүзінде кез келген нақты сценарийде бұл ставка жақсы нәтиже береді. Пайдаланылмаған виртуалды мекенжай кеңістігінің құны қазіргі 64 биттік жүйелерде нөлге тең.

Виртуалды жады және физикалық жады: неге бұл маңызды емес?

Бұл мінез-құлықты алғаш рет кездестіретін әзірлеушілер арасында жиі кездесетін мәселе - ресурстарды ысырап ету. Маған тек 4 байт қажет болса, менің бағдарламам неліктен 72 Кбайтты тұтынады? Сыни түсінік мынада: виртуалды жады физикалық жады емес. glibc бағдарлама үзілісін 72 Кбайт ұзартқанда, ядро ​​процестің виртуалды жады салыстыруларын жаңартады, бірақ ол сол беттерді физикалық жедел жадымен дереу қайтармайды. Нақты физикалық беттер сұраныс бойынша бет ақаулары арқылы бөлінеді — бағдарлама белгілі бір мекенжайға жазғанда ғана ядро оған жадтың нақты бетін тағайындайды.

💡 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) — нақты тұтынылатын физикалық ЖЖҚ мөлшері — тек сіз түртетін беттерге ғана артады дегенді білдіреді. Жалғыз жаңа int үшін бұл әдетте бір 4 КБ бет, сонымен қатар арена метадеректері алатын кез келген бет. Қалған виртуалды кеңістік сол жерде, пайдалануға дайын, мекенжай кеңістігінен басқа ештеңені қажет етпейді, оның ішінде сізде 64 биттік Linux жүйесінде 128 ТБ бар.

Бұл айырмашылық өндірістік қолданбаларды профильдеу және бақылау кезінде өте маңызды. Егер сіз нақты ресурстарды тұтынуды бақылауды қажет ететін бағдарламалық құралды жасап жатсаңыз — ол SaaS сервері, микросервис немесе бизнес операцияларына арналған Mewayz сияқты платформаларда жұмыс істейтін аналитикалық конвейер болсын — виртуалды өлшемді емес, әрқашан RSS-ті бақылауыңыз керек. /proc/[pid]/smaps, valgrind --tool=massif және pmap сияқты құралдар виртуалды жад сандарын жаңылыстырудың орнына дәл физикалық жад іздерін бере алады.

Әртүрлі бөлушілер бірінші бөлуді қалай өңдейді

72 КБ фигура glibc ptmalloc2-ге тән. Басқа бөлушілер әртүрлі айырбастарды жасайды және бастапқы бөлу үстеме шығыстары сәйкесінше өзгереді. Бұл айырмашылықтарды түсіну өнімділікке сезімтал қолданбалар үшін бөлгішті таңдау кезінде маңызды.

  • jemalloc (Facebook, FreeBSD пайдаланады) — жергілікті ағынды кэштері бар түйіршікті арена құрылымын пайдаланады. Бастапқы үстеме шығындар әдетте жоғарырақ болады (көбінесе 200+ КБ), бірақ құлыптау дауларының азаюына байланысты жақсы көп ағынды өнімділікті қамтамасыз етеді.
  • tcmalloc (Google's 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 параметрі бөлгіш дереу қажет болғаннан тыс қанша қосымша жад сұрайтынын басқарады. Оны mallopt(M_TOP_PAD, 0) көмегімен 0 мәніне орнату бөлушіге бастапқы үстеме шығындарды айтарлықтай азайта отырып, тек қажет нәрсені сұрауды білдіреді. M_MMAP_THRESHOLD параметрі жоғарыдағы бөлулер аренаның орнына mmap пайдаланатын өлшемді басқарады. M_TRIM_THRESHOLD бос жад операциялық жүйеге қайтарылған кезде басқарады. Және glibc 2.26 бастап, glibc.malloc.tcache_count және glibc.malloc.tcache_max реттелетін құрылғылар ағынның кэш әрекетін басқаруға мүмкіндік береді.

Алайда, ескерту: бұл параметрлерді мұқият салыстырусыз реттеу әрқашан дерлік жағдайды нашарлатады. Әдепкі мәндер ауқымды нақты әлемдегі профильдеу негізінде таңдалды және олар жұмыс жүктемелерінің басым көпшілігі үшін қолайлы нүкте болып табылады. Өндірістік профильдеуден сізде malloc үстеме шығындары қиын болатынына сенімді дәлелдер болмаса және сіз өзгерістердің әсерін өлшеген болсаңыз, әдепкі мәндерді жалғыз қалдырыңыз. Бөлу құралын мерзімінен бұрын оңтайландыру - елеусіз пайда алу үшін сансыз инженерлік сағаттарды жұмсайтын, сарқырама қырынудың ерекше жасырын түрі.

Бұл бізге жүйелік бағдарламалау туралы не үйретеді

72 КБ бірінші бөлу құпиясы, өзегінде, абстракциялық қабаттар туралы сабақ болып табылады. C++ сізге жаңа int4 байт бөлетін елесін береді. Тіл стандарты солай дейді. Сіздің психикалық үлгіңіз осылай дейді. Бірақ сіздің кодыңыз бен аппараттық құралдың арасында күрделі жүйелер десте орналасқан — C++ жұмыс уақыты, C кітапханасының бөлгіші, ядроның виртуалды жады ішкі жүйесі және аппараттық құралдың MMU және TLB — әрқайсысы өз әрекеттерін, оңтайландыруларын және қосымша шығындарын қосады.

Бұл кемшілік емес. Бұл жүйелік бағдарламалық қамтамасыз етудің барлық нүктесі. Әрбір қабат нақты мәселені шешу үшін бар: бөлуші бар, сондықтан әрбір бөлу үшін жүйелік қоңыраулар жасаудың қажеті жоқ. Виртуалды жад жүйесі бар, сондықтан физикалық жадты тікелей басқарудың қажеті жоқ. Бет ақауларын өңдеуші бар, сондықтан жад жалқау және тиімді орындалады. Әрбір қабат үлкен көлемде өнімділік пен ыңғайлылық үшін мөлдірліктің аз мөлшерін сатады.

Ең сенімді, өнімділігі жоғары жүйелерді құрастыратын әзірлеушілер бұл қабаттарды түсінетіндер болып табылады — олар олар туралы үнемі ойлану керек болғандықтан емес, күтпеген нәрсе болған кезде (72 Кбайтты жұмбақ бөлу сияқты) олардың себебін түсіну үшін ақыл-ой үлгісі бар. Нақты уақыттағы сауда жүйесін, ойын қозғалтқышын немесе мыңдаған пайдаланушыларға қызмет көрсететін бизнес платформасын жасап жатсаңыз да, жүйе деңгейінде кодыңыздың шын мәнінде не істейтіні туралы ойлау мүмкіндігі құзыретті әзірлеушілерді ерекшелерден ерекшелендіреді. 72 КБ қате емес. Бұл сіздің бөлушіңіз өз жұмысын тамаша орындауда.

Бүгінгі күні өз бизнесіңізді құрыңыз

Фрилансерлерден агенттіктерге дейін, Mewayz 207 біріктірілген модульдері бар 138 000+ бизнеске қуат береді. Тегін бастаңыз, өскен кезде жаңартыңыз.

Тегін тіркелгі жасау→

Try Mewayz Free

All-in-one platform for CRM, invoicing, projects, HR & more. No credit card required.

Start managing your business smarter today

Join 30,000+ businesses. Free forever plan · No credit card required.

Ready to put this into practice?

Join 30,000+ businesses using Mewayz. Free forever plan — no credit card required.

Start Free Trial →

Ready to take action?

Start your free Mewayz trial today

All-in-one business platform. No credit card required.

Start Free →

14-day free trial · No credit card · Cancel anytime