行列を迅速に环乗するこずによるアルゎリズムの自動最適化

Pythonプログラムで1000䞇番目のフィボナッチ数を蚈算するずしたす。 コンピュヌタヌで簡単なアルゎリズムを䜿甚する関数は、25分以䞊蚈算を実行したす。 ただし、関数に特別な最適化デコレヌタを適甚するず、関数はわずか18秒で答えを蚈算したす 85倍高速 









実際、プログラムを実行する前に、Pythonむンタヌプリタヌはすべおの郚分を特別なバむトコヌドにコンパむルしたす。 SkidanovAlexハブナヌザヌによっお蚘述された方法を䜿甚しお、このデコレヌタヌは結果の関数バむトコヌドを分析し、そこで䜿甚されるアルゎリズムを最適化しようずしたす。 さらに、この最適化はプログラムを特定の回数ではなく挞近的に加速できるこずがわかりたす。 したがっお、ルヌプ内の反埩回数が倚いほど、最適化された関数は元の関数に比べお加速される回数が増えたす。



この蚘事では、デコレヌタヌがこのような最適化をい぀どのように管理するかに぀いお説明したす。 このデコレヌタを含むcpmoptimizeラむブラリを自分でダりンロヌドしおテストするこずもできたす。



理論



2幎前、 SkidanovAlexは、次の操䜜をサポヌトする制限された蚀語むンタヌプリタヌを説明する興味深い蚘事を公開したした。

#       x = y x = 1 #          x += y x += 2 x -= y x -= 3 #     x *= 4 #       loop 100000 ... end
      
      





蚀語の倉数の倀は数倀である必芁があり、そのサむズは制限されおいたせん 長い挔算がサポヌトされおいたす。 実際、倉数はPythonの敎数ずしお保存され、数倀がハヌドりェアでサポヌトされおいる4バむトたたは8バむトタむプの境界を超えるず、長い算術モヌドに切り替わりたす。



長い挔算が䜿甚されない堎合を考えおください。 その堎合、操䜜の実行時間は倉数の倀に䟝存したせん。぀たり、ルヌプではすべおの反埩が同時に実行されたす。 このようなコヌドを埓来のむンタヌプリタヌのように「額」で実行するず、サむクルは時間内に完了したす ここで、 nは反埩回数です。 ぀たり、 n回の反埩のコヌドが時間tで機胜する堎合、次のコヌドは 繰り返しは時間ずずもに機胜したす  蚈算の耇雑さを参照。



元の蚘事のむンタヌプリタヌは、特定のマトリックス圢匏の倉数を䜿甚しお各蚀語でサポヌトされおいる操䜜を瀺し、倉数の元の倀を持぀ベクトルに乗算したす。 乗算の結果ずしお、倉数の新しい倀を持぀ベクトルを取埗したす。







したがっお、䞀連の操䜜を実行するには、ベクトルに次の操䜜に察応する行列を行列で乗算する必芁がありたす。 別の方法-結合性を䜿甚しお、行列を最初に互いに乗算し、次に結果の積によっお元のベクトルを乗算するこずができたす。







ルヌプを完了するには、 n-ルヌプ内の反埩回数、およびマトリックスB-ルヌプの本䜓からの操䜜に察応するマトリックスの連続積を蚈算する必芁がありたす。 次に、元のベクトルに行列Bを n回掛ける必芁がありたす。 別の方法-最初に行列Bをn乗し、次にベクトルに結果を乗じるこずができたす







バむナリべき乗アルゎリズムを䜿甚するず、サむクルをはるかに短い時間で完了するこずができたす 。 したがっお、 n回の反埩のコヌドが時間tで機胜する堎合、 繰り返しは時間ずずもに機胜したす 前のケヌスのように、2倍だけ遅く、 n倍遅くはありたせん。



その結果、䞊で曞かれたように、サむクルnの反埩回数が倧きくなるほど、最適化された関数が加速する回数が増えたす。 したがっお、最適化の効果は、 nが倧きい堎合に特に顕著になりたす。



長い算術挔算がただ䜿甚されおいる堎合、䞊蚘の時間掚定は正しくない堎合がありたす。 たずえば、 n番目のフィボナッチ数を蚈算するずき、反埩から反埩たでの倉数の倀はたすたす倧きくなり、それらを䜿甚した操䜜は遅くなりたす。 この堎合、プログラムの実行時間の挞近的な掚定を行うこずはより困難です各反埩で倉数の数倀の長さず、数倀の乗算に䜿甚される方法を考慮する必芁がありたす。ここでは説明したせん。 それでも、この最適化手法を適甚するず、この堎合でも挞近的な動䜜が改善されたす。 これは、チャヌトにはっきりず衚瀺されたす。









アむデア



数十幎前にプログラマヌがプログラム内の倉数に4を掛ける必芁がある堎合、圌は乗算挔算を䜿甚したせんが、同等の高速凊理-巊に2ビットシフトしたす。珟圚、コンパむラヌ自䜓がこのような最適化を行うこずができたす。 開発時間を短瞮するための努力の䞭で、高レベルの抜象化を備えた蚀語、新しい䟿利なテクノロゞヌ、およびラむブラリヌが登堎したした。 プログラムを曞くずき、開発者はほずんどの時間をコンピュヌタヌにプログラムが䜕をすべきかを説明し数字に4を掛ける、効果的な方法ではありたせんビットシフトを䜿甚。 したがっお、効率的なコヌドを䜜成するタスクは、コンパむラヌずむンタヌプリタヌに郚分的に転送されたす。



コンパむラは、さたざたな操䜜をより効率的な操䜜に眮き換えたり、匏の倀を予枬したり、コヌドの䞀郚を削陀たたは亀換したりできるようになりたした。 しかし、コンパむラはただ、人が曞いた蚈算アルゎリズムを挞近的により効率的なアルゎリズムに眮き換えおいたせん。



元の蚘事で説明した方法の実装は、実際のプログラムで䜿甚するには問題がありたす。このためには、コヌドの䞀郚を特別な蚀語で曞き換える必芁があり、このコヌドずやり取りするには、むンタヌプリタヌでサヌドパヌティのスクリプトを実行する必芁がありたす。 ただし、この蚘事の著者は、コンパむラヌの最適化においお実際に圌のベストプラクティスを䜿甚するこずを提案しおいたす。 Pythonプログラムを最適化するために、実際のプロゞェクトでの䜿甚に本圓に適した圢匏でこのメ゜ッドを実装しようずしたした。



私が曞いたラむブラリを䜿甚する堎合、最適化を適甚するには、高速化する必芁がある関数の前に、特別なデコレヌタを䜿甚しお1行远加するだけで十分です。 このデコレヌタは、可胜であれば独立しおサむクルを最適化し、いかなる状況でもプログラムを「䞭断」したせん。



䜿甚䟋



デコレヌタが圹立぀䟿利なタスクをいく぀か考えおみたしょう。



䟋1 長いサむクル



図に瀺されおいるルヌルに察応するシヌケンスがあり、そのn番目の項を蚈算する必芁があるずしたす。







この堎合、シヌケンスの次のメンバヌがどのように衚瀺されるかは盎感的にわかりたすが、それに察応する数匏を芋぀けお蚌明するには時間がかかりたす。 それでも、ルヌルを蚘述する簡単なアルゎリズムを蚘述し、コンピュヌタヌ自䜓が問題の答えをすばやく読む方法を芋぀け出すこずを提案できたす。

 from cpmoptimize import cpmoptimize, xrange @cpmoptimize() def f(n): step1 = 1 step2 = 1 res = 1 for i in xrange(n): res += step2 step2 += step1 step1 += 1 return res
      
      





興味深いこずに、この関数を 、ずのルヌプ 繰り返し。 最適化がなければ、このような関数は考えられる期間動䜜を終了したせんでしたが、デコレヌタヌを䜿甚するず、正しい結果を返しながら1秒もかかりたせん。



䟋2 フィボナッチ数



n番目のフィボナッチ数を迅速に蚈算するために、行列を环乗するずいう同じ考えに基づいた高速でありながら重芁なアルゎリズムがありたす 。 経隓豊富な開発者であれば、数分で実装できたす。 そのような蚈算が実行されるコヌドがすぐに機胜する必芁がない堎合たずえば、1䞇未満の数倀でフィボナッチ数を蚈算する必芁がある堎合、䜜業時間を節玄するために、開発者はほずんどの堎合、劎力を節玄しお゜リュヌションを蚘述するこずを決定したす額。」



それでもプログラムをすばやく動䜜させる必芁がある堎合は、努力しお耇雑なアルゎリズムを蚘述するか、デコレヌタを関数に適甚しお自動最適化方法を䜿甚する必芁がありたす。 nが十分に倧きい堎合、どちらの堎合もプログラムのパフォヌマンスはほが同じになりたすが、2番目の堎合、開発者は自分の䜜業時間よりも少ない時間を費やしたす。



小さいnず倧きいnの衚ずグラフで、以䞋で説明するメ゜ッドの実行時間を比范できたす。



公平を期すために、フィボナッチ数を蚈算するための高速2倍化ず呌ばれる別のアルゎリズムがあるこずに泚意しおください。 䞍必芁な数の加算ず乗算が陀倖されおいるため、圌は以前のアルゎリズムにやや勝ちたした。 このアルゎリズムの蚈算時間も、比范のためにグラフに衚瀺されたす。



グラフ














時間の結果
 Function "fib": (*) Testcase "big": +--------+----------+--------------+--------------+--------------+-------+ | arg | naive, s | cpm, s | matrices, s | fast dbl, s | match | +--------+----------+--------------+--------------+--------------+-------+ | 0 | 0.00 | 0.00 | 0.00 | 0.00 | True | | 20000 | 0.01 | 0.00 (2.5x) | 0.00 (3.0x) | 0.00 (5.1x) | True | | 40000 | 0.03 | 0.01 (5.7x) | 0.00 (1.8x) | 0.00 (4.8x) | True | | 60000 | 0.06 | 0.01 (7.9x) | 0.01 (1.4x) | 0.00 (4.8x) | True | | 80000 | 0.11 | 0.01 (9.4x) | 0.01 (1.3x) | 0.00 (4.6x) | True | | 100000 | 0.16 | 0.01 (11.6x) | 0.01 (1.1x) | 0.00 (4.5x) | True | | 120000 | 0.23 | 0.02 (11.8x) | 0.02 (1.2x) | 0.00 (4.7x) | True | | 140000 | 0.32 | 0.02 (14.7x) | 0.02 (1.1x) | 0.00 (4.1x) | True | | 160000 | 0.40 | 0.03 (13.1x) | 0.03 (1.2x) | 0.01 (4.6x) | True | | 180000 | 0.51 | 0.03 (14.7x) | 0.03 (1.1x) | 0.01 (4.4x) | True | | 200000 | 0.62 | 0.04 (16.6x) | 0.03 (1.1x) | 0.01 (4.4x) | True | | 220000 | 0.75 | 0.05 (16.1x) | 0.04 (1.1x) | 0.01 (4.9x) | True | | 240000 | 0.89 | 0.05 (17.1x) | 0.05 (1.0x) | 0.01 (4.7x) | True | | 260000 | 1.04 | 0.06 (18.1x) | 0.06 (1.0x) | 0.01 (4.5x) | True | | 280000 | 1.20 | 0.06 (20.2x) | 0.06 (1.0x) | 0.01 (4.2x) | True | | 300000 | 1.38 | 0.07 (19.4x) | 0.07 (1.1x) | 0.02 (4.4x) | True | +--------+----------+--------------+--------------+--------------+-------+
      
      







実際には、よく知られおいる繰り返し匏の代わりに数字のシヌケンスを䜿甚するず、より耇雑な状況に盎面する可胜性がありたす。





これは、たずえば次のようなやや耇雑な匏で䞎えられたす。





この堎合、むンタヌネット䞊で䞊蚘のアルゎリズムの実装を芋぀けるこずはできたせんが、行列を环乗するアルゎリズムを手動で芋぀けるにはかなりの時間がかかりたす。 それにもかかわらず、゜リュヌションを「額」ずしお蚘述し、デコレヌタを適甚するこずができたす。そうすれば、コンピュヌタヌは簡単なアルゎリズムを思い付きたす。

 from cpmoptimize import cpmoptimize @cpmoptimize() def f(n): prev3 = 0 prev2 = 1 prev1 = 1 for i in xrange(3, n + 3): cur = 7 * (2 * prev1 + 3 * prev2) + 4 * prev3 + 5 * i + 6 prev3, prev2, prev1 = prev2, prev1, cur return prev3
      
      





䟋3 等比数列の合蚈



等比数列のcountメンバヌの合蚈を蚈算する必芁があるずしたす。最初のメンバヌstartず分母coeffが䞎えられたす。 このタスクでは、デコレヌタは簡単な゜リュヌションを最適化できたす。

 from cpmoptimize import cpmoptimize @cpmoptimize() def geom_sum(start, coeff, count): res = 0 elem = start for i in xrange(count): res += elem elem *= coeff return res
      
      





グラフ








䟋番号4。 べき乗



数aを非負の敎数nに环乗する必芁があるずしたす。 このようなタスクでは、デコレヌタは実際に簡単な解決策をバむナリべき乗アルゎリズムに眮き換えたす。

 from cpmoptimize import cpmoptimize @cpmoptimize() def power(a, n): res = 1 for i in xrange(n): res *= a return res
      
      





グラフ








ダりンロヌドリンク



pipを䜿甚しおラむブラリをむンストヌルできたす。

 sudo pip install cpmoptimize
      
      





サンプル付きのラむブラリの゜ヌスコヌドは、無料のMITラむセンスの䞋でgithubで入手できたす。



UPD。 パッケヌゞはPython Package Indexで公開されおいたす。



最適化の䟋は、「 tests / 」フォルダヌにありたす。 各䟋は、「盎接的な」゜リュヌション、最適化されたバヌゞョンを衚す関数、および特定の問題に察するよく知られた耇雑だが迅速な゜リュヌションを衚す関数で構成されおいたす。 これらの関数は、さたざたなテストグルヌプで実行するスクリプトに配眮され、䜜業時間を枬定し、察応するテヌブルを構築したす。 matplotlibラむブラリがむンストヌルされおいる堎合、スクリプトは入力デヌタ通垞たたは察数に察する関数のランタむムの䟝存関係のグラフも䜜成し、それらを " tests / plots / "フォルダヌに保存したす。



ラむブラリの説明



ラむブラリを䜜成する際、Pythonむンタヌプリタヌが䜜成するプログラムのバむトコヌドを解析し、むンタヌプリタヌに干枉するこずなく実行時に倉曎できるずいう事実を利甚したした。これにより、蚀語を拡匵する可胜性が広がりたすたずえば、 gotoコンストラクトの実装を参照。 通垞、Pythonに組み蟌たれおいる長い挔算を䜿甚するず、説明した方法の利点が珟れたす。



これらの理由から、説明した最適化をPythonで実装するこずは、私にずっおより興味深いタスクになりたしたが、もちろん、C ++コンパむラで䜿甚するず、さらに高速なプログラムを䜜成できたす。 さらに、デコレヌタに最適化されたPythonコヌドは、挞近的な動䜜が優れおいるため、C ++コヌドの額を远い越す傟向がありたす。



バむトコヌドを倉曎するずき、 byteplayモゞュヌルを䜿甚したした。 このモゞュヌルは、バむトコヌドを分解および組み立おるための䟿利なむンタヌフェむスを提䟛したす。 残念ながら、このモゞュヌルはただPython 3に移怍されおいないため、プロゞェクト党䜓をPython 2.7で実装したした。



cpmoptimizeラむブラリの名前は、 「マトリックスの電力を蚈算しお最適化する 」の略です。 この蚘事のボリュヌムでは、バむトコヌドの分析ず倉曎のプロセスを詳现に説明するこずはできたせんが、このラむブラリが提䟛する機胜ずその䜜業の基瀎ずなる原則に぀いお詳现に説明しようずしたす。



自分のxrange



cpmoptimize。 xrange  停止 

cpmoptimize。 xrange  start 、 stop [、 step ]



最適化のためのPython 2の組み蟌みxrange型は、匕数ずしおlong型のlong敎数をサポヌトしおいたせん。

 >>> xrange(10 ** 1000) Traceback (most recent call last): File "<stdin>", line 1, in <module> OverflowError: Python int too large to convert to C long
      
      





ラむブラリを䜿甚する堎合、順序の操䜜の数で埪環するため 1秒未満で実行でき、プログラムで䞀般的になりたす。ラむブラリは、玔粋なPythonでxrange型の独自の実装を提䟛したすラむブラリ内ではCPMRangeず呌ばれたす 。 通垞のxrangeのすべおの機胜ず、さらにlong型の匕数をサポヌトしたす。 このようなコヌドぱラヌにならず、正しい結果をすばやく蚈算したす。

 from cpmoptimize import cpmoptimize, xrange @cpmoptimize() def f(): res = 0 for i in xrange(10 ** 1000): res += 3 return res print f()
      
      





それでも、あなたの堎合、サむクルの反埩回数が少ない堎合、デコレヌタで組み蟌みのxrangeを䜿い続けるこずができたす。



Cpmoptimize関数



cpmoptimize。 cpmoptimize  strict = False、 iters_limit = 5000、 types =int、long、 opt_min_rows = True、 opt_clear_stack = True



デコレヌタアプリケヌション


cpmoptimize関数は、実行された最適化のパラメヌタヌを受け入れ、これらのパラメヌタヌを考慮したデコレヌタヌを返したす。デコレヌタヌは、最適化された関数に適甚する必芁がありたす。 これは、特別な構文「犬」蚘号の有無にかかわらずを䜿甚しお実行できたす。

 from cpmoptimize import cpmoptimize @cpmoptimize() def f(n): #   def naive_g(n): #   smart_g = cpmoptimize()(naive_g)
      
      





バむトコヌドを倉曎する前に、元の関数がコピヌされたす。 したがっお、䞊蚘のコヌドでは、関数fずsmart_gは最終的に最適化されたすが、 naive_gは最適化されたせん。



ルヌプを怜玢する


デコレヌタは、関数のバむトコヌドで暙準のルヌプを探したすが、 whileルヌプずゞェネレヌタヌおよびリスト匏内のforルヌプは無芖されたす。 ネストされたルヌプはただサポヌトされおいたせん最も内偎のルヌプのみが最適化されおいたす。 その他の構造は正しく凊理されたす。



蚱可されたタむプ


デコレヌタは、ルヌプで䜿甚される倉数のタむプに関係なく、ルヌプを最適化できたせん。 実際、たずえばPythonでは、乗算ごずに文字列をファむルに曞き蟌むタむプを決定できたす。 最適化を適甚した結果、関数で行われた乗算の数が倉わる可胜性がありたす。 このため、ファむルの行数が異なり、最適化によりプログラムが䞭断されたす。



さらに、行列を䜿甚した操䜜では、倉数が暗黙的に远加および乗算されるため、倉数のタむプを「混圚させる」こずができたす。 定数たたは倉数のいずれかがfloat型の堎合、コヌドの実行埌にint型になっおいるはずの倉数もfloat型になる可胜性がありたす intずfloatを远加するずfloatが返されるため 。 このような動䜜は、プログラムで゚ラヌを匕き起こす可胜性があり、受け入れられたせん。



䞍芁な副䜜甚を避けるため、デコレヌタは、ルヌプで䜿甚されるすべおの定数ず倉数が有効なタむプである堎合にのみルヌプを最適化したす。 初期の有効な型はintおよびlong 、およびそれらから継承された型です。 定数たたは倉数のいずれかがlong型の堎合、コヌドの実行埌にint型になっおいるはずの倉数もlong型にするこずができたす。 ただし、Pythonのintずlongは十分に互換性があるため、゚ラヌに぀ながるこずはありたせん。



有効なタむプのセットを倉曎する堎合たずえば、 floatのサポヌトを远加する堎合、 typesパラメヌタヌで必芁なタむプのタプルを枡す必芁がありたす。 このタプルの䞀郚であるか、このタプルの䞀郚である型から継承された型は有効ず芋なされたす。 実際、 倀に有効な型があるかどうかの確認は、匏isinstancevalue、typesを䜿甚しお実行されたす。



サむクル最適化条件


芋぀かった各サむクルは、倚くの条件を満たす必芁がありたす。 それらのいく぀かは、デコレヌタを䜿甚するずきにすでにチェックされおいたす。

  1. ルヌプの本䜓は、代入呜什、単項および二項挔算のみで構成され、それらを組み合わせお耇雑な匏にするこずができたす。 条件、他の関数の呌び出し、 returnおよびyieldステヌトメントなどを含めるこずはできたせん。
  2. オペランドには、 予枬可胜性の条件が必芁な堎合がありたす。サむクルの各反埩で、それらの倀は同じでなければならず、前の反埩での蚈算の結果に䟝存しおはなりたせん。 この堎合

    • 単項マむナスのオペランドず同様に、加算および枛算のすべおのオペランドは、予枬䞍胜である可胜性がありたす。
    • 乗算の少なくずも1぀のオペランドは予枬可胜でなければなりたせん元のむンタヌプリタヌでは定数による乗算のみがサポヌトされおいたずいう事実に䌌おいたす。
    • べき乗、陀算、剰䜙挔算、およびビット単䜍挔算のすべおのオペランドは予枬可胜でなければなりたせん。
  3. ルヌプで䜿甚されるすべおの定数は、有効なタむプである必芁がありたす。


これらの条件が満たされるず、サむクルの前に「トラップ」が蚭定され、条件の別の郚分がチェックされたすが、サむクルの元のバむトコヌドは削陀されたせん。 Pythonは動的型付けを䜿甚するため、次の条件はルヌプを開始する盎前にのみチェックできたす。

  1. ルヌプが通過するオブゞェクトには、このcpmoptimize.xrangeラむブラリの暙準xrange型たたはその類䌌䜓が必芁です。 ただし、リストを返す䜎速範囲関数はサポヌトされおいたせん。
  2. ルヌプで䜿甚されるすべおの倉数の倀は有効なタむプです。


「トラップ」が最適化が蚱容可胜であるず結論付けた堎合、必芁なマトリックスが蚈算され、䜿甚される倉数の倀が新しいものに倉曎されたす。 最適化を実行できない堎合、ルヌプの元のバむトコヌドが起動されたす。



サむクルごずの匏ずコヌドの削陀


説明されおいる方法はべき乗ずビット単䜍のAND挔算をサポヌトしおいないずいう事実にもかかわらず、次のコヌドが最適化されたす。

 @cpmoptimize() def f(n, k): res = 0 for i in xrange(n): m = 3 res += ((k ** m) & 676) + i return res
      
      





バむトコヌドを分析するずき、デコレヌタは、匏k ** mおよび676の倉数kおよびmの倀は、それらが䜿甚されるサむクルの反埩に䟝存せず、したがっお匏党䜓の倀は倉化しないず結論付けたす。 各反埩で蚈算する必芁はありたせん;この倀を䞀床蚈算すれば十分です。 したがっお、察応するバむトコヌド呜什を取り出しお、サむクルの開始盎前に実行し、サむクル内の既補の倀を眮き換えるこずができたす。 コヌドは次のように倉換されたす。

 @cpmoptimize() def f(n, k): res = 0 m = 3 _const1 = (k ** m) & 676 for i in xrange(n): res += _const1 + i return res
      
      





( loop-invariant code motion ). , , ( _const1 k ). .



. , « » , .



デコレヌタは、耇数の割り圓おも郚分的にサポヌトしたす。芁玠がほずんどない堎合、Pythonはタプルを䜿甚せずにデコレヌタでサポヌトされおいるバむトコヌドを䜜成したす。

 a, b = b, a + b
      
      





しきい倀iters_limit


䞊蚘のグラフでは、反埩回数が少ない堎合、最適化されたサむクルの動䜜が通垞よりも遅くなるこずがありたす。この堎合、行列ず型チェックの構築にただ時間がかかるためです。関数が可胜な限り高速で、少数の反埩で動䜜する必芁がある堎合、iters_limitパラメヌタヌを䜿甚しお明瀺的にしきい倀を蚭定できたす。次に、最適化を開始する前の「トラップ」は、ルヌプが完了しなければならない反埩回数をチェックし、この数が指定されたしきい倀を超えない堎合、最適化をキャンセルしたす。



最初に、しきい倀は5000回の反埩に蚭定されたす。 2回の反埩より䜎く蚭定するこずはできたせん。



最も奜たしいしきい倀は、関数の最適化されたバヌゞョンず初期バヌゞョンの動䜜時間に察応する線グラフ䞊の亀点です。しきい倀がこのように正確に蚭定されおいる堎合、この堎合、関数は最速のアルゎリズムを遞択できたす最適化たたは初期。









厳栌なフラグ


堎合によっおは、最適化が必須です。そのため、最適化せずに反埩のルヌプ内にある堎合、プログラムは単にフリヌズしたす。この堎合、strictパラメヌタヌが提䟛されたす。最初は、その倀はFalseですが、Trueに蚭定されおいる堎合、暙準forルヌプの1぀が最適化されおいないず、䟋倖がスロヌされたす。



デコレヌタを適甚する段階で最適化が適甚できないずいう事実が怜出された堎合、䟋倖cpmoptimize.recompiler.RecompilationErrorがすぐに発生したす。ルヌプの䟋では、2぀の予枬䞍胜な倉数が乗算されたす。

 >>> from cpmoptimize import cpmoptimize >>> >>> @cpmoptimize(strict=True) ... def f(n, k): ... res = 0 ... for i in xrange(n): ... res += i * res ... return res ... Traceback (most recent call last): File "<stdin>", line 1, in <module> File "cpmoptimize/__init__.py", line 100, in upgrade_func index += analyze_loop(settings, code, index) + 1 File "cpmoptimize/__init__.py", line 59, in analyze_loop raise err cpmoptimize.recompiler.RecompileError: Multiplication of two unpredictable values is unsupported
      
      





ルヌプの実行前にその最適化が適甚できないこずが怜出された堎合぀たり、反埩子の倀たたは倉数のタむプがサポヌトされおいないこずが刀明した堎合、TypeError䟋倖がスロヌされたす。

 >>> from cpmoptimize import cpmoptimize >>> >>> @cpmoptimize(strict=True) ... def f(iterable): ... res = 0 ... for elem in iterable: ... res += elem ... return res ... >>> f(xrange(30)) 435 >>> f(range(30)) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in f File "cpmoptimize/hook.py", line 170, in exec_loop raise err TypeError: Iterator in optimized loops must have type "xrange" instead of <type 'list'>
      
      





远加の最適化オプション


opt_min_rowsずopt_clear_stackの 2぀のフラグは、マトリックスを構築するずきに远加の最適化メ゜ッドを䜿甚する圹割を果たしたす。最初はオンになっおおり、オフにする機胜はデモ目的でのみ存圚したすおかげで、これらの方法の有効性を比范できたす。



最適化されたコヌドを実行するず、生成されたマトリックスの䞀郚のセルで長い時間の乗算が時間の倧半を占めたす。この時間のかかるプロセスず比范するず、他のすべおのセルの増殖に費やされる時間はごくわずかです。



匏を再コンパむルするプロセスで、デコレヌタは䞭間の仮想むンタプリタスタックの䜍眮を担圓する倉数。耇雑な蚈算の埌、他の実際の倉数にすでに栌玍されおいる長い数倀が残る堎合がありたす。これらの倀をもう䞀床保存する必芁はありたせんが、マトリックスのセルに残っおプログラムを倧幅に遅くしたす。マトリックスを乗算するず、これらの長い数倀を再床乗算する必芁があるためです。opt_clear_stackオプションは、そのような倉数をクリアする責任がありたす。䜿甚の最埌にそれらをれロに割り圓おるず、それらの長い倀は消えたす。



このオプションは、プログラムが非垞に倧きな数で動䜜する堎合に特に効果的です。このような数倀の䞍必芁な乗算を排陀するこずにより、プログラムを2倍以䞊高速化できたす。Opt_min_rows



オプション乗算する正方行列のサむズを最小化する必芁がありたす。未䜿甚および予枬可胜な倉数に察応する行は、行列から陀倖されたす。ルヌプ倉数自䜓が䜿甚されない堎合、正しい倀を維持する責任がある行も陀倖されたす。プログラムを䞭断させないために、サむクル埌、これらの倉数にはすべお正しい最終倀が割り圓おられたす。さらに、元の蚘事では、倉数のベクトルに単䞀性に等しい芁玠が远加されたした。この芁玠に察応する行が䜿甚されおいない堎合、それも陀倖されたす。



このオプションは、数倀がただそれほど長くなっおおらず、マトリックスの次元がプログラムの実行時間に倧きく貢献しおいる堎合、nがそれほど倧きくない堎合にプログラムをわずかに高速化できたす。



䞡方のオプションを同時に䜿甚するず、仮想倉数は予枬可胜なれロ倀を持ち始め、opt_min_rowsはさらに効率的に機胜したす。蚀い換えるず、䞡方のオプションの有効性は、それぞれのオプションの有効性よりも優れおいたす。



以䞋のグラフは、特定のオプションを無効にした堎合のフィボナッチ数を蚈算するためのプログラムの動䜜時間を瀺しおいたす「-m」はopt_min_rowsが無効なプログラム、「-c」はopt_clear_stackが無効なプログラム、「-mc」は䞡方のオプションがすぐに無効なプログラムです 









他に䜕を実装できたすか



䞀連の操䜜を倉曎する



以䞋の堎合、 このメ゜ッドはいく぀かの操䜜をサポヌトするず蚀いたす。



メ゜ッドがいく぀かの操䜜をサポヌトしおいるこずは既に知っおいたす。 。 確かに



珟圚、最適化デコレヌタに実装されおいるのはこれらの操䜜です。ただし、説明されおいる方法は、乗算ずべき乗のペアなどの他の操䜜のペアをサポヌトしおいるこずがわかりたす。



たずえば、繰り返し関係で指定されたシヌケンスのn番目の番号を芋぀けるこずがタスクの





堎合です。デコレヌタヌがいく぀かの操䜜をサポヌトしおいる堎合、倉数に別の倉数を掛けるこずができたすただし、倉数を远加するこずはできたせん。この堎合、この問題に察する次の簡単な解決策は、デコレヌタによっお加速できたす。

 def f(n): a = 1 b = 1 for i in xrange(n): a, b = b, a * b return a
      
      





メ゜ッドが操䜜のペアもサポヌトしおいるこずを蚌明できたす 、 そしお ここで、ビット単䜍のAND たたはビット単䜍のORです。正の数の堎合、操䜜のペアで䜜業できたす そしお 。



たずえば、操䜜のサポヌトを実装するには、倉数の倀ではなく、これらの倀の察数で操䜜するこずができたす。次に、倉数の乗算は察数の加算に眮き換えられ、倉数の定数の环乗は、察数に定数を乗算するこずにより行われたす。したがっお、我々は問題を操䜜で既に実珟されたケヌスに枛らしたす 。



指定された残りの操䜜ペアのサポヌトを実装する方法の説明には、䞀定の数の数孊蚈算が含たれおおり、別の蚘事に倀したす。珟時点では、操䜜のペアはある皋床互換性があるこずに蚀及するだけです。



入れ子ルヌプ



バむトコヌドでルヌプ分析アルゎリズムを改良するこずにより、ネストされたルヌプのサポヌトを実装しお、次のようなコヌドを最適化できたす。

 def f(): res = 0 for i in xrange(10 ** 50): for j in xrange(10 ** 100): res += i * 2 + j return res
      
      





予枬可胜な条件



次のコヌドでは、ルヌプ本䜓の条件は予枬可胜です。サむクルを開始する前に、それが正しいかどうかを確認し、実行されおいないコヌドブランチを削陀できたす。結果のコヌドは最適化できたす

 def f(mode): res = 0 for i in xrange(10 ** 50): if mode == 1: res += 3 else: res += 5 return res
      
      





結論



䞀般的に、Pythonむンタヌプリタヌは、䜜成した゜ヌスコヌドずほが正確に䞀臎する予枬可胜なバむトコヌドを生成したす。デフォルトでは、関数のむンラむン展開、ルヌプの展開、プログラムの動䜜の分析を必芁ずするその他の最適化は実行されたせん。バヌゞョン2.6以降のみ、CPythonは定数挔算匏を折りたたむこずを孊びたしたが、この機胜は垞に効率的に機胜するずは限りたせん。



この状況にはいく぀かの理由がありたす。第䞀に、Pythonの䞀般的な堎合のコヌドの分析は困難です。これは、デヌタ型はプログラムの実行䞭にしか远跡できないためですこの堎合はこれを行いたす。第二に、原則ずしお、最適化ではただPythonが玔粋にコンパむルされた蚀語の速床を達成できないため、プログラムを非垞に迅速に動䜜させる必芁がある堎合は、単玔に䜎レベル蚀語で蚘述するこずをお勧めしたす。



ただし、Pythonは柔軟な蚀語であるため、必芁に応じおむンタヌプリタヌに干枉するこずなく、倚くの最適化メ゜ッドを自分で実装できたす。このラむブラリず、以䞋にリストされおいる他のプロゞェクトは、この機䌚をよく瀺しおいたす。



UPD。プロゞェクトの説明は、SlideShareのプレれンテヌションずしおも利甚できたす。



こちらもご芧ください



以䞋に、Pythonでプログラムの実行を高速化するためのいく぀かの他の興味深いプロゞェクトぞのリンクを瀺したす。

  1. PyPyは、JITコンパむルをサポヌトする代替のPythonむンタヌプリタヌです。これにより、原則ずしお、Pythonプログラムをより高速に実行できたす。PyPy自䜓はRPythonで蚘述されおおり、Cコヌドぞのトランスレヌタヌは特別に蚭蚈されおいたす。
  2. Pystonは、コヌドをLLVMツヌルを䜿甚しお最適化し、JITコンパむルを䜿甚しお実行できる䞭間LLVM衚珟に倉換する、新しい代替Pythonむンタヌプリタヌです。
  3. Nuitka — Python. py2exe, *.exe-, , , Nuitka Python .
  4. WPython — CPython 2.6.4 -, .
  5. astoptimizer — , - .
  6. promise — , -, «». , , .
  7. foldy.py — , - ( constant folding ), , .
  8. , - ( constant binding ), .


UPD1。ナヌザヌマゞックは、いく぀かのリンクを瀺唆したした。



UPD2。DuneRatナヌザヌは、他のいく぀かのPythonコンパむルプロゞェクトを瀺したしたshedskin、hope、pythran。



All Articles