Jak testujesz czas działania kodu VBA?

95

Czy w języku VBA jest kod, którym mogę zawinąć funkcję, która poinformuje mnie o czasie jej uruchomienia, aby móc porównać różne czasy działania funkcji?

Lance Roberts
źródło

Odpowiedzi:

81

Jeśli twoje funkcje nie są bardzo wolne, będziesz potrzebować timera o bardzo wysokiej rozdzielczości. Najdokładniejszy, jaki znam, to QueryPerformanceCounter. Google to, aby uzyskać więcej informacji. Spróbuj wypchnąć następujące elementy do klasy, nazwij to CTimerpowiedz, a następnie możesz ustawić instancję w dowolnym miejscu globalnym i po prostu wywołać .StartCounteri.TimeElapsed

Option Explicit

Private Type LARGE_INTEGER
    lowpart As Long
    highpart As Long
End Type

Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As LARGE_INTEGER) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As LARGE_INTEGER) As Long

Private m_CounterStart As LARGE_INTEGER
Private m_CounterEnd As LARGE_INTEGER
Private m_crFrequency As Double

Private Const TWO_32 = 4294967296# ' = 256# * 256# * 256# * 256#

Private Function LI2Double(LI As LARGE_INTEGER) As Double
Dim Low As Double
    Low = LI.lowpart
    If Low < 0 Then
        Low = Low + TWO_32
    End If
    LI2Double = LI.highpart * TWO_32 + Low
End Function

Private Sub Class_Initialize()
Dim PerfFrequency As LARGE_INTEGER
    QueryPerformanceFrequency PerfFrequency
    m_crFrequency = LI2Double(PerfFrequency)
End Sub

Public Sub StartCounter()
    QueryPerformanceCounter m_CounterStart
End Sub

Property Get TimeElapsed() As Double
Dim crStart As Double
Dim crStop As Double
    QueryPerformanceCounter m_CounterEnd
    crStart = LI2Double(m_CounterStart)
    crStop = LI2Double(m_CounterEnd)
    TimeElapsed = 1000# * (crStop - crStart) / m_crFrequency
End Property
Mike Woodhouse
źródło
2
Zaimplementowałem to w Excel VBA (dodając narzut, jak wspomniano w tym artykule KB: support.microsoft.com/kb/172338 . Działało świetnie. Dzięki.
Lance Roberts,
2
Dzięki, dla mnie to też działa dobrze. TimeElapsed()podaje wynik w milisekundach. Nie wdrożyłem żadnej kompensacji kosztów ogólnych, ponieważ bardziej martwiłem się efektem jąkania w obliczeniach narzutów niż idealną dokładnością.
Justin
2
To dużo podsłuchanych (w wierszach kodu do zarządzania) - jeśli możesz żyć z dokładnością ~ 10 ms, odpowiedź @ Kodak poniżej podaje to samo w jednej linii kodu (importowanie GetTickCountz kernel32).
BrainSlugs83
Jak używasz StartCounterAnd TimeElapsed? Zrobiłem instancję Timer CTimerna początku i With StartCounterpo prostu napisałem .StartCounterpo rozpoczęciu mojego subwoofera .TimeElapsedi odpowiedział mi Invalid use of property. Kiedy nie mówiąc .StartCounterjuż o tym, mówi mi, że obiekt nie jest ustawiony.
Revolucion dla Moniki
Dla programu Excel 2010: Declare PtrSafe Function stackoverflow.com/questions/21611744/ ...
user3226167
48

Funkcja Timer w VBA podaje liczbę sekund, które upłynęły od północy, do 1/100 sekundy.

Dim t as single
t = Timer
'code
MsgBox Timer - t
dbb
źródło
18
To by nie zadziałało - nie można uzyskać większej rozdzielczości z takiej średniej.
Andrew Scagnelli
4
Mimo to, jeśli mierzysz wydajność w VBA, uzyskanie rozdzielczości 1/100 sekundy nie jest złe. - Samo wywołanie wywołań czasowych może zająć kilka ms. Jeśli połączenie jest tak szybkie, że potrzebujesz tak dużej rozdzielczości, prawdopodobnie nie potrzebujesz danych dotyczących wydajności tego połączenia.
BrainSlugs83
1
Uwagi: na Macu licznik czasu jest dokładny tylko do jednej sekundy - i może to spowodować ujemne liczby, jeśli zaczyna się przed północą i kończy po północy
TmTron
32

Jeśli próbujesz zwrócić czas jak stoper, możesz użyć następującego interfejsu API, który zwraca czas w milisekundach od uruchomienia systemu:

Public Declare Function GetTickCount Lib "kernel32.dll" () As Long
Sub testTimer()
Dim t As Long
t = GetTickCount

For i = 1 To 1000000
a = a + 1
Next

MsgBox GetTickCount - t, , "Milliseconds"
End Sub

po http://www.pcreview.co.uk/forums/grab-time-milliseconds-included-vba-t994765.html (ponieważ timeGetTime w winmm.dll nie działał dla mnie, a QueryPerformanceCounter był zbyt skomplikowany dla potrzebnego zadania)

kodak
źródło
To świetna odpowiedź. Uwaga: dokładność zwracanych danych jest w milisekundach, jednak licznik jest dokładny tylko do około 1/100 sekundy (to znaczy może być wyłączony o 10 do 16 ms) za pośrednictwem MSDN: msdn.microsoft.com / en-us / library / windows / desktop /…
BrainSlugs83
hmm, jeśli rozdzielczość jest tutaj taka sama jak z
Kodak
Co to za Public Declare Function ...część? Tworzy błąd podczas dodawania kodu na dole mojego
Revolucion for Monica
Musisz przenieść tę publiczną deklarację na górę modułu
Kodak
3
Sub Macro1()
    Dim StartTime As Double
    StartTime = Timer

        ''''''''''''''''''''
            'Your Code'
        ''''''''''''''''''''
    MsgBox "RunTime : " & Format((Timer - StartTime) / 86400, "hh:mm:ss")
End Sub

Wynik:

RunTime: 00:00:02

Gajendra Santosh
źródło
2

Przez wiele lat korzystaliśmy z rozwiązania opartego na timeGetTime w winmm.dll z dokładnością do milisekund. Zobacz http://www.aboutvb.de/kom/artikel/komstopwatch.htm

Artykuł jest w języku niemieckim, ale kod pobierany (klasa VBA opakowująca wywołanie funkcji dll) jest wystarczająco prosty w użyciu i zrozumieniu bez możliwości przeczytania artykułu.

Tom Juergens
źródło
0

Sekundy z 2 miejscami po przecinku:

Dim startTime As Single 'start timer
MsgBox ("run time: " & Format((Timer - startTime) / 1000000, "#,##0.00") & " seconds") 'end timer

format sekund

Milisekundy:

Dim startTime As Single 'start timer
MsgBox ("run time: " & Format((Timer - startTime), "#,##0.00") & " milliseconds") 'end timer

format milisekund

Milisekundy z separatorem przecinków:

Dim startTime As Single 'start timer
MsgBox ("run time: " & Format((Timer - startTime) * 1000, "#,##0.00") & " milliseconds") 'end timer

Milisekundy z separatorem przecinkowym

Zostawiam to tutaj dla każdego, kto szukał prostego timera sformatowanego z sekundami do 2 miejsc po przecinku, tak jak ja. Są to krótkie i słodkie małe timery, których lubię używać. Zajmują tylko jedną linię kodu na początku podrzędnej lub funkcji i jedną linię kodu ponownie na końcu. To nie ma być szalenie dokładne, generalnie nie obchodzi mnie nic mniej niż 1/100 sekundy osobiście, ale licznik milisekund da ci najdokładniejszy czas działania tych trzech. może uzyskać nieprawidłowy odczyt, jeśli zdarzy się, że biegnie podczas przekraczania północy, rzadki przypadek, ale tylko do Twojej wiadomości.

technoman23
źródło
Tylko pierwszy jest przydatny, ponieważ Timer ma rozdzielczość 10 ms.
Gustav