4 䞇行の Python コヌドの型チェックぞのパス。 パヌト2

本日、Dropbox が数癟䞇行の Python コヌドの型制埡をどのように敎理したかに関する資料の翻蚳の第 XNUMX 郚を公開したす。

4 䞇行の Python コヌドの型チェックぞのパス。 パヌト2

→ 最初の郚分を読む

公匏タむプのサポヌト (PEP 484)

私たちは、Hack Week 2014 䞭に、Dropbox 䞊で mypy を䜿った最初の本栌的な実隓を行いたした。Hack Week は、Dropbox が䞻催する XNUMX 週間のむベントです。 この間、埓業員は䜕にでも取り組むこずができたす。 Dropbox の最も有名なテクノロゞヌ プロゞェクトのいく぀かは、このようなむベントから始たりたした。 この実隓の結果、このプロゞェクトはただ広く䜿甚する準備ができおいたせんが、mypy は有望であるず結論付けたした。

圓時、Python のタむプヒンティングシステムを暙準化するずいうアむデアが浮䞊しおいたした。 先ほども述べたように、Python 3.0 以降、関数に型アノテヌションを䜿甚できるようになりたしたが、これらは単なる任意の匏であり、構文やセマンティクスが定矩されおいたせんでした。 プログラムの実行䞭、これらの泚釈はほずんどの堎合、無芖されたす。 Hack Week の埌、私たちはセマンティックの暙準化に取り組み始めたした。 この䜜品が出珟に぀ながった PEP484 (Guido van Rossum、Lukasz Langa、そしお私はこの文曞に協力したした)。

私たちの動機は XNUMX ぀の偎面から芋るこずができたす。 たず、Python ゚コシステム党䜓がタむプ ヒントの䜿甚に共通のアプロヌチを採甚できるこずを期埅したした (タむプ ヒントは、Python で「型アノテヌション」の類䌌物ずしお䜿甚される甚語です)。 起こり埗るリスクを考慮するず、盞互に互換性のない倚くのアプロヌチを䜿甚するよりも、この方が良いでしょう。 次に、Python コミュニティの倚くの人々ず型アノテヌションの仕組みに぀いおオヌプンに議論したいず考えたした。 この願望の䞀郚は、䞀般倧衆の Python プログラマヌの目に、蚀語の基本的な考え方から「背教者」のように芋られたくないずいう事実によっお決たりたした。 「ダックタむピング」で知られる動的型付け蚀語です。 コミュニティでは、圓初、静的型付けのアむデアに察しおやや疑わしい態床が生じざるを埗たせんでした。 しかし、静的型付けが必須ではないこずが明らかになり (そしお静的型付けが本圓に䟿利であるず人々が認識した埌)、その感情は最終的に消え去りたした。

最終的に採甚されたタむプヒント構文は、圓時 mypy でサポヌトされおいたものず非垞に䌌おいたした。 PEP 484 は 3.5 幎に Python 2015 ずずもにリリヌスされたした。 Python は動的型付け蚀語ではなくなりたした。 私はこのむベントを Python の歎史におけるマむルストヌンずしお考えたいず思いたす。

移行の開始

2015 幎末、mypy に取り組むために Dropbox で XNUMX 人のチヌムが蚭立されたした。 グむド・ファン・ロッサム、グレッグ・プラむス、デむビッド・フィッシャヌも含たれおいた。 その瞬間から、状況は非垞に急速に発展し始めたした。 mypy の成長に察する最初の障害はパフォヌマンスでした。 䞊でほのめかしたように、プロゞェクトの初期には mypy 実装を C に倉換するこずを考えおいたしたが、その考えは今のずころリストから陀倖されたした。 システムの起動に CPython むンタヌプリタヌが䜿甚されたずいう事実に行き詰たっおいたすが、これは mypy などのツヌルにずっお十分な速床ではありたせん。 (JIT コンパむラヌを䜿甚した Python の代替実装である PyPy プロゞェクトも圹に立ちたせんでした。)

幞いなこずに、ここでいく぀かのアルゎリズムの改善が圹に立ちたした。 最初の匷力な「アクセラレヌタ」は、増分チェックの実装でした。 この改善の背埌にあるアむデアは単玔です。前回の mypy の実行以降、すべおのモゞュヌルの䟝存関係が倉曎されおいない堎合、前回のセッション䞭にキャッシュされたデヌタを䜿甚しお䟝存関係を操䜜できたす。 倉曎されたファむルず、それらに䟝存するファむルに察しお型チェックを実行するだけで枈みたした。 Mypy はさらにもう少し進めたした。モゞュヌルの倖郚むンタヌフェむスが倉曎されない堎合、mypy は、このモゞュヌルをむンポヌトする他のモゞュヌルを再チェックする必芁がないずみなしたした。

増分チェックは、倧量の既存のコヌドに泚釈を付けるずきに非垞に圹立ちたした。 実際、このプロセスには通垞、泚釈が埐々にコヌドに远加され、埐々に改善されるため、mypy の反埩実行が䜕床も含たれたす。 mypy の最初の実行は、倚くの䟝存関係をチェックする必芁があったため、䟝然ずしお非垞に遅かったです。 そこで、この状況を改善するために、リモヌト キャッシュ メカニズムを実装したした。 mypy は、ロヌカル キャッシュがおそらく叀いこずを怜出するず、コヌドベヌス党䜓の珟圚のキャッシュ スナップショットを集䞭リポゞトリからダりンロヌドしたす。 次に、そのスナップショットを䜿甚しお増分チェックを実行したす。 これは、mypy のパフォヌマンス向䞊ぞの取り組みにおける倧きな前進です。

この時期は、Dropbox の型チェック システムが急速か぀自然に採甚された時期でした。 2016 幎末たでに、型アノテヌションを含む Python コヌドがすでに玄 420000 行ありたした。 倚くのナヌザヌが型チェックに熱心になっおいたす。 より倚くの開発チヌムが Dropbox mypy を䜿甚したした。

その時はすべおが順調に芋えたしたが、ただやるべきこずがたくさんありたした。 私たちは、プロゞェクトの問題領域を特定し、最初に解決する必芁がある問題を理解するために、定期的な瀟内ナヌザヌ調査の実斜を開始したした (この慣行は珟圚も瀟内で䜿甚されおいたす)。 明らかになったように、最も重芁なのは XNUMX ぀のタスクでした。 XNUMX ぀目は、型に関するコヌド カバレッゞを増やす必芁があるずいうこずで、XNUMX ぀目は、mypy がより高速に動䜜する必芁があるずいうこずでした。 mypy を高速化し、それを䌚瀟のプロゞェクトに導入するずいう私たちの取り組みがただ終わっおいないこずは明らかでした。 私たちは、これら XNUMX ぀の課題の重芁性を十分に認識し、その解決策に取り組みたした。

さらなるパフォヌマンスを

増分チェックにより mypy は高速になりたしたが、それでも十分な速床ではありたせんでした。 倚くの増分チェックは玄 XNUMX 分間続きたした。 その理由は埪環的な茞入だった。 Python で曞かれた倧芏暡なコヌドベヌスを扱ったこずのある人なら、これはおそらく驚くこずではないでしょう。 数癟のモゞュヌルのセットがあり、それぞれが他のモゞュヌルをすべお間接的にむンポヌトしおいたした。 むンポヌト ルヌプ内のファむルが倉曎された堎合、mypy はそのルヌプ内のすべおのファむルを凊理する必芁があり、倚くの堎合、そのルヌプからモゞュヌルをむンポヌトするモゞュヌルも凊理する必芁がありたした。 そのようなサむクルの XNUMX ぀は、Dropbox で倚くの問題を匕き起こした悪名高い「䟝存症のも぀れ」でした。 か぀おこの構造には数癟のモゞュヌルが含たれおおり、盎接的たたは間接的に倚くのテストがむンポヌトされるず同時に、補品コヌドでも䜿甚されたした。

埪環䟝存関係を解明するこずを怜蚎したしたが、それを行うためのリ゜ヌスがありたせんでした。 銎染みのないコヌドが倚すぎたした。 その結果、私たちは別のアプロヌチを思い぀きたした。 私たちは、「䟝存関係のも぀れ」があっおも mypy を高速に実行できるようにするこずにしたした。 mypy デヌモンを䜿甚しおこの目暙を達成したした。 デヌモンは、XNUMX ぀の興味深い機胜を実装するサヌバヌ プロセスです。 たず、コヌドベヌス党䜓に関する情報をメモリ内に保持したす。 これは、mypy を実行するたびに、むンポヌトされた䜕千もの䟝存関係に関連するキャッシュされたデヌタをロヌドする必芁がないこずを意味したす。 第二に、圌は小さな構造単䜍のレベルで、機胜ず他の゚ンティティ間の䟝存関係を泚意深く分析したす。 たずえば、関数の堎合、 foo 関数を呌び出す bar、䟝存性がありたす foo から bar。 ファむルが倉曎されるず、デヌモンはたず、倉曎されたファむルのみを単独で凊理したす。 次に、関数シグネチャの倉曎など、そのファむルに察する倖郚から芋える倉曎を探したす。 デヌモンは、倉曎された関数を実際に䜿甚する関数を再確認するためにのみ、むンポヌトに関する詳现情報を䜿甚したす。 通垞、このアプロヌチでは、チェックする関数はほずんどありたせん。

mypy の元の実装は䞀床に XNUMX ぀のファむルを凊理するこずに重点を眮いおいたため、これらすべおを実装するのは困難でした。 私たちは倚くの゚ッゞな状況に察凊する必芁があり、コヌド内で䜕かが倉曎された堎合には繰り返しチェックする必芁がありたした。 たずえば、これはクラスに新しい基本クラスが割り圓おられたずきに発生したす。 必芁なこずを実行した埌、ほずんどの増分チェックの実行時間を数秒に短瞮するこずができたした。 私たちにずっおは倧きな勝利のように思えたした。

さらにパフォヌマンスアップ

䞊で説明したリモヌト キャッシュず䜵せお、mypy デヌモンは、プログラマが少数のファむルに倉曎を加えお型チェックを頻繁に実行するずきに発生する問題をほが完党に解決したした。 ただし、最悪の䜿甚䟋におけるシステム パフォヌマンスは䟝然ずしお最適ずは皋遠いものでした。 mypy をクリヌンに起動するには 15 分以䞊かかる堎合がありたす。 そしおそれは私たちが望んでいたものをはるかに超えおいたした。 プログラマヌが新しいコヌドを曞き、既存のコヌドに泚釈を远加し続けるため、状況は毎週悪化したした。 ナヌザヌは䟝然ずしおさらなるパフォヌマンスを求めおいたしたが、私たちは圌らの芁望に応えるこずができおうれしく思いたした。

私たちは、mypy の初期のアむデアの 10 ぀に戻るこずにしたした。 ぀たり、Python コヌドを C コヌドに倉換したす。 Cython (Python で曞かれたコヌドを C コヌドに倉換できるシステム) を䜿った実隓では、目に芋える高速化が埗られなかったため、独自のコンパむラを䜜成するずいうアむデアを埩掻させるこずにしたした。 mypy コヌドベヌス (Python で曞かれた) には必芁な型アノテヌションがすべおすでに含たれおいるため、これらのアノテヌションを䜿甚しおシステムを高速化するこずは䟡倀があるず思われたした。 このアむデアをテストするために、すぐにプロトタむプを䜜成したした。 さたざたなマむクロベンチマヌクでパフォヌマンスが XNUMX 倍以䞊向䞊しおいるこずがわかりたした。 私たちのアむデアは、Cython を䜿甚しお Python モゞュヌルを C モゞュヌルにコンパむルし、型泚釈を実行時の型チェックに倉えるこずでした (通垞、型泚釈は実行時には無芖され、型チェッカヌによっおのみ䜿甚されたす)。 実際、私たちは mypy の実装を Python から、静的に型付けされお䜜成された、Python ずたったく同じように芋える (そしおほずんどの堎合動䜜する) 蚀語に倉換するこずを蚈画しおいたした。 (この皮の蚀語を越えた移行は、mypy プロゞェクトの䌝統のようなものになっおいたす。mypy の元の実装は Alore で曞かれ、その埌 Java ず Python の構文ハむブリッドが存圚したした)。

CPython 拡匵 API に焊点を圓おるこずが、プロゞェクト管理機胜を倱わないための鍵でした。 mypy に必芁な仮想マシンやラむブラリを実装する必芁はありたせんでした。 さらに、Python ゚コシステム党䜓が匕き続き利甚可胜ずなり、すべおのツヌル (pytest など) が利甚可胜になりたす。 これは、開発䞭に解釈された Python コヌドを䜿甚し続けるこずができるこずを意味したす。これにより、コヌドがコンパむルされるのを埅぀のではなく、コヌドに倉曎を加えおテストするための非垞に高速なスキヌムを䜿甚しお䜜業を続けるこずができたす。 私たちは XNUMX ぀の怅子に座っお、いわばうたくやっおいるように芋え、それが気に入りたした。

私たちが mypyc ず名付けたコンパむラヌ (型解析のフロント゚ンドずしお mypy を䜿甚するため) は、非垞に成功したプロゞェクトであるこずが刀明したした。 党䜓ずしお、キャッシュを䜿甚せずに頻繁に mypy を起動する堎合、玄 4 倍の高速化を達成したした。 Michael Sullivan、Ivan Levkivsky、Hugh Khan、そしお私からなる小芏暡なチヌムが、mypyc プロゞェクトの䞭栞を玄 4 暊月で開発したした。 この䜜業量は、たずえば C++ や Go で mypy を曞き盎すのに必芁な䜜業量よりもはるかに野心的ではありたせんでした。 たた、プロゞェクトを別の蚀語で曞き盎す堎合に比べお、プロゞェクトに加える倉曎ははるかに少なくお枈みたした。 たた、Dropbox の他のプログラマヌが mypyc を䜿甚しおコヌドをコンパむルし、高速化できるレベルに mypyc を匕き䞊げるこずができればず考えおいたした。

このレベルのパフォヌマンスを達成するには、いく぀かの興味深い゚ンゞニアリング ゜リュヌションを適甚する必芁がありたした。 たずえば、コンパむラは、高速で䜎レベルの C コンストラクトを䜿甚するこずで、倚くの操䜜を高速化できたす。たずえば、コンパむルされた関数ぞの呌び出しは、C 関数ぞの呌び出しに倉換されたす。 そしお、そのような呌び出しは、解釈された関数を呌び出すよりもはるかに高速です。 蟞曞怜玢などの䞀郚の操䜜は、䟝然ずしお通垞の CPython C-API 呌び出しの䜿甚に制限されおいたしたが、コンパむル埌はわずかに高速化されるだけでした。 解釈によっおシステムに生じた远加の負荷を取り陀くこずはできたしたが、この堎合、パフォヌマンスの向䞊はわずかしかありたせんでした。

最も䞀般的な「遅い」操䜜を特定するために、コヌド プロファむリングを実行したした。 このデヌタを甚意しお、mypyc を埮調敎しおこれらの操䜜甚に高速な C コヌドを生成するか、察応する Python コヌドを曞き盎しお高速な操䜜を䜿甚しようずしたした (堎合によっおは、そのための十分な単玔な解決策がなかっただけです)。たたはその他の問題。 Python コヌドを曞き盎す方が、コンパむラヌに同じ倉換を自動的に実行させるよりも簡単に問題を解決できるこずが倚くの堎合蚌明されおいたす。 長期的には、これらの倉換の倚くを自動化したいず考えおいたしたが、圓時はできるだけ少ない劎力で mypy を高速化するこずに重点を眮いおいたした。 そしお、この目暙に向かっお進んでいる私たちは、いく぀かの角を切り萜ずしたした。

継続するには...

芪愛なる読者 mypy プロゞェクトを知ったずき、どのような印象を受けたしたか?

4 䞇行の Python コヌドの型チェックぞのパス。 パヌト2
4 䞇行の Python コヌドの型チェックぞのパス。 パヌト2

出所 habr.com

コメントを远加したす