ねらい:どのマクロも「どこに何秒かかっているか」を一発で見える化する
「このマクロ、なんか遅いんだけど、どこが重いのか分からない」
「改善したつもりだけど、本当に速くなったのか数字で確認できない」
ここを感覚ではなく、きちんと「時間」で語れるようにするのが、処理時間計測テンプレの目的です。
一番やりたいのは、これです。
マクロの開始時刻を記録する。
重要なステップごとに「ここまで何秒かかったか」を記録する。
最後に「合計何秒」「各ステップ何秒」をログとして残す。
そして、そのためのコードを一度テンプレとして作っておき、
どのマクロにも“同じ書き方”で組み込めるようにします。
基本の考え方:Timer と Now を「ラッパー関数」で隠す
なぜ直接 Timer を使わない方がいいのか
VBAには Timer や Now という便利な関数がありますが、
それをマクロのあちこちにベタ書きしてしまうと、次のような問題が出ます。
どこで測っているか分かりづらい。
フォーマットがバラバラになる。
あとから「ログシートにも書きたい」と思ったときに全部書き換えになる。
なので、「時間を測る」という行為を、専用の小さな関数・プロシージャに閉じ込めてしまいます。
マクロ側は「開始」「ステップ」「終了」を呼ぶだけにする——これがテンプレの肝です。
計測の粒度をどう決めるか
処理時間計測には、大きく二つの粒度があります。
マクロ全体に何秒かかったか。
マクロの中の各ステップに何秒かかったか。
今回のテンプレでは、両方を一度に取れる形にします。
コア部品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行足すだけで“見える化”が始められます。

