CeļŔ uz tipa pārbaudi 4 miljonos Python koda rindiņu. 2. daļa

Šodien mēs publicējam otro daļu materiāla tulkojumam par to, kā Dropbox organizēja tipa kontroli vairākiem miljoniem Python koda rindu.

CeļŔ uz tipa pārbaudi 4 miljonos Python koda rindiņu. 2. daļa

ā†’ Izlasi pirmo daļu

Oficiālais tipa atbalsts (PEP 484)

Pirmos nopietnos eksperimentus ar mypy veicām vietnē Dropbox 2014. gada Hack Week laikā. Hack Week ir vienas nedēļas pasākums, ko rÄ«ko Dropbox. Å ajā laikā darbinieki var strādāt pie tā, ko vēlas! Daži no Dropbox slavenākajiem tehnoloÄ£iju projektiem sākās tādos pasākumos kā Å”ie. Å Ä« eksperimenta rezultātā mēs secinājām, ka mypy izskatās daudzsoloÅ”i, lai gan projekts vēl nav gatavs plaÅ”ai lietoÅ”anai.

Toreiz gaisā virmoja ideja standartizēt Python tipa mājienu sistēmas. Kā jau teicu, kopÅ” Python 3.0 funkcijām bija iespējams izmantot tipa anotācijas, taču tās bija tikai patvaļīgas izteiksmes bez noteiktas sintakses un semantikas. Programmas izpildes laikā Ŕīs anotācijas lielākoties tika vienkārÅ”i ignorētas. Pēc Hack Week mēs sākām strādāt pie semantikas standartizācijas. Å is darbs noveda pie raÅ”anās PEP 484 (Gvido van Rosums, Łukasz Langa un es sadarbojāmies Ŕī dokumenta izstrādē).

MÅ«su motÄ«vus varēja aplÅ«kot no divām pusēm. Pirmkārt, mēs cerējām, ka visa Python ekosistēma varētu pieņemt kopēju pieeju tipa mājienu izmantoÅ”anai (termins, ko Python lieto kā "tipa anotāciju" ekvivalentu). Tas, ņemot vērā iespējamos riskus, bÅ«tu labāk nekā daudzu savstarpēji nesaderÄ«gu pieeju izmantoÅ”ana. Otrkārt, mēs vēlējāmies atklāti apspriest tipa anotācijas mehānismus ar daudziem Python kopienas locekļiem. Å o vēlmi daļēji noteica tas, ka mēs plaÅ”o Python programmētāju acÄ«s negribētu izskatÄ«ties kā ā€œatkritējiā€ no valodas pamatidejām. Tā ir dinamiski drukāta valoda, kas pazÄ«stama kā "pÄ«les rakstÄ«Å”ana". SabiedrÄ«bā paŔā sākumā nedaudz aizdomÄ«ga attieksme pret statiskās maŔīnrakstÄ«Å”anas ideju nevarēja palÄ«dzēt, bet radās. Taču Å”is noskaņojums galu galā mazinājās pēc tam, kad kļuva skaidrs, ka statiskā rakstÄ«Å”ana nebÅ«s obligāta (un pēc tam, kad cilvēki saprata, ka tā patieŔām ir noderÄ«ga).

Tipa mājienu sintakse, kas galu galā tika pieņemta, bija ļoti lÄ«dzÄ«ga tai, ko mypy tajā laikā atbalstÄ«ja. PEP 484 tika izlaists kopā ar Python 3.5 2015. gadā. Python vairs nebija dinamiski drukāta valoda. Man patÄ«k domāt par Å”o notikumu kā nozÄ«mÄ«gu pagrieziena punktu Python vēsturē.

Migrācijas sākums

2015. gada beigās Dropbox izveidoja trÄ«s cilvēku komandu, lai strādātu pie mypy. Viņu vidÅ« bija Gvido van Rosums, Gregs Praiss un Deivids FiÅ”ers. KopÅ” Ŕī brīža situācija sāka attÄ«stÄ«ties ārkārtÄ«gi ātri. Pirmais Ŕķērslis mypy izaugsmei bija veiktspēja. Kā jau minēju iepriekÅ”, projekta sākumā es domāju par mypy implementācijas tulkoÅ”anu C valodā, taču Ŕī ideja pagaidām tika svÄ«trota no saraksta. Mēs bijām iestrēguÅ”i ar sistēmas palaiÅ”anu, izmantojot CPython tulku, kas nav pietiekami ātrs tādiem rÄ«kiem kā mypy. (Mums nepalÄ«dzēja arÄ« PyPy projekts, alternatÄ«va Python ievieÅ”ana ar JIT kompilatoru.)

Par laimi, Å”eit mums ir palÄ«dzējuÅ”i daži algoritmu uzlabojumi. Pirmais spēcÄ«gais ā€œpaātrinātājsā€ bija pakāpeniskas pārbaudes ievieÅ”ana. Å Ä« uzlabojuma ideja bija vienkārÅ”a: ja visas moduļa atkarÄ«bas nav mainÄ«juŔās kopÅ” iepriekŔējās mypy palaiÅ”anas, mēs varam izmantot iepriekŔējās palaiÅ”anas laikā keÅ”atmiņā saglabātos datus, strādājot ar atkarÄ«bām. Mums bija jāveic tikai modificēto failu un no tiem atkarÄ«go failu tipa pārbaude. Mypy pat devās nedaudz tālāk: ja moduļa ārējais interfeiss nemainÄ«jās, mypy pieņēma, ka citi moduļi, kas importēja Å”o moduli, nav jāpārbauda vēlreiz.

Pakāpeniska pārbaude mums ir ļoti palÄ«dzējusi, anotējot lielu daudzumu esoŔā koda. Lieta tāda, ka Å”is process parasti ietver daudzas iteratÄ«vas mypy darbÄ«bas, jo kodam pakāpeniski tiek pievienotas anotācijas un pakāpeniski uzlabotas. Mypy pirmā palaiÅ”ana joprojām bija ļoti lēna, jo tajā bija jāpārbauda daudz atkarÄ«bu. Pēc tam, lai uzlabotu situāciju, mēs ieviesām attālās keÅ”atmiņas mehānismu. Ja mypy konstatē, ka vietējā keÅ”atmiņa, visticamāk, ir novecojusi, tā lejupielādē paÅ”reizējo keÅ”atmiņas momentuzņēmumu visai kodu bāzei no centralizētās krātuves. Pēc tam tā veic pakāpenisku pārbaudi, izmantojot Å”o momentuzņēmumu. Tas ir spēris vēl vienu lielu soli, lai palielinātu mypy veiktspēju.

Å is bija periods, kad Dropbox tika ātri un dabiski ieviesta tipa pārbaude. 2016. gada beigās mums jau bija aptuveni 420000 XNUMX Python koda rindu ar tipa anotācijām. Daudzi lietotāji bija entuziastiski par tipa pārbaudi. Arvien vairāk izstrādes komandu izmantoja Dropbox mypy.

Toreiz viss izskatÄ«jās labi, bet mums vēl bija daudz darāmā. Sākām veikt periodiskas iekŔējās lietotāju aptaujas, lai apzinātu projekta problēmzonas un saprastu, kādi jautājumi vispirms ir jāatrisina (Ŕāda prakse uzņēmumā tiek izmantota arÄ« Å”odien). SvarÄ«gākie, kā kļuva skaidrs, bija divi uzdevumi. Pirmkārt, mums bija nepiecieÅ”ams plaŔāks koda pārklājums, otrkārt, lai mypy strādātu ātrāk. Bija pilnÄ«gi skaidrs, ka mÅ«su darbs, lai paātrinātu mypy un ieviestu to uzņēmuma projektos, vēl nebija pabeigts. Mēs, pilnÄ«bā apzinoties Å”o divu uzdevumu svarÄ«gumu, ķērāmies pie to risināŔanas.

Vairāk produktivitātes!

Pakāpeniskas pārbaudes padarÄ«ja mypy ātrāku, taču rÄ«ks joprojām nebija pietiekami ātrs. Daudzas pakāpeniskas pārbaudes ilga apmēram minÅ«ti. Iemesls tam bija cikliskais imports. Tas, iespējams, nepārsteigs nevienu, kurÅ” ir strādājis ar lielām Python rakstÄ«tām kodu bāzēm. Mums bija simtiem moduļu komplekti, no kuriem katrs netieÅ”i importēja visus pārējos. Ja tika mainÄ«ts kāds importÄ“Å”anas cilpas fails, mypy bija jāapstrādā visi Ŕīs cilpas faili un bieži vien visi moduļi, kas importēja moduļus no Ŕīs cilpas. Viens no Ŕādiem cikliem bija bēdÄ«gi slavenais ā€œatkarÄ«bas juceklisā€, kas Dropbox radÄ«ja daudz nepatikÅ”anas. Kādreiz Å”ajā struktÅ«rā bija vairāki simti moduļu, kamēr tā tika tieÅ”i vai netieÅ”i importēta, daudzi testi, tā tika izmantota arÄ« ražoÅ”anas kodā.

Mēs apsvērām iespēju "atŔķetināt" cirkulārās atkarÄ«bas, taču mums nebija resursu, lai to izdarÄ«tu. Bija pārāk daudz koda, ar kuru mēs nebijām pazÄ«stami. Rezultātā mēs nonācām pie alternatÄ«vas pieejas. Mēs nolēmām, ka mypy darbojas ātri, pat ja ir ā€œatkarÄ«bas juceklisā€. Mēs sasniedzām Å”o mērÄ·i, izmantojot mypy dēmonu. Dēmons ir servera process, kas ievieÅ” divas interesantas funkcijas. Pirmkārt, tā atmiņā saglabā informāciju par visu kodu bāzi. Tas nozÄ«mē, ka katru reizi, kad palaižat mypy, jums nav jāielādē keÅ”atmiņā saglabātie dati, kas saistÄ«ti ar tÅ«kstoÅ”iem importētu atkarÄ«bu. Otrkārt, viņŔ rÅ«pÄ«gi, mazo struktÅ«rvienÄ«bu lÄ«menÄ«, analizē atkarÄ«bas starp funkcijām un citām vienÄ«bām. Piemēram, ja funkcija foo izsauc funkciju bar, tad ir atkarÄ«ba foo no bar. Kad fails mainās, dēmons vispirms atseviŔķi apstrādā tikai mainÄ«to failu. Pēc tam tiek aplÅ«kotas Ŕī faila ārēji redzamās izmaiņas, piemēram, mainÄ«tie funkciju paraksti. Dēmons izmanto detalizētu informāciju par importÄ“Å”anu tikai, lai vēlreiz pārbaudÄ«tu tās funkcijas, kuras faktiski izmanto modificēto funkciju. Parasti, izmantojot Å”o pieeju, jums ir jāpārbauda ļoti maz funkciju.

To visu ieviest nebija viegli, jo sākotnējā mypy ievieÅ”ana lielā mērā bija vērsta uz viena faila apstrādi vienlaikus. Nācās saskarties ar daudzām robežsituācijām, kuru raÅ”anās prasÄ«ja atkārtotas pārbaudes gadÄ«jumos, kad kodā kaut kas mainÄ«jās. Piemēram, tas notiek, ja klasei tiek pieŔķirta jauna bāzes klase. Kad mēs izdarÄ«jām to, ko gribējām, mēs varējām samazināt lielāko daļu papildu pārbaužu izpildes laiku lÄ«dz dažām sekundēm. Å Ä« mums Ŕķita liela uzvara.

Vēl lielāka produktivitāte!

Kopā ar attālo keÅ”atmiņu, par kuru es runāju iepriekÅ”, mypy dēmons gandrÄ«z pilnÄ«bā atrisināja problēmas, kas rodas, programmētājam bieži veicot tipa pārbaudi, veicot izmaiņas nelielā skaitā failu. Tomēr sistēmas veiktspēja visnelabvēlÄ«gākajā lietoÅ”anas gadÄ«jumā joprojām bija tālu no optimālās. TÄ«ra mypy palaiÅ”ana var ilgt vairāk nekā 15 minÅ«tes. Un tas bija daudz vairāk, nekā mēs bÅ«tu bijuÅ”i apmierināti. Katru nedēļu situācija pasliktinājās, jo programmētāji turpināja rakstÄ«t jaunu kodu un pievienot anotācijas esoÅ”ajam kodam. MÅ«su lietotāji joprojām bija izsalkuÅ”i pēc lielākas veiktspējas, taču mēs priecājāmies viņus satikt pusceļā.

Mēs nolēmām atgriezties pie vienas no iepriekŔējām idejām par mypy. Proti, pārvērst Python kodu C kodā. EksperimentÄ“Å”ana ar Cython (sistēmu, kas ļauj tulkot Python rakstÄ«to kodu C kodā) mums nedeva nekādu redzamu paātrinājumu, tāpēc mēs nolēmām atdzÄ«vināt ideju par sava kompilatora rakstÄ«Å”anu. Tā kā mypy kodu bāzē (rakstÄ«ta Python) jau bija visas nepiecieÅ”amās tipa anotācijas, mēs uzskatÄ«jām, ka bÅ«tu vērts mēģināt izmantot Ŕīs anotācijas, lai paātrinātu sistēmu. Es ātri izveidoju prototipu, lai pārbaudÄ«tu Å”o ideju. Tas uzrādÄ«ja vairāk nekā 10 reižu veiktspējas pieaugumu dažādos mikro etalonos. MÅ«su ideja bija apkopot Python moduļus C moduļos, izmantojot Cython, un pārvērst tipa anotācijas izpildlaika tipa pārbaudēs (parasti tipa anotācijas izpildes laikā tiek ignorētas un tiek izmantotas tikai tipa pārbaudes sistēmās). Mēs faktiski plānojām tulkot mypy ievieÅ”anu no Python valodā, kas bija paredzēta statiskai ievadÄ«Å”anai un kas izskatÄ«tos (un lielākoties darbotos) tieÅ”i tāpat kā Python. (Šāda veida starpvalodu migrācija ir kļuvusi par mypy projekta tradÄ«ciju. Sākotnējā mypy ievieÅ”ana tika uzrakstÄ«ta Alore, pēc tam bija Java un Python sintaktiskais hibrÄ«ds).

KoncentrÄ“Å”anās uz CPython paplaÅ”inājuma API bija galvenais, lai nezaudētu projektu pārvaldÄ«bas iespējas. Mums nebija jāievieÅ” virtuālā maŔīna vai bibliotēkas, kas bija vajadzÄ«gas mypy. Turklāt mums joprojām bÅ«tu piekļuve visai Python ekosistēmai un visiem rÄ«kiem (piemēram, pytest). Tas nozÄ«mēja, ka izstrādes laikā varējām turpināt izmantot interpretētu Python kodu, ļaujot mums turpināt strādāt ar ļoti ātru koda izmaiņu un tā testÄ“Å”anas modeli, nevis gaidÄ«t, kamēr kods tiks kompilēts. IzskatÄ«jās, ka mēs, tā teikt, paveicam lielisku darbu, sēdēdami uz diviem krēsliem, un mums tas patika.

Kompilators, ko mēs nosaucām par mypyc (jo tas izmanto mypy kā priekÅ”galu tipu analÄ«zei), izrādÄ«jās ļoti veiksmÄ«gs projekts. Kopumā mēs panācām aptuveni 4 reizes paātrinājumu biežai mypy palaiÅ”anai bez keÅ”atmiņas. Lai izstrādātu mypyc projekta kodolu, nelielai komandai Maikls Salivans, Ivans Levkivskis, HjÅ« Hāns un es aizņēma aptuveni 4 kalendāros mēneÅ”us. Å is darba apjoms bija daudz mazāks nekā tas, kas bÅ«tu bijis nepiecieÅ”ams, lai pārrakstÄ«tu mypy, piemēram, C++ vai Go. Un mums projektā bija jāveic daudz mazāk izmaiņu, nekā bÅ«tu bijis jāveic, pārrakstot to citā valodā. Mēs arÄ« cerējām, ka varēsim panākt mypyc lÄ«dz tādam lÄ«menim, lai citi Dropbox programmētāji to varētu izmantot sava koda apkopoÅ”anai un paātrināŔanai.

Lai sasniegtu Å”o veiktspējas lÄ«meni, mums bija jāpiemēro daži interesanti inženiertehniskie risinājumi. Tādējādi kompilators var paātrināt daudzas darbÄ«bas, izmantojot ātras, zema lÄ«meņa C konstrukcijas. Piemēram, kompilēts funkcijas izsaukums tiek pārtulkots C funkcijas izsaukumā. Un Ŕāds izsaukums ir daudz ātrāks nekā interpretētas funkcijas izsaukÅ”ana. Dažās operācijās, piemēram, vārdnÄ«cās meklÄ“Å”anā, joprojām tika izmantoti regulāri C-API izsaukumi no CPython, kas kompilÄ“Å”anas laikā bija tikai nedaudz ātrāki. Varējām novērst papildu slodzi sistēmai, ko radÄ«ja interpretācija, taču tas Å”ajā gadÄ«jumā deva tikai nelielu ieguvumu veiktspējas ziņā.

Lai identificētu visizplatÄ«tākās ā€œlēnāsā€ darbÄ«bas, mēs veicām koda profilÄ“Å”anu. Apbruņojoties ar Å”iem datiem, mēs mēģinājām vai nu pielāgot mypyc, lai tas Ŕādām darbÄ«bām Ä£enerētu ātrāku C kodu, vai arÄ« pārrakstÄ«t atbilstoÅ”o Python kodu, izmantojot ātrākas darbÄ«bas (un dažreiz mums vienkārÅ”i nebija pietiekami vienkārÅ”a risinājuma Å”ai vai citai problēmai). . Python koda pārrakstÄ«Å”ana bieži bija vienkārŔāks problēmas risinājums nekā kompilatora automātiska pārveidoÅ”ana. Ilgtermiņā mēs vēlējāmies automatizēt daudzas no Ŕīm transformācijām, taču tajā laikā mēs koncentrējāmies uz mypy paātrināŔanu ar minimālu piepÅ«li. Un virzoties uz Å”o mērÄ·i, mēs nogriezām vairākus stÅ«rus.

Lai varētu turpināt ...

Cienījamie lasītāji! Kādi bija jūsu iespaidi par mypy projektu, kad uzzinājāt par tā esamību?

CeļŔ uz tipa pārbaudi 4 miljonos Python koda rindiņu. 2. daļa
CeļŔ uz tipa pārbaudi 4 miljonos Python koda rindiņu. 2. daļa

Avots: www.habr.com

Pievieno komentāru