¿Cómo se prueba el tiempo de ejecución del código VBA?

95

¿Hay un código en VBA con el que pueda ajustar una función que me permita saber el tiempo que tardó en ejecutarse, de modo que pueda comparar los diferentes tiempos de ejecución de las funciones?

Lance Roberts
fuente

Respuestas:

81

A menos que sus funciones sean muy lentas, necesitará un temporizador de muy alta resolución. El más preciso que conozco es QueryPerformanceCounter. Google para obtener más información. Intente insertar lo siguiente en una clase, llámelo CTimerdigamos, luego puede hacer una instancia en algún lugar global y simplemente llamar .StartCountery.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
fuente
2
Implementé esto en Excel VBA (agregando el Overhead como se menciona en este artículo de KB: support.microsoft.com/kb/172338 . Funcionó muy bien. Gracias.
Lance Roberts
2
Gracias, esto también funciona bien para mí. TimeElapsed()da el resultado en milisegundos. No implementé ninguna compensación de gastos generales porque estaba más preocupado por el efecto de un tartamudeo en el cálculo de gastos generales que la precisión perfecta.
Justin
2
Eso es mucho escuchar (en líneas de código para administrar): si puede vivir con una precisión de ~ 10ms, la respuesta de @ Kodak a continuación da lo mismo en una línea de código (importando GetTickCountdesde kernel32).
BrainSlugs83
¿Cómo se usa StartCounterAnd TimeElapsed? Hice una instancia de Timer de CTimeral principio y With StartCounterescribí .StartCounterdespués de que mi sub comenzara y .TimeElapsedme respondió Invalid use of property. Cuando lo dejo .StartCountersolo, me dice que un objeto no está configurado.
Revolución para Monica
Para Excel 2010: Declare PtrSafe Function stackoverflow.com/questions/21611744/…
user3226167
48

La función de temporizador en VBA le da el número de segundos transcurridos desde la medianoche, a 1/100 de segundo.

Dim t as single
t = Timer
'code
MsgBox Timer - t
dbb
fuente
18
Eso no funcionaría, no se puede obtener más resolución tomando el promedio de esa manera.
Andrew Scagnelli
3
Aún así, si está midiendo el rendimiento en VBA, obtener una resolución de 1/100 de segundo no es malo. - La invocación de las llamadas de temporización por sí sola podría tardar un par de ms. Si la llamada es tan rápida que necesita tanta resolución para cronometrarla, probablemente no necesite datos de rendimiento sobre esa llamada.
BrainSlugs83
notas: en Mac, el temporizador solo tiene una precisión de un segundo, y esto puede obtener números negativos si comienza antes de la medianoche y termina después de la medianoche
TmTron
32

Si está tratando de devolver la hora como un cronómetro, puede usar la siguiente API que devuelve la hora en milisegundos desde el inicio del sistema:

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

después de http://www.pcreview.co.uk/forums/grab-time-milliseconds-included-vba-t994765.html (ya que timeGetTime en winmm.dll no funcionaba para mí y QueryPerformanceCounter era demasiado complicado para la tarea necesaria)

Kodak
fuente
Esta es una respuesta genial. Nota: la precisión de los datos devueltos está en milisegundos, sin embargo, el contador solo tiene una precisión de aproximadamente 1/100 de segundo (es decir, podría estar desviado de 10 a 16 ms) a través de MSDN: msdn.microsoft.com / en-us / library / windows / desktop /…
BrainSlugs83
hmm, si la resolución es la misma aquí que con el temporizador, entonces iría con el temporizador
Kodak
Cual es la Public Declare Function ...parte Crea un error al agregar su código en la parte inferior del mío
Revolución para Monica
Debe mover esta declaración pública a la parte superior de su módulo
Kodak
3
Sub Macro1()
    Dim StartTime As Double
    StartTime = Timer

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

Salida:

Tiempo de ejecución: 00:00:02

Gajendra Santosh
fuente
2

Hemos utilizado una solución basada en timeGetTime en winmm.dll para una precisión de milisegundos durante muchos años. Ver http://www.aboutvb.de/kom/artikel/komstopwatch.htm

El artículo está en alemán, pero el código de la descarga (una clase de VBA que envuelve la llamada a la función dll) es lo suficientemente simple de usar y comprender sin poder leer el artículo.

Tom Juergens
fuente
1

Segundos con 2 espacios decimales:

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

formato de segundos

Milisegundos:

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

formato de milisegundos

Milisegundos con separador de coma:

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

Milisegundos con separador de comas

Solo dejo esto aquí para cualquiera que esté buscando un temporizador simple formateado con segundos a 2 espacios decimales como yo. Estos son pequeños temporizadores cortos y dulces que me gusta usar. Solo ocupan una línea de código al comienzo de la función o sub y una línea de código nuevamente al final. Estos no están destinados a ser muy precisos, por lo general no me importa nada menos de 1/100 de segundo personalmente, pero el temporizador de milisegundos le dará el tiempo de ejecución más preciso de estos 3. También le he leído puede obtener la lectura incorrecta si se ejecuta mientras cruza la medianoche, un caso raro pero solo para su información.

technoman23
fuente