Comment testez-vous la durée d'exécution du code VBA?

95

Y a-t-il du code dans VBA avec lequel je peux encapsuler une fonction qui me fera savoir le temps qu'il a fallu pour s'exécuter, afin que je puisse comparer les différents temps d'exécution des fonctions?

Lance Roberts
la source

Réponses:

81

À moins que vos fonctions ne soient très lentes, vous aurez besoin d'un minuteur à très haute résolution. Le plus précis que je connaisse est QueryPerformanceCounter. Recherche le sur Google pour plus d'informations. Essayez de pousser ce qui suit dans une classe, appelez-le par CTimerexemple, vous pouvez alors créer une instance quelque part dans le monde et simplement appeler .StartCounteret.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
la source
2
J'ai implémenté cela dans Excel VBA (en ajoutant les frais généraux comme mentionné dans cet article de la base de connaissances : support.microsoft.com/kb/172338 . Cela a très bien fonctionné. Merci.
Lance Roberts
2
Merci, cela fonctionne bien pour moi aussi. TimeElapsed()donne le résultat en millisecondes. Je n'ai pas mis en place de compensation des frais généraux car j'étais plus inquiet de l'effet d'un bégaiement dans le calcul des frais généraux que d'une précision parfaite.
Justin le
2
C'est beaucoup d'entendu (en lignes de code à gérer) - si vous pouvez vivre avec une précision d'environ 10 ms, la réponse de @ Kodak ci-dessous donne la même chose dans une ligne de code (importation GetTickCountdepuis kernel32).
BrainSlugs83
Comment utilisez-vous StartCounterAnd TimeElapsed? J'ai fait une instance de Timer CTimerau début et With StartCounterje viens d'écrire .StartCounteraprès le début de mon sous-marin .TimeElapsedet cela m'a répondu Invalid use of property. Quand j'en ai encore .StartCountermoins, cela me dit qu'un objet n'est pas défini.
Revolucion pour Monica
Pour Excel 2010: Declare PtrSafe Function stackoverflow.com/questions/21611744/…
user3226167
48

La fonction Timer dans VBA vous donne le nombre de secondes écoulées depuis minuit, à 1/100 de seconde.

Dim t as single
t = Timer
'code
MsgBox Timer - t
dbb
la source
18
Cela ne fonctionnerait pas - vous ne pouvez pas obtenir plus de résolution en prenant la moyenne comme ça.
Andrew Scagnelli le
3
Pourtant, si vous mesurez les performances en VBA, obtenir une résolution de 1 / 100e de seconde n'est pas mauvais. - Le seul appel des appels de synchronisation peut prendre quelques ms. Si l'appel est si rapide que vous avez besoin d'autant de résolution pour le chronométrer, vous n'avez probablement pas besoin de données de performances sur cet appel.
BrainSlugs83
notes: sur Mac, la minuterie n'est précise qu'à une seconde - et cela peut obtenir des nombres négatifs s'il commence avant minuit et se termine après minuit
TmTron
32

Si vous essayez de renvoyer l'heure comme un chronomètre, vous pouvez utiliser l'API suivante qui renvoie l'heure en millisecondes depuis le démarrage du système:

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

après http://www.pcreview.co.uk/forums/grab-time-milliseconds-included-vba-t994765.html (car timeGetTime dans winmm.dll ne fonctionnait pas pour moi et QueryPerformanceCounter était trop compliqué pour la tâche nécessaire)

Kodak
la source
C'est une excellente réponse. À noter: la précision des données renvoyées est en millisecondes, cependant, le compteur n'est précis qu'à environ 1 / 100e de seconde (c'est-à-dire qu'il pourrait être désactivé de 10 à 16 ms) via MSDN: msdn.microsoft.com / fr-fr / library / windows / desktop /…
BrainSlugs83
hmm, si la résolution est la même ici qu'avec la minuterie, j'irais avec la minuterie
Kodak
Quelle est la Public Declare Function ...partie? Cela crée une erreur lors de l'ajout de votre code au bas du mien
Revolucion for Monica
Vous devez déplacer cette déclaration publique en haut de votre module
Kodak
3
Sub Macro1()
    Dim StartTime As Double
    StartTime = Timer

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

Production:

Durée: 00:00:02

Gajendra Santosh
la source
2

Nous avons utilisé une solution basée sur timeGetTime dans winmm.dll pour une précision à la milliseconde pendant de nombreuses années. Voir http://www.aboutvb.de/kom/artikel/komstopwatch.htm

L'article est en allemand, mais le code dans le téléchargement (une classe VBA encapsulant l'appel de fonction dll) est assez simple à utiliser et à comprendre sans pouvoir lire l'article.

Tom Juergens
la source
1

Secondes avec 2 espaces décimaux:

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

format des secondes

Millisecondes:

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

format en millisecondes

Millisecondes avec séparateur de virgule:

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

Millisecondes avec séparateur de virgule

Il suffit de laisser ceci ici pour tous ceux qui recherchaient une simple minuterie formatée avec des secondes à 2 espaces décimaux comme je l'étais. Ce sont des minuteries courtes et douces que j'aime utiliser. Ils n'occupent qu'une seule ligne de code au début de la sous ou de la fonction et une ligne de code à nouveau à la fin. Celles-ci ne sont pas censées être d'une précision folle, je ne me soucie généralement pas de moins de 1 / 100e de seconde personnellement, mais le minuteur en millisecondes vous donnera le temps d'exécution le plus précis de ces 3. Je vous ai aussi lu peut obtenir une lecture incorrecte si elle s'exécute pendant la traversée de minuit, une instance rare mais juste pour info.

technoman23
la source