Concurrency-in-python-benchmarking-and-profiling

提供:Dev Guides
移動先:案内検索

ベンチマークとプロファイリング

この章では、ベンチマークとプロファイリングがパフォーマンスの問題にどのように役立つかを学習します。

コードを作成して、目的の結果が得られたとしても、ニーズが変わったためにこのコードを少し速く実行したい場合はどうでしょうか。 この場合、コードのどの部分がプログラム全体を遅くしているのかを知る必要があります。 この場合、ベンチマークとプロファイリングが役立ちます。

ベンチマークとは何ですか?

ベンチマークの目的は、標準と比較して何かを評価することです。 ただし、ここで生じる問題は、ベンチマークとなるものと、ソフトウェアプログラミングの場合にベンチマークが必要な理由です。 コードのベンチマークとは、コードの実行速度とボトルネックの場所を意味します。 ベンチマークの主な理由の1つは、コードを最適化することです。

ベンチマークはどのように機能しますか?

ベンチマークの仕組みについて話す場合、プログラム全体を1つの現在の状態としてベンチマークすることから始めてから、マイクロベンチマークを組み合わせて、プログラムをより小さなプログラムに分解することができます。 プログラム内のボトルネックを見つけて最適化するため。 言い換えれば、大きくて難しい問題を、それらを最適化するための一連の小さくて少し簡単な問題に分割することとして理解できます。

ベンチマーク用のPythonモジュール

Pythonには、 timeit と呼ばれるベンチマーク用のデフォルトモジュールがあります。 timeit モジュールの助けを借りて、メインプログラム内の小さなPythonコードのパフォーマンスを測定できます。

次のPythonスクリプトでは、 timeit モジュールをインポートしています。このモジュールは、2つの関数( functionA および functionB )の実行にかかる時間をさらに測定します-

import timeit
import time
def functionA():
   print("Function A starts the execution:")
   print("Function A completes the execution:")
def functionB():
   print("Function B starts the execution")
   print("Function B completes the execution")
start_time = timeit.default_timer()
functionA()
print(timeit.default_timer() - start_time)
start_time = timeit.default_timer()
functionB()
print(timeit.default_timer() - start_time)

上記のスクリプトを実行した後、次のように両方の関数の実行時間を取得します。

出力

Function A starts the execution:
Function A completes the execution:
0.0014599495514175942
Function B starts the execution
Function B completes the execution
0.0017024724827479076

デコレータ関数を使用して独自のタイマーを作成する

Pythonでは、 timeit モジュールのように動作する独自のタイマーを作成できます。 decorator 関数を使用して実行できます。 以下は、カスタムタイマーの例です-

import random
import time

def timer_func(func):

   def function_timer(*args, **kwargs):
   start = time.time()
   value = func(*args, **kwargs)
   end = time.time()
   runtime = end - start
   msg = "{func} took {time} seconds to complete its execution."
      print(msg.format(func = func.__name__,time = runtime))
   return value
   return function_timer

@timer_func
def Myfunction():
   for x in range(5):
   sleep_time = random.choice(range(1,3))
   time.sleep(sleep_time)

if __name__ == '__main__':
   Myfunction()

上記のPythonスクリプトは、ランダム時間モジュールのインポートに役立ちます。 timer_func()デコレータ関数を作成しました。 これには、その中にfunction_timer()関数があります。 これで、ネストされた関数は、渡された関数を呼び出す前に時間を取得します。 次に、関数が戻るまで待機し、終了時間を取得します。 このようにして、最終的にpythonスクリプトに実行時間を出力させることができます。 スクリプトは、次のように出力を生成します。

出力

Myfunction took 8.000457763671875 seconds to complete its execution.

プロファイリングとは

プログラマーは、メモリの使用、時間の複雑さ、またはプログラムに関する特定の命令の使用などの属性を測定して、そのプログラムの実際の能力を測定したいことがあります。 このようなプログラムに関する測定は、プロファイリングと呼ばれます。 プロファイリングでは、動的プログラム分析を使用してこのような測定を行います。

後続のセクションでは、プロファイリング用のさまざまなPythonモジュールについて学習します。

cProfile –組み込みモジュール

*cProfile* は、プロファイリング用のPython組み込みモジュールです。 このモジュールは、妥当なオーバーヘッドを持つC拡張機能であり、長時間実行されるプログラムのプロファイリングに適しています。 実行後、すべての機能と実行時間が記録されます。 それは非常に強力ですが、時には解釈して行動するのが少し難しいです。 次の例では、以下のコードでcProfileを使用しています-

def increment_global():

   global x
   x += 1

def taskofThread(lock):

   for _ in range(50000):
   lock.acquire()
   increment_global()
   lock.release()

def main():
   global x
   x = 0

   lock = threading.Lock()

   t1 = threading.Thread(target=taskofThread, args=(lock,))
   t2 = threading.Thread(target= taskofThread, args=(lock,))

   t1.start()
   t2.start()

   t1.join()
   t2.join()

if __name__ == "__main__":
   for i in range(5):
      main()
   print("x = {1} after Iteration {0}".format(i,x))

上記のコードは thread_increment.py ファイルに保存されます。 今、次のようにコマンドラインでcProfileでコードを実行します-

(base) D:\ProgramData>python -m cProfile thread_increment.py
x = 100000 after Iteration 0
x = 100000 after Iteration 1
x = 100000 after Iteration 2
x = 100000 after Iteration 3
x = 100000 after Iteration 4
      3577 function calls (3522 primitive calls) in 1.688 seconds

   Ordered by: standard name

   ncalls tottime percall cumtime percall filename:lineno(function)

   5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:103(release)
   5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:143(__init__)
   5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:147(__enter__)
   … … … …

上記の出力から、cProfileは呼び出されたすべての3577関数を、それぞれに費やした時間と呼び出された回数とともに出力することが明らかです。 以下は、出力で得られた列です-

  • ncalls -これは行われた呼び出しの数です。
  • tottime -指定された関数で費やされた合計時間です。
  • percall -tottimeをncallsで割った商を指します。
  • cumtime -これは、このサブ機能およびすべてのサブ機能で費やされた累積時間です。 再帰関数に対しても正確です。
  • percall -cumtimeをプリミティブコールで割った商です。
  • * filename:lineno(function)*-基本的に各関数のそれぞれのデータを提供します。