Pythonのプロファむリングずデバッグ、緎習に移りたしょう

前回の蚘事では、プロファむリングず最適化の抂念を定矩し、プロファむリングずツヌルの皮類に察するさたざたなアプロヌチに粟通したした。 プロファむラヌの歎史に少し觊れたした。



本日は、実践に移るこずを提案し、手動プロファむリング手法および「泚芖手法」の䟋を瀺したす。





トレヌニング



䟋やトレヌニングなしで実践できるこずは䜕ですか 私は長い間、どのようなプロゞェクトを教育ツヌルずしお採甚する䟡倀があるかを考えおいたした。 私の意芋では、これはプロゞェクトオむラヌ -数孊およびコンピュヌタヌパズルのコレクションです。提案された問題を解決するには数倀アルゎリズムを䜿甚する必芁があり、最適化せずに答えを探すのは䜕幎も続くこずがあるからです。



䞀般に、オむラヌプロゞェクトのパズルを解くこずを匷くお勧めしたす。 気を散らし、瞑想し、リラックスし、同時に脳を良い状態に保぀のに圹立ちたす。



蚘事の䟋ずしお、 タスク3を取りたす 。

13195の玠数は5、7、13、および29です。

玠数である600851475143の最倧の陀数は䜕ですか


「額」ずいう簡単な゜リュヌションを䜜成したす。

"""Project Euler problem 3 solve""" from __future__ import print_function import sys def is_prime(num): """Checks if num is prime number""" for i in range(2, num): if not num % i: return False return True def find_prime_factors(num): """Find prime factors of num""" result = [] for i in range(2, num): if is_prime(i) and not num % i: result.append(i) return result if __name__ == '__main__': try: num = int(sys.argv[1]) except (TypeError, ValueError, IndexError): sys.exit("Usage: euler_3.py number") if num < 1: sys.exit("Error: number must be greater than zero") prime_factors = find_prime_factors(num) if len(prime_factors) == 0: print("Can't find prime factors of %d" % num) else: print("Answer: %d" % prime_factors[-1])
      
      





気配りのある、たたは経隓豊富な読者は、すぐに私をnoobず呌び、このプログラムの問題に名前を付け、その最適化のためのいく぀かのレシピを䞎えるかもしれたせんが、特別な簡単な䟋を取り䞊げお、このコヌドが良いず考えおいるふりをしお、途䞭で最適化したす。



確認するには、陀数がわかっおいる数字を䜿甚しおプログラムを実行したす。



rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python euler_3.py 13195

回答29


プログラムによっお発行された回答29は正しいです問題の説明に蚘茉されおいたす。 やった 次に、興味のある番号600851475143でプログラムを実行したす。

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python euler_3.py 600851475143


そしお...䜕も起こりたせん。 CPU䜿甚率は100で、プログラムは数時間操䜜した埌でも完了しおいたせん。 私たちは理解し始めたす。 正しく動䜜するコヌドのみを最適化できるこずを思い出したすが、プログラムを少数で確認し、答えは正しかったこずを思い出したす。 明らかに、問題はパフォヌマンスにあり、最適化を開始する必芁がありたす。



芖線法



実際には、匕数13195でプログラムのプロファむルを䜜成したす動䜜時間が適切な堎合。 しかし、私たちはここで、そしお䞀般的に6月のためにトレヌニングしおいるので、「泚芖法」を䜿甚したす。



コヌドを開き、泚意深く調べたす。 数Nの玄数を怜玢するために、間隔sqrtN+ 1 ... N-1から数を敎理するこずは意味がないこずを理解したす幞運ならすぐに理解したす 。 間隔2 ... sqrtNの数倀を反埩凊理するずきに、この範囲のすべおの陀数が既に芋぀かりたした。 コヌドを少し倉曎したす9行目ず18​​行目を参照。

 """Project Euler problem 3 solve""" from __future__ import print_function import math import sys def is_prime(num): """Checks if num is prime number""" for i in range(2, int(math.sqrt(num)) + 1): if not num % i: return False return True def find_prime_factors(num): """Find prime factors of num""" result = [] for i in range(1, int(math.sqrt(num)) + 1): if is_prime(i) and not num % i: result.append(i) if is_prime(num): result.append(i) return result if __name__ == '__main__': try: num = int(sys.argv[1]) except (TypeError, ValueError, IndexError): sys.exit("Usage: euler_3.py number") if num < 1: sys.exit("Error: number must be greater than zero") prime_factors = find_prime_factors(num) if len(prime_factors) == 0: print("Can't find prime factors of %d" % num) else: print("Answer: %d" % prime_factors[-1])
      
      





確認するには、陀数がわかっおいる数でプログラムを再床実行したす。



rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python euler_3.py 13195

回答29


䞻芳的には、プログラムははるかに速く動䜜したした。぀たり、興味のある番号600851475143でプログラムを再び起動するこずを意味したす。

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python euler_3.py 600851475143

回答6857


私たちはサむトで答えを確認し、正しいこずが刀明し、問題が解決され、道埳的な満足を感じおいたす。



プログラムは蚱容可胜な時間1分未満で完了したした。答えは正しかったので、この特定のケヌスでさらに最適化する意味はありたせん。 タスクを解決したした。 芚えおいるように、最適化で最も重芁なこずは、時間内に停止できるこずです。



はい、Project Eulerのメンバヌがパブリックドメむンで回答ず゜リュヌションを共有しないように求めおいるこずは承知しおいたす。 しかし、タスク3の答えはグヌグルですたずえば、「プロゞェクトオむラヌ問題3の答え」ずいう条件で䞀床に答えるので、答えを曞いおも倧䞈倫だず思いたす。



手動プロファむリング



䜕が䜕であるかをすばやく把握する最も䞀般的な方法の1぀。 最も基本的なケヌスでは、unixナヌティリティ「time」を䜿甚するず、次のようになりたす最適化前。

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜時間python euler_3.py 13195

回答29

python euler_3.py 13195 3.83sナヌザヌ0.03sシステム99CPU 3.877合蚈


最適化埌

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜時間python euler_3.py 13195

回答29

python euler_3.py 13195 0.03sナヌザヌ0.02sシステム90cpu 0.061合蚈


ほが65倍の加速〜3.87秒から〜61ミリ秒



手動プロファむリングも次のようになりたす。

 import time ... start = time.time() prime_factors = find_prime_factors(num) print("Time: %.03f s" % (time.time() - start))
      
      





結果

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python euler_3.py 600851475143

回答6857

時間19.811秒


たたは、小さなプログラムのパフォヌマンスを枬定するために蚭蚈された特別な「 timeit 」モゞュヌルを䜿甚したす。 適甚䟋

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python -m timeit -n 10 -s'import euler_3 '' euler_3.find_prime_factors600851475143 '

10ルヌプ、最高3ルヌプあたり21.3秒


手動プロファむリングはい぀適甚できたすか たず、これは開発者間でさたざたな皮類の競争を行うのに最適な方法です「私のコヌドはあなたのものよりも速くなったので、私は優れたプログラマヌです」、そしおそれは良いこずです。 第二に、プログラムの速床を決定するために「目で」必芁な堎合20秒長いたたは改善の結果を取埗するコヌドを100倍高速化する。



しかし、最も重芁なアプリケヌションは、コヌドの実行時の統蚈情報を実皌働環境でほがリアルタむムで収集するこずです。 これを行うには、メトリックを収集しおグラフを描画するために、枬定された時間を任意のシステムに送信したす GraphiteずStatsDをグラファむトの アグリゲヌタヌずしお䜿甚するのが倧奜きです。



これを行うには、単玔なコンテキストマネヌゞャヌを䜿甚できたす。

 """Collect profiling statistic into graphite""" import socket import time CARBON_SERVER = '127.0.0.1' CARBON_PORT = 2003 class Stats(object): """Context manager for send stats to graphite""" def __init__(self, name): self.name = name def __enter__(self): self.start = time.time() return self def __exit__(self, *args): duration = (time.time() - self.start) * 1000 # msec message = '%s %d %d\n' % (self.name, duration, time.time()) sock = socket.socket() sock.connect((CARBON_SERVER, CARBON_PORT)) sock.sendall(message) sock.close()
      
      





その䜿甚䟋

 from python_profiling.context_managers import Stats ... with Stats('project.application.some_action'): do_some_action()
      
      





たたは、シンプルなデコレヌタ

 """Collect profiling statistic into graphite""" import socket import time CARBON_SERVER = '127.0.0.1' CARBON_PORT = 2003 def stats(name): """Decorator for send stats to graphite""" def _timing(func): def _wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) duration = (time.time() - start) * 1000 # msec message = '%s %d %d\n' % (name, duration, time.time()) sock = socket.socket() sock.connect((CARBON_SERVER, CARBON_PORT)) sock.sendall(message) sock.close() return result return _wrapper return _timing
      
      





デコレヌタの䜿甚䟋

 from python_profiling.decorators import stats ... @stats('project.application.some_action') def do_some_action(): """Doing some useful action"""
      
      





出力では、たずえば次のように、興味のあるコヌドセクションのランタむムグラフを取埗したす。



これは、コヌドがラむブサヌバヌでどのように感じられるかを垞に瀺し、最適化するずきが来たした。 次のリリヌス埌にコヌドがどのように感じられるかを芋るこずができたす。 リファクタリングたたは最適化が実行された堎合、スケゞュヌルを䜿甚するず、結果をタむムリヌに評䟡し、状況党䜓が改善たたは悪化したかどうかを理解できたす。



この方法には欠点があり、最も重芁なのは入力デヌタぞの䟝存性の欠劂です。 したがっお、玠数「is_prime」を決定するための関数では、実行時間はこの数の倀に匷く䟝存し、この関数がプロゞェクトで頻繁に呌び出されるず、スケゞュヌルは完党に無意味になりたす。 どのアプロヌチを䜿甚できるのか、そしおどのようなアプロヌチを採甚しおいるのかを明確に理解するこずが重芁です。



もちろん、プログラムの統蚈を収集するため、グラファむトのメトリックの収集を通じおプロファむリングメ゜ッドを呌び出すこずができたす。 しかし、最初の郚分で採甚した甚語を遵守するこずを提案したす。「統蚈的」プロファむリングずは、特定の間隔での情報の収集サンプリングを意味したす。



ゞャンゎstatsd



グラファむトずそのための集玄サヌバヌ StatsD を䜿甚する堎合、1぀のメトリックに察しお、耇数のグラフを䞀床に取埗したす最小および最倧コヌド実行時間、および単䜍時間あたりの蚘録された読み取り倀関数呌び出しの䞭倮倀ず数。これは非垞に䟿利です。 StatsDをDjangoに接続するのがどれほど簡単か芋おみたしょう。



モゞュヌルを配眮したす。

➜pip install django-statsd-mozilla


settings.pyに蚭定を远加したすアプリケヌションずミドルりェア

 INSTALLED_APPS += ('django_statsd',) MIDDLEWARE_CLASSES += ( 'django_statsd.middleware.GraphiteRequestTimingMiddleware', 'django_statsd.middleware.GraphiteMiddleware', ) # send DB timings STATSD_PATCHES = ['django_statsd.patches.db']
      
      





それだけです 出力では、次のグラフを取埗したす。





StatsDの長所ず短所

+むンストヌルず䜿甚が簡単

+生産に適しおいたす䞀般的に必須

-少しの情報量/時間

-グラファむトずstatsdが必芁必須



統蚈プロファむラヌ



むベントプロファむラヌずは異なり、統蚈プロファむリング甚のツヌルはほずんどありたせん。 次の3぀に぀いおお話したす。





スタットプロフ



おそらく、Pythonで最も有名な統蚈プロファむラヌはstatprofです。 私たちは眮きたす

➜pip install statprof


たずえば、䜿甚したす。 このように

 import statprof ... statprof.start() try: do_some_action() finally: statprof.stop() statprof.display()
      
      





たたはコンテキストマネヌゞャヌずしおpypiのバヌゞョン0.1.2ではなく、 リポゞトリのバヌゞョンでのみ

 import statprof ... with statprof.profile(): do_some_action()
      
      





コヌドのプロファむルを䜜成しおみたしょう。

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python euler_3.py 600851475143

环積自己比率

時間秒秒名前

44.42 8.63 8.63 euler_3.py:12:is_prime

37.12 7.21 7.21 euler_3.py:11:is_prime

16.90 19.40 3.28 euler_3.py:21:find_prime_factors

0.95 0.18 0.18 euler_3.py:9:is_prime

0.48 0.09 0.09 euler_3.py:13:is_prime

0.06 0.01 0.01 euler_3.py:14:is_prime

0.06 0.01 0.01 euler_3.py:20:find_prime_factors

0.03 0.01 0.01 euler_3.py:23:find_prime_factors

0.00 19.42 0.00 euler_3.py:37<モゞュヌル>

-サンプル数3575

合蚈時間19.420000秒

回答6857


プログラムには2぀のホットスポットがありたす。 最初is_prime関数の12行目ず11行論理的、それらの実行にはプログラムの時間の玄82がかかり、2番目find_prime_factors関数の21行目玄17の時間

 if is_prime(i) and not num % i:
      
      





is_primeプログラムの最もホットな関数が呌び出されるのは、この行からです。 条件のオペランドを倉曎するだけで、プログラムを倧幅に高速化できたす。 陀算の䜙りnumiを取埗する操䜜は、関数「is_prime」よりも高速です。同時に、ある数倀を別の数倀で陀算したずきの䜙りがれロではなく、「not numi」はFalseを返したす。 したがっお、is_prime関数の呌び出し回数を倧幅に削枛したす。

 if not num % i and is_prime(i):
      
      





プロファむリングを開始したす。

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python euler_3.py 600851475143

环積自己比率

時間秒秒名前

87.50 0.22 0.22 euler_3.py:21:find_prime_factors

5.00 0.01 0.01 euler_3.py:20:find_prime_factors

5.00 0.01 0.01 euler_3.py:11:is_prime

2.50 0.01 0.01 euler_3.py:23:find_prime_factors

0.00 0.25 0.00 euler_3.py:37<モゞュヌル>

-サンプル数40

合蚈時間0.250000秒

回答6857


プログラムの最もホットな堎所は、関数「find_prime_factors」の21行目です。぀たり、陀算の残りを取埗する操䜜「numi」です。 関数「is_prime」の呌び出し頻床ははるかに䜎くなり、プログラムの実行時間の5になりたした。 プログラムの実行時間は倧幅に短瞮され、番号600851475143の最倧単玔陀数はわずか0.25秒で芋぀かりたしたプログラムを玄80倍加速したした。



プロファむラヌ操䜜の粟床がどれだけ䜎䞋したかに泚目したしょう3575サンプル最適化前の䟋ではなく、40の枬定のみが行われ、5行のみで情報が受信されたした。 もちろん、これでは十分ではありたせん。 実際、これは統蚈プロファむラヌの機胜です。デヌタを収集する時間が長くなればなるほど、分析はより正確になりたす。 たずえば、プログラムを10〜100回実行した堎合、より正確な結果が埗られたす。



ここで少し䜙談をしなければなりたせん。
プロファむラヌやコヌドカバレッゞ分析ツヌルなどのすべおの補助ナヌティリティの99は、情報の最小単䜍ずしお文字列を凊理したす。 したがっお、たずえば次のように、できるだけコンパクトにコヌドを蚘述しようずするず、

 result = foo() if bar else baz()
      
      





プロファむラヌでは、どの関数が呌び出され、どの関数が呌び出されなかったかを確認できたせん。 カバレッゞレポヌトでは、このシナリオたたはそのシナリオがテストでカバヌされおいるかどうかはわかりたせん。 より芪しみやすく、䞀芋䟿利なコヌドにも関わらず、堎合によっおは次のように蚘述する方が適切です。

 if bar: result = foo() else: result = baz()
      
      





そしお、すぐにどの行が実行されないか、どの行が実行されるかおよび頻床を確認したす。 もちろん、単䞀行ずコンパクトさは優れおいお楜しいですが、堎合によっおは、より倚くの時間を費やしお、プロファむル、テスト、および保守が容易なコヌドを取埗した方が良いこずもありたす。





statprofの長所ず短所

+最小限のオヌバヌヘッド

+䜿いやすい

-実装は非垞に粗雑で実隓的です

-適切な結果を埗るには、長い分析が必芁です

-デヌタ出力が少ない



Django-live-profiler



泚目に倀する別のポむントは、 django-live-profiler -statprofを䜿甚するDjangoアプリケヌションプロファむラヌです。 動䜜させるには、たずzeromqをむンストヌルする必芁がありたす。

brew brew install zmq


モゞュヌル自䜓を配眮したす。

➜pip install django-live-profiler


そしお、アグリゲヌタヌを実行したす

➜集箄--host 127.0.0.1 --port 5556


次に、settings.pyにプロファむラヌを远加したす。

 #  application INSTALLED_APPS += ('profiler',) #  middleware MIDDLEWARE_CLASSES += ( 'profiler.middleware.ProfilerMiddleware', 'profiler.middleware.StatProfMiddleware', )
      
      





そしおurls.pyで

 url(r'^profiler/', include('profiler.urls'))
      
      





サヌバヌを起動したす。

➜python manage.py runserver --noreload --nothreading


ブラりザでプロファむラヌを開きたす 127.0.0.1 8000 / profiler /リアルタむムでラむブプロゞェクトのプロファむリングの結果を芳察するこずで人生を楜しみたす





たた、django-live-profilerはSQLク゚リを衚瀺できたす。





django-live-profilerの長所ず短所

+小さなオヌバヌヘッド

+本番環境に配眮できたす非垞に慎重に

+ SQLク゚リプロファむリング

-耇雑なむンストヌル、䟝存関係

-デヌタ出力が少ない



プロップ



もう1぀の統蚈プロファむラヌはplop Python Low-Overhead Profilerず呌ばれたす。 著者は、実装が粗雑であり、プロゞェクトが掻発に開発䞭であるこずを盎ちに譊告したす。 むンストヌルは簡単です

➜pip install plop tornado


プロファむリングを開始したす。

rudnyh @ work 〜/ work / python-profilingvenv python-profiling 

➜python -m plop.collector euler_3.py 600851475143

回答6857

/tmp/plop.outに保存されたプロファむル出力

オヌバヌヘッドはサンプルあたり5.89810884916e-050.00589810884916でした


サヌバヌを起動しお結果を衚瀺したす。

➜python -m plop.viewer --datadir = / tmp /


ブラりザでhttp// localhost8888 /ペヌゞを開き、結果を楜しんでください



PlopはDjangoアプリケヌションのプロファむルに䜿甚できたす。 これを行うには、django-plopパッケヌゞをむンストヌルしたす。

➜pip install django-plop


プロファむラヌに結果を配眮する堎所を指瀺するパラメヌタヌをsettings.pyミドルりェアに远加したす。

 MIDDLEWARE_CLASSES += ( 'django-plop.middleware.PlopMiddleware', ) PLOP_DIR = os.path.join(PROJECT_ROOT, 'plop')
      
      





倧芏暡なプロゞェクトでは、グラフはより印象的に芋えたす。



写真は非垞にサむケデリックであり、本栌的なプロファむリングツヌルず呌ぶこずは困難ですが、コヌルグラフがあるにもかかわらず、コヌドの最もホットなセクションが衚瀺され、オヌバヌヘッドは最小限著者によるず2のみ、堎合によっおはこのツヌルでセクションを怜出するのに十分ですプロファむリングが必芁なコヌド。 Dropboxサヌビスは本番環境で盎接plopを䜿甚したす。



plopの長所ず短所

+最小限のオヌバヌヘッド

+生産に投入可胜

-耇雑なむンストヌル、䟝存関係

-非垞に少ない出力



新しい遺物



統蚈プロファむラヌに぀いお蚀えば、プロファむリングだけでなく、サヌバヌずWebアプリケヌションおよびモバむルバヌゞョンの監芖を目的ずしたNew Relicサヌビスに぀いおも蚀うこずができたす。 垌望する人は䌚瀟のりェブサむトですべおの情報を芋るこずができ、無料でサヌビスを詊すこずができたす。 私は個人的にNew Relicでの䜜業を詊みたこずがないため、これに぀いおは説明したせん。たた、自分で詊したものに぀いおのみ話すこずに慣れおいたす。 プロファむラペヌゞでスクリヌンショットを衚瀺したす 。



長所ず短所

+本番甚

+倚くの異なる機胜プロファむリングだけでなく

-有料無料版がありたす

-デヌタは他の人のサヌバヌに送信されたす



次の蚘事では、 Pythonをプロファむルするための䞻芁なツヌルであるむベントプロファむラヌの研究に進みたす。 連絡を取り合いたしょう



All Articles