Python кодының 4 миллион жолын теруді тексеру жолы. 2 бөлім

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

Python кодының 4 миллион жолын теруді тексеру жолы. 2 бөлім

Бірінші бөлімді оқыңыз

Ресми қолдау түрі (PEP 484)

Біз 2014 жылғы Hack Week кезінде Dropbox-та mypy-пен алғашқы күрделі тәжірибелерімізді жасадық. Hack Week – Dropbox ұйымдастыратын бір апталық іс-шара. Осы уақыт ішінде қызметкерлер қалағанымен жұмыс істей алады! Dropbox-тың ең танымал технологиялық жобаларының кейбірі осындай іс-шараларда басталды. Осы эксперименттің нәтижесінде біз mypy перспективалы болып көрінеді деген қорытындыға келдік, дегенмен жоба әлі де кеңінен қолдануға дайын емес.

Сол кезде Python типті нұсқау жүйелерін стандарттау идеясы ауада болды. Мен айтқанымдай, Python 3.0 нұсқасынан бері функциялар үшін типтік аннотацияларды қолдануға болады, бірақ бұл анықталған синтаксис пен семантикасыз ерікті өрнектер болды. Бағдарламаны орындау кезінде бұл аннотациялар, негізінен, жай ғана еленбеді. Hack Week-тен кейін біз семантиканы стандарттау бойынша жұмыс істей бастадық. Бұл жұмыс пайда болуына әкелді PEP 484 (Гвидо ван Россум, Лукаш Ланга және мен осы құжатта бірлесіп жұмыс жасадық).

Біздің уәждерімізді екі жақтан қарауға болады. Біріншіден, біз бүкіл Python экожүйесі типтік кеңестерді (Python тілінде «түр аннотацияларының» баламасы ретінде қолданылатын термин) пайдаланудың жалпы тәсілін қабылдай алады деп үміттендік. Бұл ықтимал тәуекелдерді ескере отырып, көптеген өзара үйлеспейтін тәсілдерді қолданғаннан жақсырақ болар еді. Екіншіден, біз Python қауымдастығының көптеген мүшелерімен типтік аннотация механизмдерін ашық талқылағымыз келді. Бұл тілек ішінара біз Python бағдарламашыларының кең тобының алдында тілдің негізгі идеяларынан «жолдан тайғандар» болып көрінгіміз келмейтіндігімен байланысты болды. Бұл «үйрек теру» деп аталатын динамикалық терілген тіл. Қоғамда ең басында статикалық теру идеясына біршама күдікті көзқарас пайда бола алмады. Бірақ бұл көңіл-күй ақырында статикалық теру міндетті болмайтыны белгілі болғаннан кейін (және адамдар оның шынымен пайдалы екенін түсінгеннен кейін) жойылды.

Ақырында қабылданған тип туралы анықтама синтаксисі сол уақытта mypy қолдайтын нәрсеге өте ұқсас болды. PEP 484 Python 3.5 нұсқасымен 2015 жылы шығарылды. Python енді динамикалық терілген тіл емес еді. Мен бұл оқиғаны Python тарихындағы маңызды кезең ретінде қарастырғым келеді.

Миграцияның басталуы

2015 жылдың соңында Dropbox mypy-де жұмыс істеу үшін үш адамнан тұратын топ құрды. Олардың қатарында Гвидо ван Россум, Грег Прайс және Дэвид Фишер болды. Осы сәттен бастап жағдай өте тез дами бастады. Mypy өсуіне бірінші кедергі өнімділік болды. Жоғарыда айтқанымдай, жобаның алғашқы күндерінде мен mypy енгізуді C тіліне аудару туралы ойладым, бірақ бұл идея әзірге тізімнен шығып кетті. Біз жүйені CPython аудармашы арқылы іске қосуда қалдық, бұл mypy сияқты құралдар үшін жылдам емес. (PyPy жобасы, JIT компиляторы бар баламалы Python енгізуі де бізге көмектеспеді.)

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

Қолданыстағы кодтың үлкен көлемін аннотациялау кезінде қосымша тексеру бізге көп көмектесті. Мәселе мынада, бұл процесс әдетте mypy-дің көптеген итерациялық орындалуларын қамтиды, өйткені аннотациялар кодқа бірте-бірте қосылып, бірте-бірте жетілдіріледі. Mypy бағдарламасының бірінші іске қосылуы әлі де өте баяу болды, өйткені оның тексеруге тәуелділігі көп болды. Содан кейін жағдайды жақсарту үшін біз қашықтан кэштеу механизмін енгіздік. Егер mypy жергілікті кэштің ескірген болуы мүмкін екенін анықтаса, ол орталықтандырылған репозиторийден бүкіл кодтық база үшін ағымдағы кэш суретін жүктеп алады. Содан кейін ол осы суретті пайдаланып қосымша тексеруді орындайды. Бұл mypy өнімділігін арттыруға тағы бір үлкен қадам жасады.

Бұл Dropbox-та типті тексеруді тез және табиғи қабылдау кезеңі болды. 2016 жылдың соңына қарай бізде типтік аннотациялары бар Python кодының шамамен 420000 XNUMX жолы болды. Көптеген пайдаланушылар типті тексеруге ынталы болды. Көптеген әзірлеушілер Dropbox mypy-ды пайдалана бастады.

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

Көбірек өнімділік!

Қосымша тексерулер mypy-ді жылдамдатты, бірақ құрал әлі де жеткілікті жылдам болмады. Көптеген қосымша тексерулер шамамен бір минутқа созылды. Бұған циклдік импорт себеп болды. Бұл Python тілінде жазылған үлкен кодтық базалармен жұмыс істегендерді таң қалдырмауы мүмкін. Бізде жүздеген модульдер жиынтығы болды, олардың әрқайсысы басқаларын жанама түрде импорттады. Импорттау цикліндегі кез келген файл өзгертілсе, mypy сол циклдегі барлық файлдарды және жиі осы циклден модульдерді импорттаған кез келген модульдерді өңдеуі керек болды. Осындай циклдардың бірі Dropbox-та көптеген қиындықтар тудырған әйгілі «тәуелділік шиеленісі» болды. Бұл құрылымда бірнеше жүздеген модульдер болған кезде, ол тікелей немесе жанама түрде импортталған кезде көптеген сынақтардан өтті, ол өндірістік кодта да қолданылды.

Біз дөңгелек тәуелділіктерді «ажырату» мүмкіндігін қарастырдық, бірақ бізде мұны істеу үшін ресурстар болмады. Бізге таныс емес код тым көп болды. Нәтижесінде біз балама тәсіл ойлап таптық. Біз mypy-ді «тәуелділік шиеленісулері» болған жағдайда да жылдам жұмыс істеуді шештік. Біз бұл мақсатқа mypy демонының көмегімен қол жеткіздік. Демон - екі қызықты мүмкіндікті жүзеге асыратын серверлік процесс. Біріншіден, ол жадта бүкіл кодтық база туралы ақпаратты сақтайды. Бұл mypy іске қосқан сайын мыңдаған импортталған тәуелділіктерге қатысты кэштелген деректерді жүктеудің қажеті жоқ дегенді білдіреді. Екіншіден, ол шағын құрылымдық бөлімшелер деңгейінде функциялар мен басқа субъектілер арасындағы тәуелділікті мұқият талдайды. Мысалы, егер функция foo функцияны шақырады bar, онда тәуелділік бар foo от bar. Файл өзгерген кезде, демон алдымен оқшауланған күйде тек өзгертілген файлды өңдейді. Содан кейін ол өзгертілген функция қолтаңбалары сияқты сол файлға сыртқы көрінетін өзгерістерді қарайды. Демон өзгертілген функцияны нақты пайдаланатын функцияларды екі рет тексеру үшін ғана импорт туралы толық ақпаратты пайдаланады. Әдетте, бұл тәсілмен сіз өте аз функцияларды тексеруіңіз керек.

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

Одан да көп өнімділік!

Жоғарыда талқылаған қашықтан кэштеумен бірге mypy демоны бағдарламашы файлдардың аз санына өзгерістер енгізіп, типті тексеруді жиі орындаған кезде туындайтын мәселелерді толығымен дерлік шешті. Дегенмен, ең аз қолайлы пайдалану жағдайында жүйе өнімділігі әлі де оңтайлыдан алыс болды. mypy файлын таза іске қосу 15 минуттан астам уақыт алуы мүмкін. Және бұл біз бақытты болғаннан әлдеқайда көп болды. Апта сайын жағдай нашарлады, өйткені бағдарламашылар жаңа код жазуды және бар кодқа аннотацияларды қосуды жалғастырды. Біздің пайдаланушылар әлі де жоғары өнімділікке ашты, бірақ біз оларды жарты жолда кездестіргенімізге қуаныштымыз.

Біз mypy туралы бұрынғы идеялардың біріне оралуды шештік. Атап айтқанда, Python кодын C кодына түрлендіру. Cython-мен тәжірибе жасау (Python тілінде жазылған кодты C кодына аударуға мүмкіндік беретін жүйе) бізге көзге көрінетін жылдамдықты бермеді, сондықтан біз өз компиляторымызды жазу идеясын жаңғыртуды шештік. mypy кодтық базасы (Python тілінде жазылған) барлық қажетті типтегі аннотацияларды қамтығандықтан, жүйені жылдамдату үшін осы аннотацияларды қолдануға тырысқан жөн деп ойладық. Мен бұл идеяны сынау үшін тез прототип жасадым. Ол әртүрлі микробағдарламалар бойынша өнімділіктің 10 еседен астам артқанын көрсетті. Біздің идеямыз Cython көмегімен Python модульдерін C модульдеріне компиляциялау және типтік аннотацияларды орындау уақыты түрін тексеруге айналдыру болды (әдетте типтік аннотациялар орындау уақытында еленбейді және тек типті тексеру жүйелері арқылы пайдаланылады). Біз шын мәнінде mypy іске асырылуын Python тілінен статикалық теруге арналған, дәл Python сияқты көрінетін (және көбіне жұмыс істейтін) тілге аударуды жоспарладық. (Тіларалық миграцияның бұл түрі mypy жобасының дәстүріне айналды. Бастапқы mypy енгізуі Alore тілінде жазылған, содан кейін Java және Python синтаксистік гибриді пайда болды).

CPython кеңейтімінің API-іне назар аудару жобаны басқару мүмкіндіктерін жоғалтпаудың кілті болды. Бізге виртуалды машинаны немесе mypy қажет кез келген кітапхананы енгізудің қажеті жоқ еді. Сонымен қатар, біз әлі де барлық Python экожүйесіне және барлық құралдарға (мысалы, pytest) қол жеткізе аламыз. Бұл өңдеу барысында түсіндірілетін Python кодын пайдалануды жалғастыра алатынымызды білдіреді, бұл кодты құрастыруды күтпей, кодты өзгерту және оны сынаудың өте жылдам үлгісімен жұмысты жалғастыруға мүмкіндік берді. Біз екі орындықта отырып, керемет жұмыс істеп жатқан сияқтымыз және бұл бізге ұнады.

Біз mypyc деп атаған компилятор (өйткені ол mypy-ді типтерді талдау үшін алдыңғы қатарда пайдаланады) өте сәтті жоба болып шықты. Жалпы, біз кэштеусіз жиі mypy іске қосулары үшін шамамен 4 есе жылдамдыққа қол жеткіздік. Mypyc жобасының негізін әзірлеуге Майкл Салливан, Иван Левкивский, Хью Хан және өзімнен тұратын шағын топ күнтізбелік 4 айға жуық уақытты алды. Бұл жұмыс көлемі mypy файлын қайта жазу үшін қажет болғаннан әлдеқайда аз болды, мысалы, C++ немесе Go. Және біз жобаны басқа тілде қайта жазғанда жасауға болатын өзгерістерге қарағанда әлдеқайда азырақ өзгерістер енгізуге мәжбүр болдық. Сондай-ақ, біз mypyc-ті басқа Dropbox бағдарламашылары өз кодтарын құрастыру және жылдамдату үшін пайдалана алатындай деңгейге жеткізе аламыз деп үміттендік.

Бұл өнімділік деңгейіне жету үшін бізге қызықты инженерлік шешімдерді қолдану керек болды. Осылайша, компилятор жылдам, төмен деңгейлі С конструкцияларын қолдану арқылы көптеген операцияларды жылдамдата алады.Мысалы, компиляцияланған функция шақыруы C функциясының шақыруына аударылады. Және мұндай шақыру интерпретацияланған функцияны шақырудан әлдеқайда жылдамырақ. Сөздік іздеу сияқты кейбір операциялар әлі де CPython ұсынған тұрақты C-API қоңырауларын пайдалануды қамтиды, олар құрастырылған кезде біршама жылдамырақ болды. Біз интерпретация арқылы жасалған жүйеге қосымша жүктемені жоя алдық, бірақ бұл жағдайда өнімділік тұрғысынан аз ғана пайда әкелді.

Ең көп таралған «баяу» операцияларды анықтау үшін біз код профилін жасадық. Осы деректермен қаруланған біз mypyc-ті осындай операциялар үшін жылдамырақ C кодын жасайтындай етіп түзетуге тырыстық немесе жылдамырақ операцияларды пайдалана отырып, сәйкес Python кодын қайта жазуға тырыстық (және кейде бізде сол немесе басқа мәселе үшін жеткілікті қарапайым шешім болмады). . Python кодын қайта жазу компилятордың бірдей түрлендіруді автоматты түрде орындауынан гөрі мәселені шешудің оңай шешімі болды. Ұзақ мерзімді перспективада біз осы түрлендірулердің көпшілігін автоматтандырғымыз келді, бірақ ол кезде біз ең аз күшпен mypy жылдамдығын арттыруға назар аудардық. Ал осы мақсатқа жетуде біз бірнеше бұрыштарды кестік.

Жалғасы бар…

Құрметті оқырмандар! Сіз mypy жобасының бар екенін білген кезде қандай әсер алдыңыз?

Python кодының 4 миллион жолын теруді тексеру жолы. 2 бөлім
Python кодының 4 миллион жолын теруді тексеру жолы. 2 бөлім

Ақпарат көзі: www.habr.com

пікір қалдыру