Excel VBA 逆引き集 | 実務テンプレ完全版(超再利用部品) – 処理時間計測テンプレ

Excel VBA
スポンサーリンク

ねらい:どのマクロも「どこに何秒かかっているか」を一発で見える化する

「このマクロ、なんか遅いんだけど、どこが重いのか分からない」
「改善したつもりだけど、本当に速くなったのか数字で確認できない」

ここを感覚ではなく、きちんと「時間」で語れるようにするのが、処理時間計測テンプレの目的です。

一番やりたいのは、これです。

マクロの開始時刻を記録する。
重要なステップごとに「ここまで何秒かかったか」を記録する。
最後に「合計何秒」「各ステップ何秒」をログとして残す。

そして、そのためのコードを一度テンプレとして作っておき、
どのマクロにも“同じ書き方”で組み込めるようにします。


基本の考え方:Timer と Now を「ラッパー関数」で隠す

なぜ直接 Timer を使わない方がいいのか

VBAには TimerNow という便利な関数がありますが、
それをマクロのあちこちにベタ書きしてしまうと、次のような問題が出ます。

どこで測っているか分かりづらい。
フォーマットがバラバラになる。
あとから「ログシートにも書きたい」と思ったときに全部書き換えになる。

なので、「時間を測る」という行為を、専用の小さな関数・プロシージャに閉じ込めてしまいます。
マクロ側は「開始」「ステップ」「終了」を呼ぶだけにする——これがテンプレの肝です。

計測の粒度をどう決めるか

処理時間計測には、大きく二つの粒度があります。

マクロ全体に何秒かかったか。
マクロの中の各ステップに何秒かかったか。

今回のテンプレでは、両方を一度に取れる形にします。


コア部品1:シンプルな「開始〜終了」計測テンプレ

まずは一番シンプルな、「このマクロ全体で何秒かかったか」を測るテンプレから。

経過秒数を返す関数

' ModStopWatch.bas
Option Explicit

Public Function ElapsedSeconds(ByVal startTime As Double) As Double
    Dim nowT As Double
    nowT = Timer
    
    If nowT >= startTime Then
        ElapsedSeconds = nowT - startTime
    Else
        ElapsedSeconds = (86400# - startTime) + nowT
    End If
End Function
VB

初心者向けにポイントをかみ砕きます。

Timer は「その日になってからの秒数」を返します(0〜86400)。
日付をまたぐと Timer がリセットされるので、「今の方が小さい」ケースがあり得ます。
その場合は「24時間分(86400秒)から startTime を引いて、今の Timer を足す」という計算で補正します。

この関数を使うと、「開始時刻を startTime に取っておいて、あとから ElapsedSeconds(startTime) で経過秒数を取る」という形にできます。

マクロ全体の時間を測るコード例

Sub HeavyProcess()
    Const MODULE_NAME As String = "HeavyProcess"
    Dim t0 As Double
    t0 = Timer
    
    ' ここに重い処理を書く
    ' 例:大量の行をループして処理するなど
    
    Dim sec As Double
    sec = ElapsedSeconds(t0)
    
    MsgBox "処理時間: " & Format(sec, "0.00") & " 秒", vbInformation
End Sub
VB

これだけでも、「このマクロは何秒かかっているのか」が分かるようになります。
ただ、これだと「どの部分が重いのか」が分からないので、次のステップに進みます。


コア部品2:ステップごとの時間を記録する「ストップウォッチクラス」

なぜクラスにするのか

ステップごとの時間を測り始めると、こんな情報を持ちたくなります。

全体の開始時刻。
前のステップの時刻。
ステップ名と、そのステップにかかった時間の一覧。

これをバラバラの変数で管理すると、すぐにぐちゃぐちゃになります。
そこで、「処理時間計測専用の“ストップウォッチオブジェクト”」を1つ作ってしまうのがきれいです。

StopWatch クラスの定義

VBEで「挿入 → クラスモジュール」を選び、名前を StopWatch にします。
中身を次のように書きます。

' Class: StopWatch
Option Explicit

Private tStart As Double
Private tLast As Double
Private logText As String

Public Sub Start()
    tStart = Timer
    tLast = tStart
    logText = ""
End Sub

Public Sub Lap(ByVal stepName As String)
    Dim nowT As Double
    nowT = Timer
    
    Dim diff As Double
    If nowT >= tLast Then
        diff = nowT - tLast
    Else
        diff = (86400# - tLast) + nowT
    End If
    
    logText = logText & stepName & ":" & Format(diff, "0.00") & "秒" & vbCrLf
    tLast = nowT
End Sub

Public Function TotalSeconds() As Double
    Dim nowT As Double
    nowT = Timer
    
    If nowT >= tStart Then
        TotalSeconds = nowT - tStart
    Else
        TotalSeconds = (86400# - tStart) + nowT
    End If
End Function

Public Function GetLogText() As String
    GetLogText = logText
End Function
VB

ここでやっていることを整理します。

Start で「全体の開始時刻」と「前ステップの時刻」を Timer で記録する。
Lap(stepName) を呼ぶたびに、「前ステップからの経過秒数」を計算し、
「ステップ名: xx.xx秒」という文字列を logText に追記する。
TotalSeconds で「全体の経過秒数」を返す。
GetLogText で「ステップごとの時間一覧」をまとめて文字列として返す。

このクラスを1つ生成してマクロの中で使えば、
「Start → 各ステップで Lap → 最後に TotalSeconds と GetLogText」という流れで、
きれいに時間を取れるようになります。


実戦例:顧客処理マクロにストップウォッチを組み込む

想定する処理の流れ

顧客データを処理するマクロが、次のようなステップを持っているとします。

CSVを読み込む。
データを整形する。
マスタとJOINする。
集計する。

この4ステップに、それぞれ何秒かかっているかを測ってみます。

コード例

Sub ProcessCustomer()
    Const MODULE_NAME As String = "ProcessCustomer"
    
    Dim sw As StopWatch
    Set sw = New StopWatch
    
    sw.Start
    
    ' ステップ1: CSV読み込み
    ' …ここにCSV読み込み処理…
    sw.Lap "CSV読み込み"
    
    ' ステップ2: データ整形
    ' …ここに整形処理…
    sw.Lap "データ整形"
    
    ' ステップ3: マスタJOIN
    ' …ここにJOIN処理…
    sw.Lap "マスタJOIN"
    
    ' ステップ4: 集計
    ' …ここに集計処理…
    sw.Lap "集計"
    
    Dim totalSec As Double
    totalSec = sw.TotalSeconds
    
    Dim msg As String
    msg = "総処理時間: " & Format(totalSec, "0.00") & " 秒" & vbCrLf & vbCrLf
    msg = msg & sw.GetLogText
    
    MsgBox msg, vbInformation, "処理時間計測結果"
End Sub
VB

このマクロを実行すると、メッセージボックスに例えばこんな感じで出ます。

総処理時間: 12.34 秒

CSV読み込み: 2.10秒
データ整形: 3.45秒
マスタJOIN: 4.00秒
集計: 2.79秒

これを見ると、「JOINが一番重いな」「CSV読み込みはそんなに問題じゃないな」と、
どこをチューニングすべきかが一目で分かります。


処理ログテンプレと組み合わせて「時間付きログ」にする

LogWrite と組み合わせるイメージ

前に作った処理ログテンプレ(LogWrite, LogStart, LogEnd)があるなら、
ストップウォッチの結果をログシートにも残すと、さらに強力になります。

例えば、ProcessCustomer の最後をこう変えます。

Sub ProcessCustomer()
    Const MODULE_NAME As String = "ProcessCustomer"
    On Error GoTo ErrHandler
    
    LogStart MODULE_NAME, "顧客処理開始"
    
    Dim sw As StopWatch
    Set sw = New StopWatch
    sw.Start
    
    ' …各ステップ…
    sw.Lap "CSV読み込み"
    ' …
    sw.Lap "データ整形"
    ' …
    sw.Lap "マスタJOIN"
    ' …
    sw.Lap "集計"
    
    Dim totalSec As Double
    totalSec = sw.TotalSeconds
    
    LogWrite "INFO", MODULE_NAME, "TIME", _
             "総処理時間=" & Format(totalSec, "0.00") & "秒 / " & Replace(sw.GetLogText, vbCrLf, " | ")
    
    LogEnd MODULE_NAME, "顧客処理正常終了"
    Exit Sub
    
ErrHandler:
    LogError MODULE_NAME, "MAIN", Err
End Sub
VB

こうしておくと、Log シートに

日時 / INFO / ProcessCustomer / TIME / 総処理時間=12.34秒 / CSV読み込み:2.10秒 | データ整形:3.45秒 | …

という1行が残ります。
これを日付ごとに並べていけば、「最近遅くなっていないか」「改善後にどれくらい速くなったか」を、
後から見返すことができます。


重要ポイントの深掘り:処理時間計測テンプレを“武器”にするためのコツ

「とりあえず全体時間」から始めて、徐々にステップを刻む

いきなり全部のステップに Lap を入れようとすると、
どこに区切りを入れるかで悩んで止まってしまいがちです。

最初は「マクロ全体の時間」だけを測る。
次に、「一番重そうな部分」をざっくり1〜3ステップに分けて Lap を入れる。
それでも足りなければ、さらに細かく刻む。

この“段階的に細かくしていく”やり方の方が、現場では回しやすいです。

「測ると遅くなるのでは?」という不安について

Timer やクラス呼び出しのオーバーヘッドは、
通常の業務マクロ(数千〜数万行の処理)に比べれば誤差レベルです。

本当にミリ秒単位の世界を争うような処理でなければ、
「測るためのコスト」より「測らないことによるブラックボックス化」の方が圧倒的に痛いです。

なので、まずは気にせず入れてみて、
どうしても気になる部分だけ後からオフにする、くらいの感覚で大丈夫です。

「テンプレとしてどこまで共通化するか」

今回の StopWatch クラスは、かなり汎用的に作ってあります。

Start
Lap(stepName)
TotalSeconds
GetLogText

この4つさえあれば、どんなマクロにも同じ書き方で組み込めます。

もし、あなたの現場で「必ずログシートに書きたい」「必ずバッチログと連携したい」などのルールがあるなら、
StopWatch の中に「LogWrite を呼ぶオプション」を追加することもできます。

例えば、Lap の中で「一定以上時間がかかったステップだけログに出す」など、
“遅いところだけ検知するセンサー”として使うこともできます。


まとめ:処理時間計測テンプレは「StopWatch を1つ持ち込むだけ」で始められる

今回のテンプレの核は、こう整理できます。

Timer を直接あちこちで使うのではなく、
StopWatch クラス(Start / Lap / TotalSeconds / GetLogText)という“時間計測専用オブジェクト”に閉じ込める。
マクロ側は、そのオブジェクトを1つ生成して、ステップごとに Lap を呼び、最後に結果を表示・ログ出力するだけにする。

これを一度作ってしまえば、
「このマクロ、どこが遅いんだろう?」と思ったときに、
StopWatch を1行足すだけで“見える化”が始められます。

タイトルとURLをコピーしました