処理時間を記録するクラスを作る

NO IMAGE

プログラムの動作時間を計測するとき、処理のどのブロックで時間がかかっているのか知りたいことがあります。

しかし、そのたびに時間を計算する処理を書くのは非効率的です。

そこで、クラス定義の勉強もかねて、処理時間を計測するクラスを作ってみましょう。

要件

こんな風に記述できるクラスを作ってみましょう。

# 処理時間計測開始
rap = RapTime()

time.sleep(2)  # 2秒かかる処理

# 処理時間を出力
print(rap.rap("2秒かかる処理"))

time.sleep(5)  # 5秒かかる処理

# 処理時間を出力
print(rap.rap("5秒かかる処理"))

# 処理時間の記録を全て出力
print(rap.history)

こんな感じで、処理の要所要所でrap関数を実行することで、ラップタイムを計測するクラスを作ってみます。

実装

というわけで作ってみました。

import time

# 処理時間を記録するクラス
class RapTime():
    starttime = time.perf_counter()  # クラスが生成された時間
    history = []  # ラップタイムの履歴

    # ラップタイムを記録
    def rap(self, label):
        t = time.perf_counter()  # 呼ばれた瞬間の時刻を取得
        self.starttime, t = t, t - self.starttime  # timeを更新し、beforetimeにかかった時間を代入
        self.history.append([label, t])  # ラップタイムを記録
        return t  # かかった時間を返す

処理としては、クラス生成時に時刻を記録し、rapメソッドを呼び出すと直前の生成またはrap呼び出しからの時間を計算し、文字列と共に履歴に記録し、計算した経過時間を返します。

starttime変数は直前の時間、historyはrap関数が呼び出されるごとに経過時間を記録するリストです。

動作中に経過時間を見たい場合はrapをprintし、最後にまとめて見たい場合はhistroryをprintすればOKです。

実装例

これで最初の要件のプログラムを実際に動かしてみましょう。

import time

# 処理時間を記録するクラス
class RapTime():
    starttime = time.perf_counter()  # クラスが生成された時間
    history = []  # ラップタイムの履歴

    # コンストラクタ:初期化処理
    def __init__(self):
        pass

    # ラップタイムを記録
    def rap(self, label):
        t = time.perf_counter()  # 呼ばれた瞬間の時刻を取得
        self.starttime, t = t, t - self.starttime  # timeを更新し、beforetimeにかかった時間を代入
        self.history.append([label, t])  # ラップタイムを記録
        return t  # かかった時間を返す

# 処理時間計測開始
rap = RapTime()

time.sleep(2)  # 2秒かかる処理

# 処理時間を出力
print(rap.rap("2秒かかる処理"))

time.sleep(5)  # 5秒かかる処理

# 処理時間を出力
print(rap.rap("5秒かかる処理"))

# 処理時間の記録を全て出力
print(rap.history)

結果は次のように出力されました。

2.0548264
 5.0002797999999995
 [['2秒かかる処理', 2.0548264], ['5秒かかる処理', 5.0002797999999995]]

画像のモノクロ化の処理時間を計測するサンプルコード

では画像のモノクロ化処理の時間を計測してみましょう。

from PIL import Image
import numpy as np
import time

# 処理時間を記録するクラス
class RapTime():
    starttime = time.perf_counter()  # クラスが生成された時間
    history = []  # ラップタイムの履歴

    # コンストラクタ:初期化処理
    def __init__(self):
        pass

    # ラップタイムを記録
    def rap(self, label):
        t = time.perf_counter()  # 呼ばれた瞬間の時刻を取得
        self.starttime, t = t, t - self.starttime  # timeを更新し、beforetimeにかかった時間を代入
        self.history.append([label, t])  # ラップタイムを記録
        return t  # かかった時間を返す

# 処理時間計測開始
rap = RapTime()

# 画像を開く
img = Image.open('input.jpg')
rap.rap("画像の読み込み")

# 画像を書き換え可能な配列に変換
a = np.copy(np.asarray(img))
rap.rap("画像を配列に変換")

# RGBの平均を求め、RGBに反映してモノクロ化する
for col in a:
    for p in col:
        ave = np.average(p)  # 全色の平均値を求める
        p[:] = ave
rap.rap("画像をモノクロ化する計算")

# 配列を画像に変換
img = Image.fromarray(a)
rap.rap("配列から画像に変換")

# 反転した画像を保存
img.save('output.jpg')
rap.rap("画像を保存")

# 処理時間の記録を全て出力
print(rap.history)

出力結果はこちら。

[['画像の読み込み', 0.015970700000000004], ['画像を配列に変換', 0.03027089999999999], ['画像をモノクロ化する計算', 33.2706186], ['配列から画像に変換', 0.008368099999998435], ['画像を保存', 0.012884800000001917]]

モノクロ化の計算にかなりの時間がかかっていることがわかりました。

出力用のメソッドを追加する

出力結果が配列のままだと見づらいので、クラスに出力メソッドを追加します。

# 中略

    # ラップタイムの一覧を出力
    def printhistory(self):
        total = 0  # 合計時間カウンタ
        for h in self.history:
            print("{} : {} sec".format(h[0], h[1]))  # ラップタイム履歴を出力
            total += h[1]  # 合計時間を加算
        print("[total] : {} sec".format(total))  # 合計時間を出力

# 中略

# 処理時間の記録を全て出力
rap.printhistory()

画像処理の例で実行したところ、このように出力されました。

画像の読み込み : 0.015939199999999987 sec
画像を配列に変換 : 0.03472949999999997 sec
画像をモノクロ化する計算 : 50.025535999999995 sec
配列から画像に変換 : 0.01075490000000201 sec
画像を保存 : 0.023629499999998416 sec
[total] : 50.1105891 sec

だいぶ見やすくなりましたね。

クラスを別のファイルで定義する

大きなプロジェクトの場合、1つのソースファイルに全部記述してしまうと、管理も大変になります。

そこで、今回作ったクラスは、CoushLib.pyに移して、再利用しやすくしてみます。

#CoushLib.pyで保存する
import time

# 処理時間を記録するクラス
class RapTime():
    starttime = time.perf_counter()  # クラスが生成された時間
    history = []  # ラップタイムの履歴

    # コンストラクタ:初期化処理
    def __init__(self):
        pass

    # ラップタイムを記録
    def rap(self, label):
        t = time.perf_counter()  # 呼ばれた瞬間の時刻を取得
        self.starttime, t = t, t - self.starttime  # timeを更新し、beforetimeにかかった時間を代入
        self.history.append([label, t])  # ラップタイムを記録
        return t  # かかった時間を返す

    # ラップタイムの一覧を出力
    def printhistory(self):
        total = 0  # 合計時間カウンタ
        for h in self.history:
            print("{} : {} sec".format(h[0], h[1]))  # ラップタイム履歴を出力
            total += h[1]  # 合計時間を加算
        print("[total] : {} sec".format(total))  # 合計時間を出力

呼び出すときは、次のように呼び出します。

import CoushLib

rap = CoushLib.RapTime()

ファイル名で呼び出すと長い場合、別名を付けて省略することもできます。

import CoushLib as CL

rap = CL.RapTime()

このクラスをライブラリにしました

このクラスは、このサイトのサンプルコードで使用するライブラリ、CoushLib.pyに入れました。こちらをどうぞ。