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

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

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

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

ベンチマークは、標準と比較して何かを評価することを目的としています。ただし、ここで発生する問題は、ベンチマークとは何か、ソフトウェアプログラミングの場合にベンチマークが必要な理由です。コードのベンチマークとは、コードの実行速度とボトルネックの場所を意味します。ベンチマークの主な理由の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をncallで割った商を指します。

  • cumtime−これは、このサブ機能とすべてのサブ機能に費やされた累積時間です。再帰関数に対しても正確です。

  • percall −これはcumtimeをプリミティブ呼び出しで割った商です。

  • filename:lineno(function) −基本的に各機能のそれぞれのデータを提供します。