検索の速度を測ってみた

検索の所要時間を調べた

ちょっと興味があったので、調べてみた。

f:id:akashi_keirin:20190114223859j:plain

このように、10001000列目のセル、すなわちALL1000セルに「ち~んw」という値(笑)を入力したシートを準備する。

で、この「ち~んw」を検索するのにどのぐらい時間がかかるものなのか、測ってみたわけである。

検索方法

私は所詮素人なので、次の三つの方法を用いた。

  1. 1000×1000のセル範囲のセルのValueプロパティを一つ一つ調べる方式
  2. 1000×1000のセル範囲のRangeオブジェクトのValueプロパティの値を一旦二次元配列にぶち込んで、同じく一つ一つ調べる方式
  3. 1000×1000のセル範囲に対してExcelFindメソッドを使う方式

以上三つである。

実験用のコード

下記のコードを用いる。

時間計測用に、自作のWindowsAPIクラスを用いているが、これについては、

akashi-keirin.hatenablog.com

コチラをどうぞ。WindowsAPI.callGetTickCountメソッドというのは、WindowsAPIのGetTickCountを利用するメソッドです。

リスト1 標準モジュール
'エントリポイント。'
Public Sub testSearch()
  '自作WindowsAPIクラスのインスタンスを用意。'
  Dim winAPI As New WindowsAPI

  'セルを一つ一つ巡回する方式の時間を表示する。'
  Dim startTime As Long
  startTime = winAPI.callGetTickCount
  Dim ret As Boolean
  ret = testSearchByNormalForLoop
  Dim elapsedTime As Double
  elapsedTime = winAPI.callGetTickCount - startTime
  Call showResult(ret, "testSearchByNormalForLoop", elapsedTime)

  '二次元配列にぶち込んで一つ一つ巡回する方式の時間を表示する。'
  startTime = winAPI.callGetTickCount
  ret = testSearchBy2DimensionArray
  elapsedTime = winAPI.callGetTickCount - startTime
  Call showResult(ret, "testSearchBy2DimensionArray", elapsedTime)

  'ExcelのFindメソッドを使う方式の時間を表示する。'
  startTime = winAPI.callGetTickCount
  ret = testSearchByFindMethod
  elapsedTime = winAPI.callGetTickCount - startTime
  Call showResult(ret, "testSearchByFindMethod", elapsedTime)
End Sub

'計測結果を表示するためのメソッド。'
Private Sub showResult(ByVal isSuccessed As Boolean, _
                       ByVal methodName As String, _
                       ByVal elapsedTime As Double)
  Debug.Print methodName & " の結果:"
  If isSuccessed Then
    Debug.Print "「ち~んw」を みつけた!"
  Else
    Debug.Print "見つけられなかった……。"
  End If
  Debug.Print elapsedTime / 1000 & "秒かかった!"
  Debug.Print String(20, "=")
End Sub

'セルを一つ一つ巡回する方式。'
Public Function testSearchByNormalForLoop() As Boolean
'  Dim ret As Boolean'
'  ret = False'
  testSearchByNormalForLoop = False
  Dim sh As Worksheet
  Set sh = ActiveSheet
  Dim r As Long
  Dim c As Long
  For r = 1 To 1000
    For c = 1 To 1000
      If sh.Cells(r, c).Value = "ち~んw" Then _
'        ret = True: Exit For'
         '///アホすぎる誤りを修正'
         testSearchByNormalForLoop = True: _
         Exit Function
    Next
  Next
'  If ret Then testSearchByNormalForLoop = True'
End Function

'一旦二次元配列にぶち込んで一つ一つ巡回する方式。'
Public Function testSearchBy2DimensionArray() As Boolean
'  Dim ret As Boolean'
'  ret = False'
  testSearchBy2DimensionArray = False
  Dim sh As Worksheet
  Set sh = ActiveSheet
  Dim rng As Range
  With sh
    Set rng = .Range(.Cells(1, 1), _
                     .Cells(1000, 1000))
  End With
  Dim ar As Variant
  ar = rng.Value
  Dim r As Long
  Dim c As Long
  For r = 1 To 1000
    For c = 1 To 1000
      If ar(r, c) = "ち~んw" Then _
'        ret = True: Exit For'
         '///アホすぎる誤りを修正'
         testSearchBy2DimensionArray = True: _
         Exit Function
    Next
  Next
'  If ret Then testSearchBy2DimensionArray = True'
End Function

'ExcelのFindメソッドを使う方式。'
Public Function testSearchByFindMethod() As Boolean
  Dim ret As Boolean
  ret = False
  Dim sh As Worksheet
  Set sh = ActiveSheet
  Dim rng As Range
  With sh
  Set rng = .Range(.Cells(1, 1), _
                   .Cells(1000, 1000)).Find(what:="ち~んw")
  End With
  If Not rng Is Nothing Then ret = True
  testSearchByFindMethod = ret
End Function

処理内容はコード内のコメントの通り。行数の割に中身はないw

追記

2018/1/19

コード中、あまりにアホ過ぎる誤りに気付いたので、修正しました。実は、うpした次の日には気付いていたのですが、連休が明けると本業の方が絶讃炎上中となっておりまして、連日アレな感じだったために修正ができなかったのです。

修正箇所は、リスト内にコメントで記した通りです。

実行結果

f:id:akashi_keirin:20190114223921j:plain

ご覧の通り。

1000×1000のセル範囲のセルのValueプロパティを一つ一つ調べる方式

4.016秒。

1000×1000のセル範囲のRangeオブジェクトのValueプロパティの値を一旦二次元配列にぶち込んで、同じく一つ一つ調べる方式

0.235秒。

1000×1000のセル範囲に対してExcelFindメソッドを使う方式

0.015秒。

なんと、まさにケタ違い。

ちょっとビックリの結果。

まあ、一つ目、二つ目については、一番時間がかかるところに値(笑)を配置するという意地悪な実験なんですが。

しかし、こうなると、ExcelFindメソッドの実装がどうなっとるのか、非常に気になるのであります。