Матч против CountIf против Find feat. MicroTimer
Я провел некоторое исследование с использованием MicroTimer
, написанного Чарльзом Уильямсом , которое я нашел в статье Поиск узких мест в Excel VBA .
Описание
Извините за результаты в комментариях к коду, но мне потребовалось некоторое время, чтобы создать следующую настройку:
fillColumns
- заполняет диапазон, определенный Cols
, строкой, определенной FillString
. TestAll
- выполняет все четыре теста. countFillChars
- подсчитывает количество символов в FillString
. saveBook
- сохраняет рабочую книгу. TestWFMatch
- проверяет WorksheetFunction
версию Match
. TestWFMatch
- проверяет Application
версия Match
. TestCountIf
- тестирует WorksheetFunction
версию CountIf
. TestFind
- тестирует Find Method
. MicroTimer
- «измеряет» время, необходимое для завершения sh теста.
Результаты показывают время, прошедшее в секундах. Все тесты, за исключением теста, использующего сопоставленный результат в первой ячейке, выполняются для данных, в которых нет совпадения. Функции просто «l oop через ячейки» из 10 столбцов, заполненных одной и той же строкой, и «выполняют свою работу». Отсюда можно сделать вывод, что тестирование только «поцарапало» поверхность.
Заключение
Очевидные (для меня не очень научные c) выводы этих тестов следующие:
- Когда
Match
используется, он автоматически останавливается, когда совпадение найдено, в то время как CountIf
должно считаться до конца диапазона. Поэтому Match
должно быть быстрее, но не может рассчитывать. Find
(один из моих любимых) даже близко не подходит, но он может сделать кучу других вещей. Я специально использовал те же начала строки, которые показали Мне кажется, что чем больше совпадений символов, тем медленнее становятся функции, что-то вроде:
- '
This
' - это даже близко не имеет 4 символа (особенно Match
). - '
This string cont
' - приближение (CountIf
уже страдает, Match
остается быстрым.) - Мне кажется, что функции сначала подсчитывают количество символов и только тогда проверяют, совпадают ли они.
- Но шокер - это когда первый символ отличается. Затем
CountIf
«оживает», даже Match
на том же количестве символов, на котором оно было только проверено.
Можно сделать еще много выводов и тесты должны быть выполнены. Инструменты здесь, остальное зависит от вас. Вы готовы? Начните с теста для AutoFilter
, который я, к сожалению, здесь не включил.
Примечания:
14.5s
против 0.0004s
при совпадении не должно вас смущать, потому что это крайность: Match
10 раз нашел совпадение в первой ячейке, а CountIf
10 раз считает более миллиона совпадений.
Код
Поместите код в модуль. Вам просто нужно иметь Sheet1
(CodeName).
Option Explicit
' saveBook 12-15s if filled
' Fill String WFCountIf WFMatch Match Find FillC
' This 3.0 0.4 0.4 4.5 4.5
' This str 4.7 0.4 0.4 4.5 4.7
' This string cont 9.0 0.4 0.4 4.5 5.0
' This string contains 32c 11.5 0.4 0.4 4.5 5.2
' This string contains 32 chars!!! 14.5 0.0005 0.0007 0.001 5.5
' This string contains 32 chars!!. 14.5 14.5 14.5 19.0 5.5
' This string contained 32 chars! I added some more to make it 64.
' 11.0 11.0 11.0 72.0 6.4
' 32chars contained in this string 1.8 2.0 2.0 6.5 5.5
Private Const FillString As String = "32chars contained in this string"
Private Const Srch As String = "This string contains 32 chars!!!"
Private Const Cols As String = "A:J"
Private Const UB As Long = 9
Private i As Long
Private tStart As Double
Private tEnd As Double
Sub fillColumns()
tStart = MicroTimer
Sheet1.Columns(Cols).Value = FillString
Debug.Print MicroTimer - tStart
End Sub
Sub TestAll()
TestWFMatch
TestMatch
TestWFCountIf
TestFind
End Sub
Sub countFillChars()
Debug.Print Len(FillString)
End Sub
Sub saveBook()
tStart = MicroTimer
ThisWorkbook.Save
Debug.Print MicroTimer - tStart
End Sub
Sub TestWFMatch()
Dim ws As Worksheet: Set ws = Sheet1
Dim Result(UB) As Boolean
Dim FResult As Long
tStart = MicroTimer
With Application.WorksheetFunction
For i = 1 To 10
On Error Resume Next
FResult = _
.Match(Srch, ws.Range(Cols).Columns(i), 0)
Result(i - 1) = Err.Number = 0
On Error GoTo 0
Next i
End With
tEnd = MicroTimer - tStart
Debug.Print String(10, "-") & vbCrLf & tEnd * 1000
For i = 0 To 9: Debug.Print Result(i): Next
End Sub
Sub TestMatch()
Dim ws As Worksheet: Set ws = Sheet1
Dim Result(UB) As Boolean
tStart = MicroTimer
With Application
For i = 1 To 10
Result(i - 1) = _
Not IsError(.Match(Srch, ws.Range(Cols).Columns(i), 0))
Next i
End With
tEnd = MicroTimer - tStart
Debug.Print String(10, "-") & vbCrLf & tEnd * 1000
For i = 0 To 9: Debug.Print Result(i): Next
End Sub
Sub TestWFCountIf()
Dim ws As Worksheet: Set ws = Sheet1
Dim Result(UB) As Boolean
tStart = MicroTimer
With Application.WorksheetFunction
For i = 1 To 10
Result(i - 1) = _
.CountIf(ws.Range(Cols).Columns(i), Srch) > 0
Next i
End With
tEnd = MicroTimer - tStart
Debug.Print String(10, "-") & vbCrLf & tEnd * 1000
For i = 0 To UB: Debug.Print Result(i): Next
End Sub
Sub TestFind()
Dim ws As Worksheet: Set ws = Sheet1
Dim Result(UB) As Boolean
tStart = MicroTimer
With Application
For i = 1 To 10
Result(i - 1) = _
Not ws.Columns(i).Find(What:=Srch, After:=ws.Cells( _
ws.Rows.Count, i), LookIn:=xlFormulas) Is Nothing
Next i
End With
tEnd = MicroTimer - tStart
Debug.Print String(10, "-") & vbCrLf & tEnd * 1000
For i = 0 To 9: Debug.Print Result(i): Next
End Sub
'START ****************************************************************** START'
Private Declare Function getFrequency Lib "Kernel32" _
Alias "QueryPerformanceFrequency" (cyFrequency As Currency) As Long
Private Declare Function getTickCount Lib "Kernel32" _
Alias "QueryPerformanceCounter" (cyTickCount As Currency) As Long
'******************************************************************************'
' Purpose: Returns processor time. Used to determine small time intervals '
' passed between places in code. '
' Returns: Processor time. '
' Precedents: getFrequency, getTickCount '
'******************************************************************************'
Function MicroTimer() As Double
' Returns seconds.
Dim cyTicks1 As Currency
Static cyFrequency As Currency
MicroTimer = 0
' Get frequency.
If cyFrequency = 0 Then getFrequency cyFrequency
' Get ticks.
getTickCount cyTicks1
' Seconds
If cyFrequency Then MicroTimer = cyTicks1 / cyFrequency
End Function
'******************************************************************************'
' Example: '
'------------------------------------------------------------------------------'
'Sub MicroTimerExample()
' Dim dTime As Double
' dTime = MicroTimer
' Dim i As Long: For i = 1 To 1000000: Next
' Debug.Print MicroTimer - dTime
'End Sub
'******************************************************************************'
' Source '
' Article '
' Title: Excel 2010 Performance: Improving Calculation Performance
' Link: https://docs.microsoft.com/en-us/previous-versions/office/_
' developer/office-2010/ff700515(v%3doffice.14)
'******************************************************************************'
' Found: 2018 '
' Origin: VBAddin.xlam '
'******************************************************************************'