VBAで1ミリ秒単位の処理時間を計測!Win32 API(GetTickCount)による精密プロファイリング
【背景と目的】
VBA標準のTimer関数は精度が秒単位と低く、高速なループ処理のボトルネック特定には不向きです。本稿ではWin32 APIを利用し、ミリ秒単位で正確な実効速度を計測する手法を解説します。
【処理フロー図】
graph TD A["計測開始"] --> B["Win32 APIで現在時刻を取得"] B --> C["画面更新・自動計算の停止"] C --> D["メイン処理実行/配列処理等"] D --> E["画面更新・自動計算の再開"] E --> F["APIで終了時刻を取得"] F --> G["差分を計算し実行時間を表示"]
【実装:VBAコード】
Option Explicit
' --- Win32 API 宣言 ---
' 64bit/32bit両対応のため PtrSafe を付与
' GetTickCount はシステム起動からの経過時間をミリ秒単位で返します
#If VBA7 Then
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
#Else
Private Declare Function GetTickCount Lib "kernel32" () As Long
#End If
''' <summary>
''' 高速化手法を適用した処理時間計測のサンプルコード
''' </summary>
Public Sub MeasurePerformance()
Dim startTime As Long
Dim endTime As Long
Dim diffTime As Double
' 計測開始
startTime = GetTickCount()
' --- 高速化設定 ---
With Application
.ScreenUpdating = False ' 画面更新停止
.Calculation = xlCalculationManual ' 自動計算停止
.EnableEvents = False ' イベント抑止
End With
On Error GoTo ErrorHandler
' --- メイン処理(例:大量データの配列処理) ---
Dim targetRange As Range
Dim dataArray As Variant
Dim i As Long, j As Long
Set targetRange = Sheet1.Range("A1:J10000")
dataArray = targetRange.Value ' セル値を配列に格納(高速化の定石)
For i = LBound(dataArray, 1) To UBound(dataArray, 1)
For j = LBound(dataArray, 2) To UBound(dataArray, 2)
' ここに何らかの加工処理を記述
dataArray(i, j) = dataArray(i, j) * 1.1
Next j
Next i
targetRange.Value = dataArray ' 結果を一括書き戻し
' --------------------------------------------
CleanExit:
' --- 設定の復元 ---
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
' 計測終了
endTime = GetTickCount()
' 秒単位に変換して表示
diffTime = (endTime - startTime) / 1000
MsgBox "処理が完了しました。" & vbCrLf & _
"実行時間: " & Format(diffTime, "0.000") & " 秒", vbInformation
Exit Sub
ErrorHandler:
MsgBox "エラーが発生しました: " & Err.Description, vbCritical
Resume CleanExit
End Sub
【技術解説】
GetTickCountの利点: VBA標準の
Timer関数は午前0時からの秒数(Single型)を返しますが、精度が約1/64秒程度に制限されます。GetTickCountを使用することで、OSレベルの1ミリ秒単位のカウンタにアクセスでき、より厳密なパフォーマンス測定が可能になります。64bit環境への対応:
PtrSafeキーワードを使用することで、Office 64bit版でもエラーなくAPIを呼び出せます。戻り値のLongは32bit整数ですが、経過時間の差分(ミリ秒)を取得する用途では十分実用的です。ボトルネックの排除: 計測対象の処理に
ScreenUpdating = Falseや「配列への一括読み込み」を含めることで、純粋なロジックの計算速度を浮き彫りにします。
【注意点と運用】
49.7日の壁:
GetTickCountの戻り値は32bit符号なし整数(をVBAのLongで受ける形)のため、PCを再起動せず約49.7日が経過すると値が0にリセットされます。長時間連続稼働するサーバー等の環境では注意が必要です。エラーハンドリングの徹底:
ScreenUpdatingを停止した状態でマクロが異常終了すると、Excelの操作ができなくなったように見えます。必ずOn Error GoToで設定を復元するルートを確保してください。
【まとめ】
精度を求めるならAPI: 秒単位のTimer関数ではなく、ミリ秒単位の
GetTickCountを採用する。環境互換性の確保:
PtrSafe宣言を行い、32bit/64bit双方のExcelで動作するコードを書く。純粋な速度計測: 画面更新停止や配列処理を併用し、VBA特有のオーバーヘッドを最小化して計測する。

