VBAで1ミリ秒単位の処理時間を計測!Win32 API(GetTickCount)による精密プロファイリング

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

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

【技術解説】

  1. GetTickCountの利点: VBA標準のTimer関数は午前0時からの秒数(Single型)を返しますが、精度が約1/64秒程度に制限されます。GetTickCountを使用することで、OSレベルの1ミリ秒単位のカウンタにアクセスでき、より厳密なパフォーマンス測定が可能になります。

  2. 64bit環境への対応: PtrSafeキーワードを使用することで、Office 64bit版でもエラーなくAPIを呼び出せます。戻り値のLongは32bit整数ですが、経過時間の差分(ミリ秒)を取得する用途では十分実用的です。

  3. ボトルネックの排除: 計測対象の処理に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特有のオーバーヘッドを最小化して計測する。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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