views:

1110

answers:

8

Since performance of string concatenation is quite weak in VB6 I'm testing several StringBuilder implementations. To see how long they're running, I currently use the built-in

Timer

function which only gives me the number of seconds that have passed after midnight.

Is there a way (I guess by importing a system function) to get something with milliseconds precision?

+5  A: 

Yes you can use Win32 API

DWORD WINAPI GetTickCount(void);

to import it in VB6 declare it like this:

Private Declare Function GetTickCount Lib "kernel32" () As Long

call it before the operation and after and then calculate the difference in time passed.

justadreamer
GetTickCount only has 10ms precision, if you need more precision that this you should use QueryPerformanceCounter
Patrick McDonald
See http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
Patrick McDonald
msdn says nothing about 10ms, but it says: "The resolution is limited to the resolution of the system timer. This value is also affected by adjustments made by the GetSystemTimeAdjustment function." so maybe 10ms is for your specific machine?
justadreamer
GetTickCount is good enough for most purposes, so long as you are aware if its limitations, try the code in http://support.microsoft.com/kb/172338 however to satisfy yourself that 10ms accuracy is the best you're gonna get on any Windows system
Patrick McDonald
Yes, Raymond's article http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx is nice, he still refers to a resolution of a system timer, however he advises to also use QueryPerformanceCounter which also takes some time to execute - so there are tradeoffs. However good to know.
justadreamer
A: 

You can try using the System::Diagnostics::Stopwatch

Imports System.Diagnostics

Dim sw As New Stopwatch()
sw.Start()
// do something
LOG("Elapsed " + sw.ElapsedMilliseconds + " ms")
Ezekiel Rage
This is .NET code, he specifically said VB6 (which was the last release before .NET 1.0 was released)
Matthew Scharley
Sorry, my bad. Then the GetTickCount is the way to go in VB6 as it is the alternative to clock_t object in C/C++
Ezekiel Rage
.NET StopWatch uses QueryPerformanceCounter/QueryPerformanceFrequency in the background where possible (you can find out if it is used by examining the IsHighResolution property of StopWatch)
Patrick McDonald
+1  A: 

You can use two Win32 APIs:

These use LARGE_INTEGER to represent 64 bit numbers.

Richard
VB6 code in this MSDN KnowledgeBase article: http://support.microsoft.com/kb/172338
MarkJ
+2  A: 

Put the following code in a Stopwatch class:

Option Explicit

Private Declare Function QueryPerformanceCounter Lib "Kernel32" (X As Currency) As Boolean
Private Declare Function QueryPerformanceFrequency Lib "Kernel32" (X As Currency) As Boolean

Private m_startTime As Currency
Private m_freq As Currency
Private m_overhead As Currency

Public Sub start()
    QueryPerformanceCounter m_startTime
End Sub

Public Function ElapsedSeconds() As Double
    Dim currentTime As Currency
    QueryPerformanceCounter currentTime
    ElapsedSeconds = (currentTime - m_startTime - m_overhead) / m_freq
End Function

Public Function ElapsedMilliseconds() As Double
    ElapsedMilliseconds = ElapsedSeconds * 1000
End Function

Private Sub Class_Initialize()
    QueryPerformanceFrequency m_freq
    Dim ctr1 As Currency
    Dim ctr2 As Currency
    QueryPerformanceCounter ctr1
    QueryPerformanceCounter ctr2
    m_overhead = ctr2 - ctr1
End Sub

You can use it as follows:

Dim sw as StopWatch
sw = New StopWatch
sw.Start

' Code you want to time

Debug.Print "Code took " & sw.ElapsedMilliseconds " ms"
Patrick McDonald
This is also quite nice for collecting some debug statistics or implementing a performance logger.
RobertB
+1  A: 

There's code and an explanation in the MSDN KB article Q172338 How To Use QueryPerformanceCounter to Time Code

MarkJ
+2  A: 

You might also consider using a different approach. Try calling your routines from a loop with enough iterations to give you a measurable time difference.

Beaner
+1  A: 

There's a Thomas Edison story, where he's interviewing some prospective engineers.

He asks them to determine the volume of a light bulb. Candidate A measures it and then uses the formula for the volume of a sphere, and another formula for the volume of the neck, and so on. Candidate B fills it with water and pours it into a measuring cup. Who do you think got the job?

Run it 1000 times and look at your watch before and after. Seconds = milliseconds.

Mike Dunlavey
A: 

I always use this in a module somewhere (could be in a class though). This code allows you to maintain up to six timers, with high accuracy:

Option Explicit

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

Private cFrequency As Currency
Private cCounters(0 To 5) As Currency

Public Sub StartCounter(Optional lCounterIndex As Long)
    QueryPerformanceFrequency cFrequency
    QueryPerformanceCounter cCounters(lCounterIndex)
End Sub

Public Function GetCounter(Optional lCounterIndex As Long) As Double
    Dim cCount As Currency

    QueryPerformanceFrequency cFrequency
    QueryPerformanceCounter cCount
    GetCounter = Format$((cCount - cCounters(lCounterIndex) - CCur(0.0008)) / cFrequency, "0.000000000")
End Function

Public Function Scientific(ByVal dValue As Double) As String
    Dim lMultiplier As Long
    Dim vNames As Variant

    lMultiplier = 5
    vNames = Array("peta", "tera", "giga", "mega", "kilo", "", "milli", "micro", "nano", "pico", "femto")
    If Abs(dValue) < 1 Then
        While Abs(dValue) < 1
            dValue = dValue * 1000
            lMultiplier = lMultiplier + 1
        Wend
    ElseIf Abs(dValue) >= 1000 Then
        While Abs(dValue) >= 1000
            dValue = dValue / 1000
            lMultiplier = lMultiplier - 1
        Wend
    End If

    Scientific = Format$(dValue, "0.000") & " " & vNames(lMultiplier)
End Function
Jonathan Swift