Wie testest du die Laufzeit von VBA-Code?

95

Gibt es Code in VBA, mit dem ich eine Funktion umbrechen kann, der mir die Ausführungszeit mitteilt, damit ich die verschiedenen Laufzeiten von Funktionen vergleichen kann?

Lance Roberts
quelle

Antworten:

81

Wenn Ihre Funktionen nicht sehr langsam sind, benötigen Sie einen Timer mit sehr hoher Auflösung. Das genaueste, das ich kenne, ist QueryPerformanceCounter. Google es für weitere Informationen. Versuchen Sie, Folgendes in eine Klasse zu verschieben, nennen Sie es CTimersay, dann können Sie eine Instanz irgendwo global erstellen und einfach .StartCounterund aufrufen.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
quelle
2
Ich habe dies in Excel VBA implementiert (Hinzufügen des Overheads, wie in diesem KB-Artikel erwähnt: support.microsoft.com/kb/172338 . Es hat großartig funktioniert. Danke.
Lance Roberts
2
Danke, das funktioniert auch gut für mich. TimeElapsed()gibt das Ergebnis in Millisekunden an. Ich habe keine Overhead-Kompensation implementiert, da ich mir mehr Sorgen über die Auswirkung eines Ruckelns bei der Overhead-Berechnung als über die perfekte Genauigkeit gemacht habe.
Justin
2
Das ist viel belauscht (in zu verwaltenden Codezeilen) - wenn Sie mit einer Genauigkeit von ~ 10 ms leben können, gibt die folgende Antwort von @ Kodak dasselbe in einer Codezeile (Import GetTickCountaus Kernel32).
BrainSlugs83
Wie benutzt man StartCounterUnd TimeElapsed? Ich habe CTimeram Anfang einen Instanz-Timer von gemacht und With StartCounterich habe gerade geschrieben, .StartCounternachdem mein Sub angefangen hat und .TimeElapsedund es hat mir geantwortet Invalid use of property. Wenn ich geschweige .StartCounterdenn sage, dass ein Objekt nicht gesetzt ist.
Revolucion für Monica
Für Excel 2010: Declare PtrSafe Function stackoverflow.com/questions/21611744/…
user3226167
48

Die Timer-Funktion in VBA gibt Ihnen die Anzahl der seit Mitternacht verstrichenen Sekunden auf 1/100 Sekunde an.

Dim t as single
t = Timer
'code
MsgBox Timer - t
dbb
quelle
18
Das würde nicht funktionieren - man kann nicht mehr Auflösung daraus ziehen, den Durchschnitt so zu nehmen.
Andrew Scagnelli
2
Wenn Sie die Leistung in VBA messen, ist es jedoch nicht schlecht, eine Auflösung von 1/100 Sekunde zu erhalten. - Das Aufrufen der Timing-Anrufe allein kann einige ms dauern. Wenn der Anruf so schnell ist, dass Sie so viel Auflösung benötigen, um ihn zeitlich festzulegen, benötigen Sie wahrscheinlich keine Leistungsdaten zu diesem Anruf.
BrainSlugs83
Hinweise: Auf dem Mac ist der Timer nur auf eine Sekunde genau - und dies kann zu negativen Zahlen führen, wenn er vor Mitternacht beginnt und nach Mitternacht endet
TmTron
32

Wenn Sie versuchen, die Zeit wie eine Stoppuhr zurückzugeben, können Sie die folgende API verwenden, die die Zeit seit dem Systemstart in Millisekunden zurückgibt:

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

nach http://www.pcreview.co.uk/forums/grab-time-milliseconds-included-vba-t994765.html (da timeGetTime in winmm.dll für mich nicht funktionierte und QueryPerformanceCounter für die erforderliche Aufgabe zu kompliziert war)

Kodak
quelle
Dies ist eine großartige Antwort. Die Anmerkung: die Genauigkeit der zurückgesandten Daten in Millisekunden ist jedoch der Zähler nur präzise auf etwa 1 / 100stel Sekunde über MSDN (das heißt, es könnte von 10 bis 16 ms aus): msdn.microsoft.com / de-de / library / windows / desktop /…
BrainSlugs83
hmm, wenn die Auflösung hier die gleiche ist wie beim Timer, dann würde ich mit dem Timer
Kodak
Was ist der Public Declare Function ...Teil? Es erzeugt einen Fehler beim Hinzufügen Ihres Codes am Ende von mir
Revolucion für Monica
Sie müssen diese öffentliche Erklärung an die Spitze Ihres Moduls verschieben
Kodak
3
Sub Macro1()
    Dim StartTime As Double
    StartTime = Timer

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

Ausgabe:

Laufzeit: 00:00:02

Gajendra Santosh
quelle
2

Wir haben seit vielen Jahren eine auf timeGetTime basierende Lösung in winmm.dll für Millisekundengenauigkeit verwendet. Siehe http://www.aboutvb.de/kom/artikel/komstopwatch.htm

Der Artikel ist in deutscher Sprache, aber der Code im Download (eine VBA-Klasse, die den DLL-Funktionsaufruf umschließt) ist einfach genug zu verwenden und zu verstehen, ohne den Artikel lesen zu können.

Tom Jürgens
quelle
1

Sekunden mit 2 Dezimalstellen:

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

Sekundenformat

Millisekunden:

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

Millisekundenformat

Millisekunden mit Komma-Trennzeichen:

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

Millisekunden mit Komma-Trennzeichen

Lassen Sie dies hier für alle, die nach einem einfachen Timer suchen, der wie ich mit Sekunden bis 2 Dezimalstellen formatiert ist. Dies sind kurze und süße kleine Timer, die ich gerne benutze. Sie nehmen nur eine Codezeile am Anfang des Unter- oder Funktionsbereichs und am Ende wieder eine Codezeile ein. Diese sollen nicht verrückt genau sein, ich persönlich kümmere mich im Allgemeinen nicht um weniger als eine Hundertstelsekunde, aber der Millisekunden-Timer gibt Ihnen die genaueste Laufzeit dieser 3. Ich habe Sie auch gelesen kann die falsche Anzeige erhalten, wenn es während der Überquerung von Mitternacht läuft, eine seltene Instanz, aber nur zu Ihrer Information.

technoman23
quelle