高速化するための設定&高速テクニックまとめ

高速化について

高速化とは

高速化とは、マクロの処理速度をできるだけ速くすることをいいます。 同じ結果を得る場合でも、記述の仕方や処理の方法によって実行時間は大きく変わることがあります。 一度の短縮はわずかでも、細かい工夫を積み重ねることで、全体として大きな差につながります。 特に大量のデータを扱う場面では、その効果が顕著に現れます。
つまり「同じ実行結果を保ちながら、より短時間で完了させる」ことを目指すのが、高速化の考え方です。

具体的には、Application.ScreenUpdating = False や Application.Calculation = xlCalculationManual などが代表的な手法です。

処理速度を計測する関数について

本記事の実行速度の測定には、ChatGPT が作成した処理時間計測プログラムをベースに、著者が一部改修したものを使用しています。

処理時間計測プログラム

Option Explicit

Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (ByRef lpPerformanceCount As LongLong) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (ByRef lpFrequency As LongLong) As Long
Private tStart As LongLong
Private freq  As LongLong

'=== 計測開始 ===
Public Sub StartTimer()
    QueryPerformanceFrequency freq
    QueryPerformanceCounter tStart
End Sub
'=== 計測終了 ===
Public Function EndTimer() As Double
    Dim tEnd As LongLong
    QueryPerformanceCounter tEnd
    EndTimer = (tEnd - tStart) / freq
End Function

'=== 計測開始 ===
Public Sub StartTimer2()
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationManual
    Call StartTimer
End Sub
'=== 計測終了 ===
Public Function EndTimer2() As Double
    EndTimer2 = EndTimer
    Application.ScreenUpdating = True
    Application.EnableEvents = True
    Application.Calculation = xlCalculationAutomatic
End Function

処理速度を計測するPCについて

本検証は以下の環境で実施しました。
・OS : Windows 11 Home 64bit
・CPU : Intel Core i7-13700H
・メモリ: 64GB
・Excel : Excel 2021(64ビット)
・ストレージ:SSD

画面更新・自動計算・イベントをオフにする

画面更新をオフにする

画面更新をオフにすると処理速度は向上しますが、その間は画面表示が更新されません。
しかし、画面更新をオンにするまで、Excelの画面はプログラム実行前の画面で止まってしまいます。 処理が終わったら必ずオンに戻してください。

・画面更新をオフにする
 Application.ScreenUpdating = False

・画面更新をオンにする
 Application.ScreenUpdating = True

処理時間計測プログラム

Sub SampleSw1_1()
    Dim i As Long
    Dim j As Long
    
    '高速化有り
    Call StartTimer
    Application.ScreenUpdating = False
    For i = 1 To 100000
        Cells(1, 1) = "test"
    Next i
    Application.ScreenUpdating = True
    Debug.Print "高速化有りの処理時間:" & EndTimer

    '高速化無し
    Call StartTimer
    For i = 1 To 100000
        Cells(1, 1) = "test"
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
11.42053991.674115915.15%
21.38459931.666032316.89%
31.41734101.683864915.83%
41.38214431.649770216.22%
51.38510571.661670116.64%
平均1.39794601.667090916.14%

数式の自動計算を手動計算にする

数式の自動計算を「手動計算」に切り替えると、処理速度が速くなります。 特に大量のデータや複雑な関数が含まれている場合に効果があります。
ただし、手動計算モードでは数式が自動で更新されないため、セルの値を正しく取得したい場合は再計算をする必要があります。 通常は 処理の前に手動計算へ切り替えて、処理後に自動計算へ戻す という流れで利用すると安全です。

・手動計算に切り替える
 Application.Calculation = xlCalculationManual

・自動計算に切り替える
 Application.Calculation = xlCalculationAutomatic

・Excel関数を再計算をさせる
 Application.Calculate

処理時間計測プログラム

Sub SampleSw1_2()
    Dim i As Long
    Dim pos As Long
    
    Application.Calculation = xlCalculationAutomatic
    Range("B2") = "=SUM(A1:A3)"
    
    '高速化有り
    Call StartTimer
    Application.Calculation = xlCalculationManual
    For i = 1 To 50000
        pos = 1 + (i Mod 3)
        Cells(pos, 1).Value2 = i
    Next i
    Application.Calculation = xlCalculationAutomatic
    Debug.Print "高速化有りの処理時間:" & EndTimer

    '高速化無し
    Call StartTimer
    For i = 1 To 50000
        pos = 1 + (i Mod 3)
        Cells(pos, 1).Value2 = i
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
10.56713875.673625790.00%
20.51529635.717190590.99%
30.53213255.728247790.71%
40.52040835.708550190.88%
50.51892895.761701790.99%
平均0.53078095.717863190.72%

イベント処理を無視する

イベント処理を無視すると処理が速くなります。
例えば、セルに値を入力したときに「Changeイベント」などが発生しますが、大量のセルを書き換える場合、イベントが何度も呼び出されるため処理が遅くなります。
そのため、処理の最初にイベントを無効化し、処理後に有効化するのが定石です。

・イベント処理を無視する
 Application.EnableEvents = False

・イベント処理を有効にする
 Application.EnableEvents = True

処理時間計測プログラム

Sub SampleSw1_3()
    Dim i As Long
    
    '高速化有り
    Call StartTimer
    Application.EnableEvents = False
    For i = 1 To 100000
        Cells(1, 1) = "test"
    Next i
    Application.EnableEvents = True
    Debug.Print "高速化有りの処理時間:" & EndTimer

    '高速化無し
    Call StartTimer
    For i = 1 To 100000
        Cells(1, 1) = "test"
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
111.140171012.28892939.35%
210.974324512.512851112.30%
311.180235512.737980712.23%
411.124359512.704585112.44%
511.120214812.372502410.12%
平均11.107861112.523369711.3%

上記をまとめたプログラム

処理前に、画面更新・自動計算・イベントをまとめてオフにする(無視する)関数を自作して、処理前に実行すると処理速度が速くなります。
処理後は、忘れずにオンに戻しましょう。

処理時間計測プログラム

Sub SampleSw1_4()
    Dim i As Long
    Dim pos As Long
    
    Range("B2") = "=SUM(A1:A3)"
    
    '高速化有り
    Call StartTimer
    Application.DisplayAlerts = False
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    For i = 1 To 100000
        pos = 1 + (i Mod 3)
        Cells(pos, 1).Value2 = i
    Next i
    Application.DisplayAlerts = True
    Application.ScreenUpdating = True
    Application.Calculation = xlCalculationAutomatic
    Debug.Print "高速化有りの処理時間:" & EndTimer

    '高速化無し
    Call StartTimer
    For i = 1 To 100000
        pos = 1 + (i Mod 3)
        Cells(pos, 1).Value2 = i
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
11.029848411.485858091.03%
21.024669611.195654290.85%
31.024368411.278964890.92%
41.030892111.163175090.77%
51.035083311.274061690.82%
平均1.028972711.279542790.88%

セルへのアクセスを工夫する

Rangeでなく、Cellsを使用する

Rangeでなく、Cellsを使用すると処理速度が上がります。
「Range("A1")」のようにセルを指定すると、Excelは内部で文字列を解析し、セル番号に変換してからアクセスします。 一方、「Cells(1, 1)」は行・列番号で直接指定できるため、この変換処理が不要で高速です。

余談ですが、範囲の指定はCellsだけでは指定できないため、Rangeを使用してください。 単純にセルを1つずつ処理する場面ではCellsを使うと効率的です。

処理時間計測プログラム

Sub SampleSw2_1()
    Dim i As Long

    '高速化有り
    Call StartTimer2
    For i = 1 To 100000
        Cells(1, 1) = "test"
    Next i
    Debug.Print "高速化有りの処理時間:" & EndTimer2

    '高速化無し
    Call StartTimer2
    For i = 1 To 100000
        Range("A1") = "test"
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer2
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
10.89300201.151542622.45%
20.89881341.223845526.56%
30.96990781.176412917.55%
40.90389421.139778720.70%
50.89777611.190389624.58%
平均0.91267871.176393922.42%

Value2を使用する

セルに値を代入する際、省略すると既定で「.Value」が使われます。
ただし「.Value」は Currency型や Date型などで変換を行うため、わずかに処理が遅くなります。 一方、「.Value2」は変換を行わずにそのまま代入するため、処理速度が速いのが特徴です。
大規模なデータ処理では .Value2 を利用すると高速化につながります。

処理時間計測プログラム

Sub SampleSw2_2()
    Dim i As Long

    '高速化有り
    Call StartTimer2
    For i = 1 To 100000
        Cells(1, 1).Value2 = "test"
        Cells(1, 2).Value2 = "test"
    Next i
    Debug.Print "高速化有りの処理時間:" & EndTimer2

    '高速化無し
    Call StartTimer2
    For i = 1 To 100000
        Cells(1, 1).Value = "test"
        Cells(1, 2).Value = "test"
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer2
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
11.91129071.91584340.24%
21.89816481.93871742.09%
31.92100301.92268220.09%
41.91814702.00009124.10%
51.91742751.92355840.32%
平均1.91320661.94017851.39%

セルの書式設定などは範囲で行う

セルの書式設定は、1つずつループで処理するよりも、範囲をまとめて指定した方が高速化できます。 繰り返し処理に時間がかかるうえ、Excelとのやり取りを大幅に減らせるためです。

処理時間計測プログラム(連続したセルの範囲)

Sub SampleSw2_3()
    Dim i As Long
    
    '高速化有り
    Call StartTimer2
    Range("A1:A1000").Font.Bold = True
    Range("A1:A1000").Interior.Color = vbGreen
    Debug.Print "高速化有りの処理時間:" & EndTimer2

    '高速化無し
    Call StartTimer2
    For i = 1 To 1000
        Cells(i, 1).Font.Bold = True
        Cells(i, 1).Interior.Color = vbGreen
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer2
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
10.01023990.034494870.31%
20.00073460.031783097.69%
30.00048020.034226598.60%
40.00052680.032430998.38%
50.00055760.032310698.27%
平均0.00250780.033049292.41%

先の例では「連続した範囲」のセルをまとめて処理しました。
一方で、離れたセルをまとめて書式設定する方法は大きく分けて2種類あります。

①.Rangeでカンマ区切りにする方法
例:Range("A1,C1")
→ 処理速度は速いですが、指定文字列が 256文字を超えると、エラー(1004)になる

②.Unionでまとめる方法
例:Union(Range("A1"), Range("C1"))
→ 少し遅めですが、文字数制限がなく、大量のセルでも安全に使えます

大量セルを扱う場合は、安全性を優先してUnionを使うのがおすすめです。

処理時間計測プログラム(連続していないセル)

Sub SampleSw2_3Ex()
    Dim i As Long
    Dim arr(65) As String
    Dim pos As String
    Dim rng As Range
    
    '変更対象のセルを配列に入れる
    For i = 0 To 65
        arr(i) = Cells(1 + i, 1 + (i Mod 2)).Address
    Next i
    
    '高速化(Join)
    Call StartTimer2
    '変更対象のセルをカンマ区切りにして、Rangeで操作できるようにする
    pos = Join(arr, ",")
    pos = Replace$(pos, "$", "")
    If Len(pos) < 256 Then
        Range(pos).Font.Bold = True
        Range(pos).Interior.Color = vbGreen
        Debug.Print "Rangeを使用した高速化:" & EndTimer2
    Else
        'Rangeのカッコ内が256字を超えると、エラーになるので回避する
        Debug.Print "Range内の指定文字列が256字を超えました"
    End If
    
    '高速化(Union)
    Call StartTimer2
    For i = 0 To 65
        If rng Is Nothing Then
            Set rng = Range(arr(i))
        Else
            Set rng = Union(rng, Range(arr(i)))
        End If
    Next i
    rng.Font.Bold = True
    rng.Interior.Color = vbGreen
    Debug.Print "Unionを使用した高速化:" & EndTimer2

    '高速化無し
    Call StartTimer2
    For i = 0 To 65
        Range(arr(i)).Font.Bold = True
        Range(arr(i)).Interior.Color = vbGreen
    Next i
    Debug.Print "高速化無しの処理時間 :" & EndTimer2
End Sub
※ループを65回から、66回に変更すると”pos”が256字を超えます。

【実行結果】

回数高速化:Union高速化:Range高速化:無削減効率
10.00105200.00046160.002442456.93%
20.00091920.00069380.003024769.62%
30.00130580.00063710.002322443.78%
40.00112920.00049740.003570068.37%
50.00099450.00044910.002266556.13%
平均0.00108000.00054780.002725060.36%

※ 削減効率は、「高速化:Union」と「高速化:無」の比較です。

セルの値を「範囲」と「配列」で操作する

セルの範囲を配列として取得したり、逆に配列(二次元配列)をセル範囲にまとめて書き込むことができます。
この方法は、1セルずつループで読み書きするよりも圧倒的に高速です。Excelシートへのアクセス回数を大幅に減らせるため、数千件~数万件のデータ処理で特に効果を発揮します。

処理時間計測プログラム(範囲への書き込み)

Sub SampleSw2_4_Write()
    Dim arr(1 To 50000, 1 To 1) As String
    Dim i As Long
    
    '変更対象のセルを配列に入れる
    For i = 1 To 50000
        arr(i, 1) = "test"
    Next i
    
    '高速化有り
    Call StartTimer2
    Range("A1:A50000") = arr
    Debug.Print "高速化有りの処理時間:" & EndTimer2

    '高速化無し
    Call StartTimer2
    For i = 1 To 50000
        Cells(i, 1) = arr(i, 1)
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer2
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
10.03625970.455164492.04%
20.03469370.455356492.39%
30.03522170.477739492.63%
40.03808500.448786391.52%
50.03905390.459153291.50%
平均0.03666280.459240092.02%

読み取りの場合のプログラム及び実行速度です。

処理時間計測プログラム(範囲の読み取り)

Sub SampleSw2_4_Read()
    Dim arr1() As Variant
    Dim arr2(1 To 50000, 1 To 1) As Variant
    Dim i As Long
    
    '高速化有り
    Call StartTimer2
    arr1 = Range("A1:A50000")
    Debug.Print "高速化有りの処理時間:" & EndTimer2

    '高速化無し
    Call StartTimer2
    For i = 1 To 50000
        arr2(i, 1) = Cells(i, 1)
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer2
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
10.01044680.085711987.81%
20.01005270.098702889.82%
30.00886580.101433991.26%
40.01016740.085222188.07%
50.01208800.088826286.39%
平均0.01032410.091979088.78%

型の宣言を見直す

演算用の変数は、Variant型で宣言しない

Variantで変数を宣言すると実行速度が遅くなります。
これは、変数への代入時に何の型のデータが格納されるかを判断してから、データを格納するためです。 また、Variant型の変数にまつわるエラーが発生した時、的確なエラーメッセージが表示されません。
特別な理由がない限り、Variant型は避けるのが基本です。

処理時間計測プログラム

Sub SampleSw3_1_Long(ByVal i As Long, ByVal j As Long)
    Dim buf As Long
    buf = i * j
End Sub
Sub SampleSw3_1_Variant(ByVal i As Variant, ByVal j As Variant)
    Dim buf As Variant
    buf = i * j
End Sub

Sub SampleSw3_1()
    Dim i As Long
    
    '高速化有り
    Call StartTimer2
    For i = 1 To 1000000
        SampleSw3_1_Long i, 1
    Next i
    Debug.Print "高速化有りの処理時間:" & EndTimer2
    
    '高速化無し
    Call StartTimer2
    For i = 1 To 1000000
        SampleSw3_1_Variant i, 1
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer2
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
10.03458670.084217758.93%
20.03932380.082909552.57%
30.03885480.081771952.48%
40.03771640.084354455.29%
50.03431960.081915458.10%
平均0.03696030.083033855.49%

CreateObjectを乱用しない(参照設定を使用する)

Object型で変数を宣言し、後から CreateObject で型を設定すると、速度は遅くなります。
これは「後期バインディング」と呼ばれ、型が実行時に決定されるためです。 一方、参照設定を行い、変数の宣言時に型を指定する方法は「早期バインディング」と呼ばれ、コンパイル時に型が決まるため処理が速く、補完やエラーチェックの利点もあります。

処理時間計測プログラム

'早期バインディング
Sub SampleSw3_2_Fast()
    '参照設定で「Microsoft Scripting Runtime」にチェックを入れること!
    Dim fso As FileSystemObject  
    Dim filePath As String
    Dim fileExists As Boolean
    filePath = "C:\Users\Public\TestFolder\sample.txt"
    Set fso = New FileSystemObject
    fileExists = fso.fileExists(filePath)
    Set fso = Nothing
End Sub
'後期バインディング
Sub SampleSw3_2_Slow()
    Dim fso As Object
    Dim filePath As String
    Dim fileExists As Boolean
    filePath = "C:\Users\Public\TestFolder\sample.txt"
    Set fso = CreateObject("Scripting.FileSystemObject")
    fileExists = fso.fileExists(filePath)
    Set fso = Nothing
End Sub

Sub SampleSw3_2()
    Dim i As Long
    
    '高速化有り
    Call StartTimer2
    For i = 1 To 10000
        SampleSw3_2_Fast
    Next i
    Debug.Print "高速化有りの処理時間:" & EndTimer2
    
    '高速化無し
    Call StartTimer2
    For i = 1 To 10000
        SampleSw3_2_Slow
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer2
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
11.64637411.75657166.27%
21.70936241.75343022.51%
31.60785261.77224159.28%
41.62266021.77283928.47%
51.65692331.75266905.46%
平均1.64863451.76155036.41%

プログラムを見直す

文字列操作の関数の返す型を指定する

Replace関数やTrim関数など一部の文字列関数は、通常、Variant型を返します。
関数名の後ろに「$」を付けると String型で返すようになり、型判定の処理が不要になるため、高速化できます。

処理時間計測プログラム

Sub SampleSw4_1()
    Dim i As Long
    Dim str As String
    
    '高速化有り
    Call StartTimer2
    For i = 1 To 100000
        str = Replace$("test", "t", "")
        str = Left$("090-1234-5678", 3)
        str = Mid$("090-1234-5678", 5, 4)
        str = Right$("090-1234-5678", 3)
    Next i
    Debug.Print "高速化有りの処理時間:" & EndTimer2
    
    
    '高速化無し
    Call StartTimer2
    For i = 1 To 100000
        str = Replace("test", "t", "")
        str = Left("090-1234-5678", 3)
        str = Mid("090-1234-5678", 5, 4)
        str = Right("090-1234-5678", 3)
    Next i
    Debug.Print "高速化無しの処理時間:" & EndTimer2
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
10.04231930.048445912.65%
20.04182430.049196114.98%
30.04042060.051774421.93%
40.04793330.058854618.56%
50.04305350.051586416.54%
平均0.04311020.051971517.05%

Select や Activate を乱用しない

Select や Activate を使うと、VBA は一度そのセルを「選択状態」にしてから操作を行います。
つまり「選択する」という無駄な処理が毎回挟まるため、特にループ処理で繰り返すと速度低下が顕著になります。 また、ユーザー操作に依存するため「対象が正しく選択されていないとエラーになる」などの不安定要因にもなります。
そのため、「Range("A1").Value = 1」のように、直接指定する書き方を心掛けた方が、速くて安全です。

処理時間計測プログラム

Sub SampleSw4_2()
    Dim i As Long
    Dim arr(100) As String
    
    '変更対象のセルを配列に入れる
    For i = 0 To 100
        arr(i) = Cells(1 + i, 1 + (i Mod 3)).Address
    Next i
    
    'Select / Active 不使用
    Call StartTimer2
    For i = 0 To 100
        Range(arr(i)).Font.Bold = True
        Range(arr(i)).Interior.Color = vbGreen
    Next i
    Debug.Print "Select / Active 不使用の処理時間:" & EndTimer2
    
    '高速化無し(Select)
    Call StartTimer2
    For i = 0 To 100
        Range(arr(i)).Select
        Selection.Font.Bold = True
        Selection.Interior.Color = vbGreen
    Next i
    Debug.Print "Select の処理時間:" & EndTimer2

    '高速化無し(Activate)
    Call StartTimer2
    For i = 0 To 100
        Range(arr(i)).Activate
        ActiveCell.Font.Bold = True
        ActiveCell.Interior.Color = vbGreen
    Next i
    Debug.Print "Activate の処理時間:" & EndTimer2
End Sub

【実行結果】

回数Select /
Active 不使用
低速
(Select)
低速
(Activate)
削減効率
10.00754060.80395730.794168599.05%
20.00622560.84503360.872505299.29%
30.00755440.78906380.781831699.03%
40.00786160.77317390.775844598.99%
50.00723200.78434960.770132599.06%
平均0.00728280.79911600.798896099.09%

オブジェクトに複数回接続する時、変数を使う

何度も同じオブジェクトにアクセスする場合は、オブジェクト変数に代入してから使うと処理が速くなります。
これは、毎回「どのシート?どのセル?」と探す手間を省けるからです。

・変数を使用しない場合
 ThisWorkbook → ActiveSheet → Range("A1") と 毎回アクセスする

・変数を使用する場合
 ActiveSheet → Range("A1") と アクセスするため、手順を減らせる

さらに、処理速度の向上だけでなく、コードが短くなり可読性の向上などのメリットがあります。 なお、オブジェクトをまとめて扱うもう一つの方法として「Withステートメント」もあります。 これは「特定のオブジェクトに対する処理を一括で書く」方法で、若干ですが高速化に役立ちます。

「Setを使用した場合」、「Setを使用しない場合」、「Withを使用した場合」で処理速度を比較してみます。

処理時間計測プログラム

Sub SampleSw4_3()
    Dim i As Long
    Dim ws As Worksheet
    
    'Set による高速化
    Call StartTimer2
    Set ws = ThisWorkbook.ActiveSheet
    For i = 0 To 100000
        ws.Cells(1, 1) = "test1"
        ws.Cells(2, 1) = "test2"
    Next i
    Set ws = Nothing
    Debug.Print "Setを使用した処理時間 :" & EndTimer2

    'With を使用した場合
    Call StartTimer2
    With ThisWorkbook.ActiveSheet
        For i = 0 To 100000
            .Cells(1, 1) = "test1"
            .Cells(2, 1) = "test2"
        Next i
    End With
    Debug.Print "Withを使用した処理時間:" & EndTimer2
    
    '高速化無し
    Call StartTimer2
    For i = 0 To 100000
        ThisWorkbook.ActiveSheet.Cells(1, 1) = "test1"
        ThisWorkbook.ActiveSheet.Cells(2, 1) = "test2"
    Next i
    Debug.Print "高速化無しの処理時間  :" & EndTimer2
End Sub

【実行結果】

回数高速化/SetWith高速化:無削減効率※
11.81322782.14510372.235788318.90%
21.81971452.15596212.210707517.69%
31.85001102.17780312.224806316.85%
41.85684502.16374952.252865517.58%
51.84986992.24125732.209163216.26%
平均1.83793362.17677502.22666617.46%

※ 削減効率は、「高速化/Set」と「高速化:無」の比較です。

Excel関数で、集計・抽出を行う

VBAでは、Excelに用意されているワークシート関数を使用することができます。
特に、セル範囲のデータを加工・検索する処理では、これらの関数を活用することで処理速度を大幅に向上できる場合があります。 ただし、VLOOKUP 関数などの一部の関数は、エラー処理を組み込んでおく必要があります。

処理時間計測プログラム

Sub SampleSw4_4()
    Dim i As Long
    Dim buf1 As Variant
    Dim buf2 As Variant
    Dim searchData As Variant
    
    '検索するデータ
    searchData = 1000
    
    '仮のデータを入力させる
    For i = 1 To 2000
        Cells(i, 2) = i
        Cells(i, 3) = "C" & i
    Next i
    
    '高速化あり
    Call StartTimer2
    On Error Resume Next
    buf1 = Application.WorksheetFunction.VLookup(searchData, _
                                    Range("B1:C2000"), 2, False)
    If buf1 = "" Then
        buf1 = "検索データなし"
    End If
    On Error GoTo 0
    Debug.Print "高速化有りの処理時間:" & EndTimer2()

    '高速化なし
    Call StartTimer2
    For i = 1 To 2000
        If Cells(i, 2) = searchData Then
            buf2 = Cells(i, 3)
            Exit For
        End If
    Next i
    If buf2 = "" Then
        buf2 = "検索データなし"
    End If
    Debug.Print "高速化無しの処理時間:" & EndTimer2()
End Sub

【実行結果】

回数高速化:有高速化:無削減効率
10.00006030.002565097.65%
20.00006100.002349297.41%
30.00005900.002347197.49%
40.00005590.002345297.62%
50.00005520.002346897.65%
平均0.00005830.002391097.56%

※ データが4,5件の場合、For文の方が速くなる場合があります。

その他

処理途中でDebug.Printしすぎない

プログラム中に Debug.Print を書くと、イミディエイトウィンドウに変数などの値を出力できます。 テストやデバッグで非常に便利な機能ですが、出力のたびに画面描画が行われるため、処理が大きく遅くなる点に注意が必要です。
特に反復処理の中に「Debug.Print」を入れると、膨大な出力が発生して処理速度が極端に低下してしまいます。これは、VBE画面を閉じていても同じです。
そのため、「Debug.Print」は『必要な箇所だけに限定して使う』、『ループ中では件数を絞る』などの工夫をすると、安全に活用できます。

処理時間計測プログラム

Sub SampleSw5_1()
    Dim i As Long
    Dim num(1 To 3) As Long
    
    '高速化有り
    Call StartTimer2
    For i = 0 To 1000
        num(1) = i: num(2) = i: num(3) = i
    Next i
    Cells(1, 1) = "Debug.Print無しの処理時間:" & EndTimer2
    
    '高速化有り
    Call StartTimer2
    For i = 0 To 1000
        num(1) = i: num(2) = i: num(3) = i
        Debug.Print "num(1) = " & num(1) & _
                    ", num(2) = " & num(2) & _
                    ", num(3) = " & num(3)
    Next i
    Cells(2, 1) = "Debug.Print1回の処理時間:" & EndTimer2

    '高速化無し
    Call StartTimer2
    For i = 0 To 1000
        num(1) = i: num(2) = i: num(3) = i
        Debug.Print "num(1) = " & num(1)
        Debug.Print "num(2) = " & num(2)
        Debug.Print "num(3) = " & num(3)
    Next i
    Cells(3, 1) = "Debug.Print3回の処理時間:" & EndTimer2
End Sub

【実行結果】

VBE表示した状態で、VBEの実行ボタンより実行した場合

回数表示0回表示1回表示3回削減効率※
10.00001441.50057024.424520999.99%
20.00002821.58337334.315421599.99%
30.00004301.54057944.427137699.99%
40.00001421.55805014.337899599.99%
50.00004101.55647724.351373799.99%
平均0.00002821.54781004.371271099.99%

VBEを閉じた状態で、シート上のボタンからプログラムを実行した場合

回数表示0回表示1回表示3回削減効率※
10.00003021.33831313.901070699.99%
20.00001471.31084303.902384899.99%
30.00003031.35597143.936790699.99%
40.00001481.32696743.924562499.99%
50.00003071.29873403.879240099.99%
平均0.00002411.32616603.908810099.99%

※ 削減効率は、「表示0回」と「表示3回」の比較です。

値渡しと参照渡しの処理速度の違い

自作関数では、呼び出し時に値を受け取るための「引数」を指定できます。 引数の渡し方には「値渡し(ByVal)」と「参照渡し(ByRef)」の 2通りがあります。 ここでは、値渡しと参照渡しで、処理速度に違いがあるかを、Long型・Double型・String型の3種類のデータ型で比較してみます。

処理時間計測プログラム

Sub SampleSw5_2_ByRef_Long(ByRef i&, ByRef j&)
    Dim buf&
    buf = i * j
End Sub
Sub SampleSw5_2_ByVal_Long(ByVal i&, ByVal j&)
    Dim buf&
    buf = i * j
End Sub

Sub SampleSw5_2_ByRef_Double(ByRef i#, ByRef j#)
    Dim buf#
    buf = i * j
End Sub
Sub SampleSw5_2_ByVal_Double(ByVal i#, ByVal j#)
    Dim buf#
    buf = i * j
End Sub

Sub SampleSw5_2_ByRef_String(ByRef i$, ByRef j$)
    Dim buf$
    buf = i & j
End Sub
Sub SampleSw5_2_ByVal_String(ByVal i$, ByVal j$)
    Dim buf$
    buf = i & j
End Sub

Sub SampleSw5_2()
    Dim i As Long
    
    'ByRef + Long
    Call StartTimer2
    For i = 1 To 100000
        SampleSw5_2_ByRef_Long i, 1
    Next i
    Debug.Print "ByRef+Longの処理時間:" & EndTimer2
    
    'ByVal + Long
    Call StartTimer2
    For i = 1 To 100000
        SampleSw5_2_ByVal_Long i, 1
    Next i
    Debug.Print "ByVal+Longの処理時間:" & EndTimer2
    
    'ByRef + Double
    Call StartTimer2
    For i = 1 To 100000
        SampleSw5_2_ByRef_Double 1.2345, 5.6789
    Next i
    Debug.Print "ByRef+Doubleの処理時間:" & EndTimer2
    
    'ByVal + Double
    Call StartTimer2
    For i = 1 To 100000
        SampleSw5_2_ByVal_Double 1.2345, 5.6789
    Next i
    Debug.Print "ByVal+Doubleの処理時間:" & EndTimer2
    
    'ByRef + String
    Call StartTimer2
    For i = 1 To 100000
        SampleSw5_2_ByRef_String "ABC", "CDE"
    Next i
    Debug.Print "ByRef+Stringの処理時間:" & EndTimer2
    
    'ByVal + String
    Call StartTimer2
    For i = 1 To 100000
        SampleSw5_2_ByVal_String "ABC", "CDE"
    Next i
    Debug.Print "ByVal+Stringの処理時間:" & EndTimer2
End Sub

【実行結果】

Long型

回数ByRef/LongByVal/Long比較
10.00420090.003341879.55%
20.00696270.003416349.07%
30.00636560.003260551.22%
40.00592860.003524159.44%
50.00591310.004013767.88%
平均0.00587420.003511359.77%

Double型

回数ByRef/DoubleByVal/Double比較
10.00501910.003771275.14%
20.00506490.003658572.23%
30.00514430.003638470.73%
40.00493800.003754176.02%
50.00509200.003676172.19%
平均0.00505170.003699773.24%

String型

回数ByRef/StringByVal/String比較
10.01273730.0156570122.92%
20.01367510.0163442119.52%
30.01280560.0155236121.23%
40.01281250.0152671119.16%
50.01349930.0148518110.02%
平均0.01310600.0155287118.49%

警告を表示させないと速くなる?

Application.DisplayAlerts = False とすると、ファイル保存や削除時の確認メッセージを表示しない設定にできます。元に戻す場合は True を指定します。
「この設定で処理が速くなるのでは?」とよく思われますが、結論としては速くなりません。 むしろ、フラグの切替処理にオーバーヘッドがあるため、頻繁に切り替えると逆に遅くなります。
そのため、使いどころは「ブックを閉じるときに保存確認を出さない」など、ユーザー操作を省く目的に限定するのが安全です。速度向上を目的に使うのは避けましょう。

処理時間計測プログラム

Sub SampleSw5_3()
    Dim i As Long
    Dim j As Long
    Dim buf As String
    
    '通常通り処理をする
    Call StartTimer
    For i = 1 To 100000
        buf = "test"
    Next i
    Debug.Print "DisplayAlertsを切り替えない場合の処理時間:" & EndTimer

    '警告などを表示しない設定で処理をする
    Call StartTimer
    Application.DisplayAlerts = False
    For i = 1 To 100000
        buf = "test"
    Next i
    Application.DisplayAlerts = True
    Debug.Print "DisplayAlertsを切り替えた場合の処理時間:" & EndTimer
End Sub

【実行結果】

回数警告の操作なし警告オフに切り替え比率
10.00275040.0088009319.99%
20.00237990.0074404312.63%
30.00288980.0081828283.16%
40.00294390.0074425252.81%
50.00339260.0086217254.13%
平均0.00287130.0080977282.02%

関連リンク

ページの先頭へ