2014/02/18

VBA With~End Withによる高速化



こんにちは!高速化ネタが大好きなtknriaです!


繰り返し処理をしたい場合にちょー頼りになるVBA。

仕事では120%お世話になりまくりです。


そんなVBAですが、たま~に「処理遅いな。。」と感じてしまうこともあります。

原因はいろいろありますが、解決策の一つとして考えられ、すぐにでも導入できるものを紹介します。

それが「With ~ End With」構文。



■ Withを使ってみる(コード例)


まず、Excelのsheet上に、2つボタンを用意してください。




これらのボタンに以下を割り当てます。


'----- 以下、ソース -----

Const YOKO_MAX = 100
Const TATE_MAX = 5000
Const heikin = 10


Sub ベタ更新_Click()
    Dim start As Double
    Dim finish As Double
    Dim time_total As Double
    Dim i As Integer
    Dim j As Integer
    Dim kaisuu As Integer
 
    time_total = 0
 
    For kaisuu = 1 To heikin
        Worksheets("Sheet1").Range("A1:KN10000").Clear
        start = Timer
        For i = 1 To TATE_MAX
            For j = 1 To YOKO_MAX
                Worksheets("Sheet1").Cells(j, i) = 1
            Next j
        Next i
        finish = Timer
'        MsgBox ("ベタ更新:" & finish - start & "sec")
        time_total = time_total + (finish - start)
    Next kaisuu
 
    Worksheets("Sheet2").Range("B10") = time_total / heikin
 
End Sub


Sub with更新_Click()
    Dim start As Double
    Dim finish As Double
    Dim time_total As Double
    Dim i As Integer
    Dim j As Integer
    Dim kaisuu As Integer
 
    time_total = 0
 
With Worksheets("sheet1")
    For kaisuu = 1 To heikin
        .Range("A1:AI100").Clear
        start = Timer
        For i = 1 To TATE_MAX
            For j = 1 To YOKO_MAX
                .Cells(j, i) = 1
            Next j
        Next i
        finish = Timer
'        MsgBox ("with更新:" & finish - start & "sec")
        time_total = time_total + (finish - start)
    Next kaisuu
End With

    Worksheets("Sheet2").Range("B10") = time_total / heikin
 
End Sub

'----- 以上、ソース -----


n行、100列の幅に1を代入するというマクロですが、代入にかかった時間を計測しています。
また、今回は10回の試行の平均時間で比較します。



■ Withの効果!


横軸にn行、縦軸に平均時間secをとったグラフが以下の通り。



データ数10万個(1000行、100列)ともなると、約10%の時間削減効果が出てきています。

10%の時間削減は、その作業にかかる人件費の10%削減に繋がります!



■ 何で差が出るの?


この差はどうして生じるかというところですが、

        For i = 1 To TATE_MAX
            For j = 1 To YOKO_MAX
                Worksheets("Sheet1").Cells(j, i) = 1
            Next j
        Next i

だと、Worksheets("Sheet1")→Cells(j,i)→i,jをインクリ→
        Worksheets("Sheet1")→Cells(j,i)→i,jをインクリ→…
と処理されていくところが、


        With Worksheets("Sheet1")
            For i = 1 To TATE_MAX
                For j = 1 To YOKO_MAX
                    .Cells(j, i) = 1
                Next j
            Next i
        End With

だと、Worksheets("Sheet1")→Cells(j,i)→i,jをインクリ→Cells(j,i)→インクリ→Cells(j,i)→…
と処理されるためです。

家族の紹介をするときに、「○○家」ということを最初に伝えて、名前だけ紹介するのと同じ感覚でしょうか。
中学校の理科でいうと、信号を脳まで送らずに脊髄で処理する「反射」と同じ?



■ 億劫なあとがき


実験や評価の自動化ツールとしてExcel VBAを用いる人も多いと思うので、遅いと感じる人は見直してみてはいかがでしょうか。