Como você testa o tempo de execução do código VBA?

95

Existe código em VBA com o qual posso encapsular uma função que me avisará quanto tempo levou para ser executado, para que eu possa comparar os diferentes tempos de execução das funções?

Lance Roberts
fonte

Respostas:

81

A menos que suas funções sejam muito lentas, você precisará de um cronômetro de resolução muito alta. O mais preciso que conheço é QueryPerformanceCounter. Pesquise no Google para mais informações. Tente empurrar o seguinte para uma classe, chame-a por CTimerexemplo, então você pode fazer uma instância em algum lugar global e apenas chamar .StartCountere.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
fonte
2
Implementei isso no Excel VBA (adicionando as despesas gerais conforme mencionado neste artigo da KB: support.microsoft.com/kb/172338 . Funcionou muito bem. Obrigado.
Lance Roberts,
2
Obrigado, isso funciona bem para mim também. TimeElapsed()dá o resultado em milissegundos. Não implementei nenhuma compensação de overhead porque estava mais preocupado com o efeito da gagueira no cálculo do overhead do que com a precisão perfeita.
Justin
2
Isso é muito ouvido (em linhas de código para gerenciar) - se você pode viver com uma precisão de aproximadamente 10 ms, a resposta da @Kodak abaixo fornece a mesma coisa em uma linha de código (importando GetTickCountdo kernel32).
BrainSlugs83,
Como você usa o StartCounterAnd TimeElapsed? Eu fiz uma instância Timer de CTimerno início e With StartCountersó escrevi .StartCounterdepois que meu sub começou .TimeElapsede ele me respondeu Invalid use of property. Quando eu o deixo .StartCountersozinho, ele me diz que um objeto não está definido.
Revolucion para Monica
Para excel 2010: Declare PtrSafe Function stackoverflow.com/questions/21611744/…
user3226167
48

A função Timer em VBA fornece o número de segundos decorridos desde a meia-noite até 1/100 de segundo.

Dim t as single
t = Timer
'code
MsgBox Timer - t
dbb
fonte
18
Isso não funcionaria - você não pode obter mais resolução tirando a média assim.
Andrew Scagnelli
3
Ainda assim, se você estiver medindo o desempenho em VBA, obter resolução de 1/100 de segundo não é ruim. - Invocar apenas as chamadas de tempo pode levar alguns ms. Se a chamada for tão rápida que você precisa de muita resolução para cronometrá-la, provavelmente não precisa de dados de desempenho sobre essa chamada.
BrainSlugs83,
notas: no Mac, o cronômetro tem precisão de apenas um segundo - e isso pode
gerar
32

Se você está tentando retornar o tempo como um cronômetro, pode usar a seguinte API, que retorna o tempo em milissegundos desde a inicialização do 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

depois de http://www.pcreview.co.uk/forums/grab-time-milliseconds-included-vba-t994765.html (já que timeGetTime em winmm.dll não estava funcionando para mim e QueryPerformanceCounter era muito complicado para a tarefa necessária)

Kodak
fonte
Esta é uma ótima resposta. Observação: a precisão dos dados retornados é em milissegundos, no entanto, o contador é preciso apenas cerca de 1/100 de segundo (ou seja, ele pode estar errado
BrainSlugs83
hmm, se a resolução for a mesma aqui que com o temporizador, então eu escolheria o temporizador
Kodak
Qual é a Public Declare Function ...parte? Ele cria um erro ao adicionar seu código na parte inferior do meu
Revolucion para Monica
Você precisa mover esta declaração pública para o topo do seu 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

Resultado:

Tempo de execução: 00:00:02

Gajendra Santosh
fonte
2

Há muitos anos, usamos uma solução baseada em timeGetTime em winmm.dll para precisão de milissegundos. Veja http://www.aboutvb.de/kom/artikel/komstopwatch.htm

O artigo está em alemão, mas o código no download (uma classe VBA que envolve a chamada de função dll) é simples o suficiente para usar e entender sem ser capaz de ler o artigo.

Tom Juergens
fonte
1

Segundos com 2 casas decimais:

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

formato de segundos

Milissegundos:

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

formato de milissegundos

Milissegundos com separador de vírgula:

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

Milissegundos com separador de vírgula

Só deixando isso aqui para quem estava procurando um cronômetro simples formatado com segundos para 2 casas decimais como eu. Estes são pequenos temporizadores curtos e doces que eu gosto de usar. Eles ocupam apenas uma linha de código no início da sub ou função e uma linha de código novamente no final. Eles não foram feitos para serem extremamente precisos, geralmente não me importo com nada menos que 1/100 de segundo pessoalmente, mas o cronômetro de milissegundos dará a você o tempo de execução mais preciso desses 3. Eu também li para você pode obter a leitura incorreta se acontecer de ser executado durante a passagem da meia-noite, um caso raro, mas apenas para sua informação.

technoman23
fonte