Measure Time with 1 Millisecond Resolution with Stats in VBA / MS Access

AID: 1578
  • Status: Published

1940 points

  • Byballamber
  • TypeTips/Tricks
  • Posted on2009-09-18 at 08:28:09
Summary
It all started that I found a useful small code snippet on this site to measure time with millisecond precision. Got carried away and developed it to a more complex timer solution. This was used by me to measure the performance of a form in MS Access. Then I submitted it as an article and the comments I got made me work on it a lot more. Now I think it is a super deluxe timer solution... :)

This timer implementation
- is capable of measuring time using multiple timers identified by string labels
- is capable of maintaining statistics for each timer (min, max, average, total)
- can exclude the first measurement from statistics calculation
- is accurate to 1 ms resolution
- is extremely easy to use.

The code is packaged now in a MS Access class module and comes with a wrapper module to make it easily accessible for use from anywhere in your code without needing to create an object. I have also attached a test module to demonstrate the use.

About some of the more important design decisions
Originally the class was using the GetTickCount() API call however as 'aikimark' pointed out to me this API has an accuracy problem as the value is not updated frequently enough to truly represent millisecond resolution. Now instead GetTickCount() the timeGetTime() API is used which can be configured to provide true 1 ms accuracy. This however necessitate that the code is put in a class module as only this way it can be ensured that on Terminate, the default timer resolution is restored.

QueryPerformanceCounter() could have also been an option, but there are problems with this implementation on different platforms and the general consensus seems to be that if a 1ms resolution is acceptable, timeGetTime is the safest bet. A good starting point to start reading up on these isssues is this: http://gpwiki.org/index.php/VB:Timers .

On more point worth mentioning the use of arrays to maintain the timer information instead of using the Scripting.Dictionary object. Again 'aikimark' suggested to me that the dictionary object would perform faster then a Redim Preserve. Also the Exist() function would be faster than searching with a For loop in an array (this however would not return the index though and retreiving the data element will require another search.) In all honesty I have found information pro and con both solutions but I tend to accept that when the number of items in the array grows, the dictionary object will be faster than the array.

The primary reason I decided to stick with the arrays on the end was that the dictionary object can only handle literals or objects. The user defined data type (in C++ it would be a structure) I created to maintain the information cannot be passed in as an argument and I would have to create yet another class just for the time measurement structure. I wanted to keep it simple and limit the number of modules so arrays they remained. In exchange though I tried to optimize the code to use Redim Preserve with batch resizes (i.e. size is incerasing with chunks of 20). In normal use you would not need to resize the array anyway.

Without further ado here is the class code. There are plentiful comments to help understanding. Place the code in a class module called clsMilliSecondTimer.

 
Option Compare Database
Option Explicit
 
' Using GetTickCount() is dropped in favour of more exact time measurement
' with timeGetTime()
' See more at http://gpwiki.org/index.php/VB:Timers
' Private Declare Function GetTickCount Lib "kernel32" () As Long
 
Private Declare Function timeGetTime Lib "winmm.dll" () As Integer
Private Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Integer) As Integer
Private Declare Function timeEndPeriod Lib "winmm.dll" (ByVal uPeriod As Integer) As Integer
 
' The below is commented out as is now defined in the modTimer module
' Private Const TIMER_DEFAULT_LABEL = "~\£""`¬|"  ' This is an unlikely label anyone would define
 
Private m_Measurements() As TimeMeasurement
 
Private m_isInit As Boolean
Private m_MaxIdx As Long
 
' ****************************************************************************************
' ----------------------------------------------------------------------------------------
' Usage of the module:
'   Simply call the StartTimer function at the point you want to start timing from. If no
'   label is specified, you will be able to measure only one process from finish to stop.
'   If you specify labels, you will be able to measure times in parallel and get results
'   back per label. E.g. start timing when form start loading, measure the time spent in
'   each form event handler and stop overall timing when first control gets focus.
'
'   If you measure the timings of a repeated process you can use the GetTotalTime function
'   to retreive the total time measured. E.g. if you want to open and close a form 100
'   times for testing and want to know how much time was spent in each method overall, you
'   would want to do the following:
'   1. Create a test form with a button that would kick off the whole testing process
'   2. In the click event of that button you will create a loop that will open and close
'      the form 100 times
'   3. Within the form use the ResetTimer method at the beginning of each method and the
'      GetTime on the end of each method. For example to time how much time Form_Load
'      takes do this:
'
'      Private Sub Form_Load()
'        modTimer.ResetTimer "Form_Load", True.
'        ... your code doing lots of things here ...
'        modTimer.GetTime "Form_Load"
'      End Sub
'
'      The True param in ResetTimer will ensure that the timer is created even if you did
'      not use StartTimer before.
'   4. Once you returned from the loop you can get the total time spent like this:
'
'      debug.Print "Total time in Form_Load:" & modTimer.GetTotalTime "Form_Load"
'
'   The rest of the methods are self explanatory. Read the individual notes.
' ----------------------------------------------------------------------------------------
' ****************************************************************************************
 
' ****************************************************************************************
' Sub StartTiming
' ----------------------------------------------------------------------------------------
' Params:
'   label   - Optional String. The label of the timer you want to create/start.
'   excludefirst  - Optional Boolean. If True, the first measurement is excluded from
'                   stats (total, min, max, average). Defaults to False.
' Return value:
'   None.
' ----------------------------------------------------------------------------------------
' Usage:
'   Use this method to create a timer and start timing.
' ****************************************************************************************
Public Sub StartTimer(Optional label As String = TIMER_DEFAULT_LABEL, _
                      Optional excludefirst As Boolean = False)
  Const MINARRAYINCREASE = 20
  
  If (0 = Len(label)) Then
     Err.Raise vbObjectError + 520, , "You must specify a unique timer label. Empty string not allowed."
     Exit Sub
  End If
  
  Dim idx As Long
  idx = FindInMeasurements(label)
  If idx >= 0 Then
   Err.Raise vbObjectError + 513, , "Duplicate timer. You must reset the timers or specify a unique timer label."
   Exit Sub
  End If
 
  If Not m_isInit Then
    m_isInit = True
    ReDim m_Measurements(MINARRAYINCREASE)
    m_MaxIdx = 0
    timeBeginPeriod 1
  Else
    Dim arraymax As Long
    arraymax = UBound(m_Measurements)
    If m_MaxIdx = arraymax Then
      ' All available slots are used, we need to increase the array size
      ReDim Preserve m_Measurements(arraymax + MINARRAYINCREASE)
    End If
    m_MaxIdx = m_MaxIdx + 1
  End If
  ' We always define the new timer as the last array element
  m_Measurements(m_MaxIdx).label = label
  m_Measurements(m_MaxIdx).excludefirst = excludefirst
  m_Measurements(m_MaxIdx).Starttime = timeGetTime()
End Sub
 
' ****************************************************************************************
' Sub GetTime
' ----------------------------------------------------------------------------------------
' Params:
'   label   - Optional String. The label of the timer you want to get the value of.
' Return value:
'   Single. Time in seconds down to the millisecond level. If the label does not exist,
'   the return value is set to -999.
' ----------------------------------------------------------------------------------------
' Usage:
'   When you want to get the time of a timer specified by the label call this.
' ****************************************************************************************
Public Function GetTime(Optional label As String = TIMER_DEFAULT_LABEL) As Single
  Dim StopTime As Long
  StopTime = timeGetTime()
  
  Dim idx As Long
  
  idx = FindInMeasurements(label)
  If idx < 0 Then
   GetTime = -999
   Err.Raise vbObjectError + 514, , "Undefined timer."
   Exit Function
  End If
  
  GetTime = (StopTime - m_Measurements(idx).Starttime) / 1000
  m_Measurements(idx).Counter = m_Measurements(idx).Counter + 1
  
  Select Case True
    ' First measurement
    Case (1 = m_Measurements(idx).Counter)
      m_Measurements(idx).First = GetTime
      
      If Not m_Measurements(idx).excludefirst Then
        m_Measurements(idx).Max = GetTime
        m_Measurements(idx).Min = GetTime
        m_Measurements(idx).Total = GetTime
      End If
    
    ' Second measurement if the first measurement was excluded
    Case (2 = m_Measurements(idx).Counter) And m_Measurements(idx).excludefirst
      m_Measurements(idx).Max = GetTime
      m_Measurements(idx).Min = GetTime
      m_Measurements(idx).Total = GetTime
    
    ' Every subsequent measurement
    Case Else
      If GetTime > m_Measurements(idx).Max Then
        m_Measurements(idx).Max = GetTime
      ElseIf GetTime < m_Measurements(idx).Min Then
        m_Measurements(idx).Min = GetTime
      End If
      m_Measurements(idx).Total = m_Measurements(idx).Total + GetTime
    
  End Select
End Function
 
' ****************************************************************************************
' Function ResetTimer
' ----------------------------------------------------------------------------------------
' Params:
'   label   - Optional String. The label of the timer you want to reset
'   createtimer - Optional Boolean. Defines whether a new timer is to be created if label
'                 is not found. Defaults to False.
'   excludefirst  - Optional Boolean. If True, the first measurement is excluded from
'                   stats (total, min, max, average). Used when createtimer is True.
'                   Defaults to False.
'   resetstats - Optional Boolean. If True, the statistics belonging to that timer will
'                also be reset. Defaults to False.
' Return value:
'   Boolean. True if reset was successful or new timer was created, False otherwise.
' ----------------------------------------------------------------------------------------
' Usage:
'   If the label is found, the timing will be restarted at that point. Function returns
'   True.
'
'   If the label is not found and 'createtimer' is False the function returns False. If
'   createtimer is True, this works as StartTiming, a new timer is created.
' ****************************************************************************************
Public Function ResetTimer(Optional label As String = TIMER_DEFAULT_LABEL, _
                           Optional createtimer As Boolean = False, _
                           Optional excludefirst As Boolean = False, _
                           Optional resetstats As Boolean = False) As Boolean
  Dim idx As Long
  idx = FindInMeasurements(label)
  
  If idx < 0 Then
    If createtimer Then
      ResetTimer = True
      StartTimer label, excludefirst
    Else
      ResetTimer = False
      Err.Raise vbObjectError + 514, , "Undefined timer."
    End If
  Else
    ResetTimer = True
    If resetstats Then
      m_Measurements(idx).Total = 0
      m_Measurements(idx).Average = 0
      m_Measurements(idx).Counter = 0
      m_Measurements(idx).First = 0
      m_Measurements(idx).Max = 0
      m_Measurements(idx).Min = 0
    End If
    m_Measurements(idx).Starttime = timeGetTime()
  End If
End Function
 
' ****************************************************************************************
' Sub DeleteAll
' ----------------------------------------------------------------------------------------
' Removes all timers by deleting the timer arrays. Enables the user to start measurement
' with a "clean slate". After this the class is effectively in an uninitialised state.
' ****************************************************************************************
Public Sub DeleteAll()
  If m_isInit Then
    timeEndPeriod 1
    ReDim m_Measurements(0)
    m_isInit = False
  End If
End Sub
 
' ****************************************************************************************
' Function GetResult
' ----------------------------------------------------------------------------------------
' Returns the measurement structure (populated with the results) belonging to the label
' ****************************************************************************************
Public Function GetResult(Optional label As String = TIMER_DEFAULT_LABEL) As TimeMeasurement
  Dim idx As Long
  
  idx = FindInMeasurements(label)
  If idx < 0 Then
    Dim tmp As TimeMeasurement
    tmp.Counter = -1
    tmp.Total = -1
    GetResult = tmp
    Err.Raise vbObjectError + 514, , "Undefined timer."
  Else
    ' Calculating the average here
    m_Measurements(idx).Average = m_Measurements(idx).Total / _
                                  IIf(m_Measurements(idx).excludefirst, _
                                      m_Measurements(idx).Counter - 1, m_Measurements(idx).Counter)
    GetResult = m_Measurements(idx)
  End If
End Function
 
' ****************************************************************************************
' Function FindInMeasurements
' ----------------------------------------------------------------------------------------
' Params:
'   label - the timer label to find in the array
'   startidx  - Optional. Start searching from this index. Defaults to 0.
' Return value:
'   Long. Returns index of the first occurence of searchval in myarray after startidx. If
'   startidx is out of bounds the function returns -1.
' ****************************************************************************************
Private Function FindInMeasurements(label As String, _
                                    Optional startidx As Long = 0) As Long
  If m_isInit Then
    Dim i As Long
    Debug.Assert startidx >= 0  ' Startidx should be greater than 0
    For i = startidx To m_MaxIdx  ' Only search until the last used element
      If 0 = StrComp(m_Measurements(i).label, label, vbBinaryCompare) Then
        FindInMeasurements = i
        Exit Function
      End If
    Next i
  End If
  FindInMeasurements = -1
End Function
 
Private Sub Class_Terminate()
  ' Restores the default setup (about 15 ms accuracy)
  timeEndPeriod 1
End Sub
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:

Select allOpen in new window



When you have a class, you need to create objects to use them and if you measure timings through multiple forms you have an issue with object visibility and even possibly with the timing of when the object is created. To address this I have added a wrapper module which creates the timer object for you.

Also note that the TimerMeasurement user defined type is also put in this module as otherwise it could not have been used outside of the class module. Unfortunately it introduced a dependency between the class module and the generic module, but if you need only the class without the wrapper, you can very easaily tweak this. Later on I also moved the TIMER_DEFAULT_LABEL const definiiton to the general module so if you need the class only, take this as well.

The only method you need to remember to call after finished is the DestroyTimer() method. This will make sure the timer object is destroyed and that the API is set back to its default value.

The error handling in the code is based on the logic of raising errors, but if you do not like it, you can get rid of the Raise.Error commands and the functions will still return enough info to be able to determine if the operation was successful.

To have the examples I am about to present work, put this code in a module called modTimer.

 
Option Compare Database
Option Explicit
 
Public Type TimeMeasurement
  label As String    ' Keeps the timer label
  Starttime As Long  ' Keeps the start time
  Counter As Integer ' Keeps the number of measurements
  Total As Single    ' Keeps the total of multiple measurements until reset
  Average As Single  ' Keeps the average of multiple measurements
  First As Single    ' Keeps the result of the first measurement
  Max As Single      ' Keeps the maximum value of all measurements
  Min As Single      ' Keeps the minimum value of all measurements
  excludefirst As Boolean ' If true the first measurement is ignored for stats
End Type
 
Public Const TIMER_DEFAULT_LABEL = "~\£""`¬|"  ' This is an unlikely label anyone would define
 
Private m_mytimer As clsMilliSecondTimer
 
' ****************************************************************************************
' ----------------------------------------------------------------------------------------
' This is a wrapper around the clsMilliSecondTimer class to provide easy accessibility
' from anywhere in the code. Usage and explanations are in the comments of the class
' code.
' ----------------------------------------------------------------------------------------
' ****************************************************************************************
 
' ****************************************************************************************
' Sub DestroyTimer
' ----------------------------------------------------------------------------------------
' Params:
'   None.
' ----------------------------------------------------------------------------------------
' Usage:
'   This is to be called after the timer is no longer required to restore system timer
'   resolution to default setting.
' ****************************************************************************************
Public Sub DestroyTimer()
  Set m_mytimer = Nothing
End Sub
 
' ****************************************************************************************
' Sub StartTiming
' ****************************************************************************************
Public Sub StartTimer(Optional label As String = TIMER_DEFAULT_LABEL, _
                      Optional excludefirst As Boolean = False)
  If m_mytimer Is Nothing Then
    Set m_mytimer = New clsMilliSecondTimer
  End If
 
  m_mytimer.StartTimer label
End Sub
 
' ****************************************************************************************
' Sub GetTime
' ****************************************************************************************
Public Function GetTime(Optional label As String = TIMER_DEFAULT_LABEL) As Single
  GetTime = m_mytimer.GetTime(label)
End Function
 
' ****************************************************************************************
' Function ResetTimer
' ****************************************************************************************
Public Function ResetTimer(Optional label As String = TIMER_DEFAULT_LABEL, _
                           Optional createtimer As Boolean = False, _
                           Optional excludefirst As Boolean = False, _
                           Optional resetstats As Boolean = False) As Boolean
  ResetTimer = m_mytimer.ResetTimer(label, createtimer, excludefirst, resetstats)
End Function
 
' ****************************************************************************************
' Sub DeleteAll
' ****************************************************************************************
Public Sub DeleteAll()
  m_mytimer.DeleteAll
End Sub
 
' ****************************************************************************************
' Function GetResult
' ****************************************************************************************
Public Function GetResult(Optional label As String = TIMER_DEFAULT_LABEL) As TimeMeasurement
  GetResult = m_mytimer.GetResult(label)
End Function
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:

Select allOpen in new window



And finally here is the test code demonstrating the use of the class. I believe it is pretty straightforward. Note however the trick of using ResetTimer() in the TestSubx() methods instead of StartTimer(). This ensures that the timer is created on the first use and then reset on every subsequent call.

 
Option Compare Database
Option Explicit
 
' ****************************************************************************************
' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
' !!!!!!!!!!!!           THIS IS JUST FOR TESTING THE TIMER FUNCTION           !!!!!!!!!!!
' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
' ****************************************************************************************
 
Private m_delay As Long
 
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
 
Public Sub testmain()
  Dim i As Long
  m_delay = 0
  
  Debug.Print "-------- STARTING TIMERS --------"
  StartTimer "overall"
  For i = 1 To 20 ' We do 20 cycles
    Call TestSub1 ' Called every time
    If 0 = i Mod 2 Then Call TestSub2 ' Called every 2nd time
    If 0 = i Mod 3 Then Call TestSub3 ' Called every 3rd time
    Call TestSub4 ' Called every time, first cycle excluded from measurement
  Next i
  GetTime "overall"
  Debug.Print "========== TestSub1 =========="
  Debug.Print "counter:" & GetResult("TestSub1").Counter
  Debug.Print "average:" & GetResult("TestSub1").Average
  Debug.Print "total:" & GetResult("TestSub1").Total
  Debug.Print "Max:" & GetResult("TestSub1").Max
  Debug.Print "Min:" & GetResult("TestSub1").Min
  Debug.Print "excludefirst:" & GetResult("TestSub1").excludefirst
  Debug.Print ""
  
  Debug.Print "========== TestSub4 =========="
  Debug.Print "counter:" & GetResult("TestSub4").Counter
  Debug.Print "average:" & GetResult("TestSub4").Average
  Debug.Print "total:" & GetResult("TestSub4").Total
  Debug.Print "Max:" & GetResult("TestSub4").Max
  Debug.Print "Min:" & GetResult("TestSub4").Min
  Debug.Print "excludefirst:" & GetResult("TestSub4").excludefirst
  Debug.Print ""
  
  Debug.Print "========== TestSub2 =========="
  Debug.Print "counter:" & GetResult("TestSub2").Counter
  Debug.Print "average:" & GetResult("TestSub2").Average
  Debug.Print "total:" & GetResult("TestSub2").Total
  Debug.Print "Max:" & GetResult("TestSub2").Max
  Debug.Print "Min:" & GetResult("TestSub2").Min
  Debug.Print "excludefirst:" & GetResult("TestSub2").excludefirst
  Debug.Print ""
  
  Debug.Print "========== TestSub3 =========="
  Debug.Print "counter:" & GetResult("TestSub3").Counter
  Debug.Print "average:" & GetResult("TestSub3").Average
  Debug.Print "total:" & GetResult("TestSub3").Total
  Debug.Print "Max:" & GetResult("TestSub3").Max
  Debug.Print "Min:" & GetResult("TestSub3").Min
  Debug.Print "excludefirst:" & GetResult("TestSub3").excludefirst
  Debug.Print ""
  
  Debug.Print "======== overall time ========"
  Debug.Print GetResult("overall").Total
  
  Debug.Print "-------- Timing ENDED --------"
  DestroyTimer
  Debug.Print "\\\\\\\\ TIMER DESTROYED \\\\\\\"
  
End Sub
 
Public Sub TestSub1()
  ResetTimer "TestSub1", True, True
  Sleep 100
  Debug.Print "TestSub1:" & GetTime("TestSub1")
End Sub
 
Public Sub TestSub4()
  If m_delay = 0 Then
    m_delay = 1000
  Else
    m_delay = 100
  End If
  
  ResetTimer "TestSub4", True, False
  Sleep m_delay
  Debug.Print "      TestSub4:" & GetTime("TestSub4")
End Sub
 
Public Sub TestSub2()
  ResetTimer "TestSub2", True
  Sleep 200
  Debug.Print "  TestSub2:" & GetTime("TestSub2")
End Sub
 
Public Sub TestSub3()
  ResetTimer "TestSub3", True
  Sleep 300
  Debug.Print "    TestSub3:" & GetTime("TestSub3")
End Sub
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:

Select allOpen in new window



Known issues, constraints
  • As mentioned above, if the number of timers grow above a treshold ('aikimark' estimated this treshold to be around 20) there will be a performance impact. If you are a heavy user who for whatever reason wants to use a couple of 100 or more timers simultaneously, you might want to look into implementing this with the dictionary object.

    Note however, that this will not impact the elapse time measured for one single timer, only if they overlap. For example if in my example I would have measured 200 TestSub methods, the overall timer would have been hit by the time it took to manage the 200 individual timers. The individual timers though would still measure the same values.

  • The timeGetTime() API on occasions returns negative values. This ususally happens when the timer rolls over (i.e. the DWORD storing the timer info turns back to 0 when its capacity is fully used up. MS articles state that this happens every 49.2 days but for me it happened with more frequency. I could not find any way to address this so if you see a negative value, just repeat the measurement.
Asked On
2009-09-18 at 08:28:09ID1578
Tags

time measurement

,

millisecond

,

MS Access

,

search in array

,

Scrip[ting.Dictionary object

,

timeGetTime()

Topic

Access Forms

Views
1407

Comments

Add your Comment

Please Sign up or Log in to comment on this article.

Join Experts Exchange Today

Gain Access to all our Tech Resources

Get personalized answers

Ask unlimited questions

Access Proven Solutions

Search 3.2 million solutions

Read In-Depth How-To Guides

1000+ articles, demos, & tips

Watch Step by Step Tutorials

Learn direct from top tech pros

And Much More!

Your complete tech resource

See Plans and Pricing

30-day free trial. Register in 60 seconds.

Loading Advertisement...

Top Access Forms Experts

  1. LSMConsulting

    8,400

    0 points yesterday

    Profile
    Rank: Savant
  2. DatabaseMX

    8,064

    0 points yesterday

    Profile
    Rank: Savant
  3. capricorn1

    5,300

    0 points yesterday

    Profile
    Rank: Savant
  4. peter57r

    4,800

    0 points yesterday

    Profile
    Rank: Savant
  5. mbizup

    4,664

    0 points yesterday

    Profile
    Rank: Genius
  6. Helen_Feddema

    4,300

    0 points yesterday

    Profile
    Rank: Genius
  7. eghtebas

    4,132

    0 points yesterday

    Profile
    Rank: Genius
  8. boag2000

    2,940

    0 points yesterday

    Profile
    Rank: Genius
  9. _agx_

    2,800

    0 points yesterday

    Profile
    Rank: Genius
  10. Bitsqueezer

    2,800

    0 points yesterday

    Profile
    Rank: Wizard
  11. bingie

    2,000

    0 points yesterday

    Profile
    Rank: Guru
  12. dqmq

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  13. imnorie

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  14. als315

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  15. harfang

    1,680

    20 points yesterday

    Profile
    Rank: Genius
  16. hnasr

    1,500

    0 points yesterday

    Profile
    Rank: Genius
  17. etsherman

    1,500

    0 points yesterday

    Profile
    Rank: Sage
  18. jrogersok

    1,200

    0 points yesterday

    Profile
  19. ABeasley

    1,200

    0 points yesterday

    Profile
  20. eoinisme

    1,000

    0 points yesterday

    Profile
  21. sparab

    752

    0 points yesterday

    Profile
    Rank: Guru
  22. LPurvis

    720

    0 points yesterday

    Profile
    Rank: Genius
  23. khairil

    668

    0 points yesterday

    Profile
    Rank: Wizard
  24. Nick67

    668

    0 points yesterday

    Profile
    Rank: Sage
  25. sb9

    500

    0 points yesterday

    Profile
    Rank: Wizard

Hall Of Fame