Thursday, March 25, 2010

SnTT: A LotusScript StopWatch Class With Nanosecond Precision

I was doing some work in Access and wanted to time how long it took to do something. I was doing a small scale test so the timing was pretty minuscule. After poking around a bit I discovered a way to use the Windows API to count CPU clock cycles. I converted it to LotusScript since virtually nobody who reads my blog cares about Access. :-)

Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Double) As Long
Declare Function QueryPerformanceFrequency Lib "kernel32" (lpPerformanceCount As Double) As Long

Public Class StopWatch
Private m_StartTime As Double
Private m_EndTime As Double
Private m_Freq As Double
Private m_Overhead As Double

Private m_Days As Integer
Private m_Hours As Integer
Private m_Minutes As Integer
Private m_Seconds As Integer
Private m_Deci As Long
Private m_Centi As Long
Private m_Milli As Long
Private m_Micro As Long
Private m_Nano As Long

Private m_TotalSeconds As Single

Public Property Get Hours As Integer
Hours = m_Hours
End Property

Public Property Get Minutes As Integer
Minutes = m_Minutes
End Property

Public Property Get Seconds As Integer
Seconds = m_Seconds
End Property

Public Property Get Milli As Integer
Milli = m_Milli
End Property

Public Property Get Centi As Integer
Centi = m_Centi
End Property

Public Property Get Micro As Long
Micro = m_Micro
End Property

Public Property Get Nano As Long
Nano = m_Nano
End Property

Public Property Get TotalSeconds As Single
TotalSeconds = m_TotalSeconds
End Property

Public Sub StartTimer()
QueryPerformanceCounter m_StartTime
End Sub

Public Sub EndTimer()
QueryPerformanceCounter m_EndTime

Dim ElapsedTime As Double

ElapsedTime = (m_EndTime - m_StartTime - m_Overhead) / m_Freq
m_TotalSeconds = Csng(ElapsedTime)

m_Days = ElapsedTime \ 86400
If m_Days > 0 Then
ElapsedTime = ElapsedTime - m_Days * 86400
End If

m_Hours = ElapsedTime \ 3600
If m_Hours > 0 Then
ElapsedTime = ElapsedTime - m_Hours * 3600
End If

m_Minutes = ElapsedTime \ 60
If m_Minutes > 0 Then
ElapsedTime = ElapsedTime - m_Minutes * 60
End If

m_Seconds = Int(ElapsedTime)
If m_Seconds > 0 Then
ElapsedTime = ElapsedTime - m_Seconds
End If

m_Deci = Clng(Round(Clng(ElapsedTime * 10), 1))
m_Centi = Clng(Round(Clng(ElapsedTime * 100), 2))
m_Milli = Clng(Round(Clng(ElapsedTime * 1000), 3))
m_Micro = Clng(Round(Clng(ElapsedTime * 100000), 6))
m_Nano = Clng(Round(Clng(ElapsedTime * 1000000000), 9))

End Sub

Public Sub New()
Dim mStartTime As Double
Dim mEndTime As Double

'First figure out the API overhead
QueryPerformanceCounter mStartTime
QueryPerformanceCounter mEndTime

m_Overhead = mEndTime - mStartTime

'Now get the frequency
QueryPerformanceFrequency m_Freq
End Sub

End Class

This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at


  1. Dragon CotterillThu Mar 25, 06:36:00 AM

    Nice. Useful for working out which functions take the most time and need to be optimised. Think I'm going to use it on some of my more complicated agents.

  2. I like. That's a sexy looking class too.

  3. Burke LaShelle published a stopwatch class in The View in January, 2000. It used to be in the sand box - I had grabbed it and archived it a long time ago. Good comparative study to what you did here.

    What we really need is millisecond event triggering. 1 second is far too long. There are API calls for this, but LS lacks AddressOf functionality like VB, which provides the address of a sub or function to the API timer object to call it on the "alarm" event. If you know of something like that, you'd be a true hero of the day.

  4. Glad some of you are going to be able to make use of this. That's the whole point of Show-n-Tell-Thursday. :-)

    @Jerry - You could use a variant of this technique in a loop to poll the clock and execute events as needed, but you aren't going to be able to do callbacks without hooks. Now that Java is more widely available that might be an option. I don't use Java so I wouldn't know how to begin.

  5. yep. i have no idea what this says or is for, but it looks impressive. good job!