Microsoft KB Archive/172338: Difference between revisions

From BetaArchive Wiki
m (Text replacement - ">" to ">")
m (Text replacement - """ to """)
Line 83: Line 83:
The following sample code compares the various counters:<br />
The following sample code compares the various counters:<br />
<br />
<br />
WARNING: ANY USE BY YOU OF THE CODE PROVIDED IN THIS ARTICLE IS AT YOUR OWN RISK. Microsoft provides this code &quot;as is&quot; without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.
WARNING: ANY USE BY YOU OF THE CODE PROVIDED IN THIS ARTICLE IS AT YOUR OWN RISK. Microsoft provides this code "as is" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.
=== Step-by-Step Procedures ===
=== Step-by-Step Procedures ===


Line 90: Line 90:
<pre class="codesample">      Option Explicit
<pre class="codesample">      Option Explicit


       Declare Function QueryPerformanceCounter Lib &quot;Kernel32&quot; _
       Declare Function QueryPerformanceCounter Lib "Kernel32" _
                                 (X As Currency) As Boolean
                                 (X As Currency) As Boolean
       Declare Function QueryPerformanceFrequency Lib &quot;Kernel32&quot; _
       Declare Function QueryPerformanceFrequency Lib "Kernel32" _
                                 (X As Currency) As Boolean
                                 (X As Currency) As Boolean
       Declare Function GetTickCount Lib &quot;Kernel32&quot; () As Long
       Declare Function GetTickCount Lib "Kernel32" () As Long
       Declare Function timeGetTime Lib &quot;winmm.dll&quot; () As Long
       Declare Function timeGetTime Lib "winmm.dll" () As Long


       Sub Test_Timers()
       Sub Test_Timers()
Line 105: Line 105:
         If QueryPerformanceCounter(Ctr1) Then
         If QueryPerformanceCounter(Ctr1) Then
           QueryPerformanceCounter Ctr2
           QueryPerformanceCounter Ctr2
           Debug.Print &quot;Start Value: &quot;; Format$(Ctr1, &quot;0.0000&quot;)
           Debug.Print "Start Value: "; Format$(Ctr1, "0.0000")
           Debug.Print &quot;End Value: &quot;; Format$(Ctr2, &quot;0.0000&quot;)
           Debug.Print "End Value: "; Format$(Ctr2, "0.0000")
           QueryPerformanceFrequency Freq
           QueryPerformanceFrequency Freq
           Debug.Print &quot;QueryPerformanceCounter minimum resolution: 1/&quot; &amp; _
           Debug.Print "QueryPerformanceCounter minimum resolution: 1/" &amp; _
                       Freq * 10000; &quot; sec&quot;
                       Freq * 10000; " sec"
           Debug.Print &quot;API Overhead: &quot;; (Ctr2 - Ctr1) / Freq; &quot;seconds&quot;
           Debug.Print "API Overhead: "; (Ctr2 - Ctr1) / Freq; "seconds"
         Else
         Else
           Debug.Print &quot;High-resolution counter not supported.&quot;
           Debug.Print "High-resolution counter not supported."
         End If
         End If
       '
       '
Line 124: Line 124:
           Loops = Loops + 1
           Loops = Loops + 1
         Loop Until Count1 <> Count2
         Loop Until Count1 <> Count2
         Debug.Print &quot;GetTickCount minimum resolution: &quot;; _
         Debug.Print "GetTickCount minimum resolution: "; _
                     (Count2 - Count1); &quot;ms&quot;
                     (Count2 - Count1); "ms"
         Debug.Print &quot;Took&quot;; Loops; &quot;loops&quot;
         Debug.Print "Took"; Loops; "loops"
       '
       '
       ' Time timeGetTime
       ' Time timeGetTime
Line 137: Line 137:
           Loops = Loops + 1
           Loops = Loops + 1
         Loop Until Count1 <> Count2
         Loop Until Count1 <> Count2
         Debug.Print &quot;timeGetTime minimum resolution: &quot;; _
         Debug.Print "timeGetTime minimum resolution: "; _
                     (Count2 - Count1); &quot;ms&quot;
                     (Count2 - Count1); "ms"
         Debug.Print &quot;Took&quot;; Loops; &quot;loops&quot;
         Debug.Print "Took"; Loops; "loops"
       End Sub
       End Sub
                         </pre></li>
                         </pre></li>
Line 173: Line 173:
     Next I
     Next I
     QueryPerformanceCounter Ctr2
     QueryPerformanceCounter Ctr2
     Debug.Print &quot;(&quot;; Ctr1; &quot;-&quot;; Ctr2; &quot;-&quot;; Overhead; &quot;) /&quot;; Freq
     Debug.Print "("; Ctr1; "-"; Ctr2; "-"; Overhead; ") /"; Freq
     Debug.Print &quot;100 additions took&quot;;
     Debug.Print "100 additions took";
     Debug.Print (Ctr2 - Ctr1 - Overhead) / Freq; &quot;seconds&quot;
     Debug.Print (Ctr2 - Ctr1 - Overhead) / Freq; "seconds"
   End Sub
   End Sub
                 </pre>
                 </pre>

Revision as of 11:05, 21 July 2020

Article ID: 172338

Article Last Modified on 1/20/2007



APPLIES TO

  • Microsoft Excel 2000 Standard Edition
  • Microsoft Visual Basic 5.0 Learning Edition
  • Microsoft Visual Basic 6.0 Learning Edition
  • Microsoft Visual Basic 5.0 Professional Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic 5.0 Enterprise Edition
  • Microsoft Visual Basic 6.0 Enterprise Edition
  • Microsoft Visual Basic 5.0 Control Creation Edition
  • Microsoft Visual Basic 4.0 Standard Edition
  • Microsoft Visual Basic 4.0 Professional Edition
  • Microsoft Visual Basic 4.0 32-Bit Enterprise Edition
  • Microsoft Access 2002 Standard Edition
  • Microsoft Access 2000 Standard Edition
  • Microsoft Access 97 Standard Edition
  • Microsoft Access 95 Standard Edition
  • Microsoft Excel 2002 Standard Edition
  • Microsoft Excel 97 Standard Edition
  • Microsoft Excel 95 Standard Edition
  • Microsoft Word 2002 Standard Edition
  • Microsoft Word 2000 Standard Edition
  • Microsoft Word 97 Standard Edition



This article was previously published under Q172338

SUMMARY

When timing code to identify performance bottlenecks, you want to use the highest resolution timer the system has to offer. This article describes how to use the QueryPerformanceCounter function to time application code.

MORE INFORMATION

Several timers of differing accuracy are offered by the operating system:

Function                 Units                      Resolution
---------------------------------------------------------------------------
Now, Time, Timer         seconds                    1 second
GetTickCount             milliseconds               approx. 10 ms
TimeGetTime              milliseconds               approx. 10 ms
QueryPerformanceCounter  QueryPerformanceFrequency  same
                

If your system supports a high-resolution counter, you can use QueryPerformanceCounter and QueryPerformanceFrequency to do high-resolution timings.

The following sample code compares the various counters:

WARNING: ANY USE BY YOU OF THE CODE PROVIDED IN THIS ARTICLE IS AT YOUR OWN RISK. Microsoft provides this code "as is" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.

Step-by-Step Procedures

  1. Enter the following code into a Module. If you enter it into a class, form, or report module, make the declarations Private.

           Option Explicit
    
          Declare Function QueryPerformanceCounter Lib "Kernel32" _
                                     (X As Currency) As Boolean
          Declare Function QueryPerformanceFrequency Lib "Kernel32" _
                                     (X As Currency) As Boolean
          Declare Function GetTickCount Lib "Kernel32" () As Long
          Declare Function timeGetTime Lib "winmm.dll" () As Long
    
          Sub Test_Timers()
          Dim Ctr1 As Currency, Ctr2 As Currency, Freq As Currency
          Dim Count1 As Long, Count2 As Long, Loops As Long
          '
          ' Time QueryPerformanceCounter
          '
            If QueryPerformanceCounter(Ctr1) Then
              QueryPerformanceCounter Ctr2
              Debug.Print "Start Value: "; Format$(Ctr1, "0.0000")
              Debug.Print "End Value: "; Format$(Ctr2, "0.0000")
              QueryPerformanceFrequency Freq
              Debug.Print "QueryPerformanceCounter minimum resolution: 1/" & _
                          Freq * 10000; " sec"
              Debug.Print "API Overhead: "; (Ctr2 - Ctr1) / Freq; "seconds"
            Else
              Debug.Print "High-resolution counter not supported."
            End If
          '
          ' Time GetTickCount
          '
            Debug.Print
            Loops = 0
            Count1 = GetTickCount()
            Do
              Count2 = GetTickCount()
              Loops = Loops + 1
            Loop Until Count1 <> Count2
            Debug.Print "GetTickCount minimum resolution: "; _
                        (Count2 - Count1); "ms"
            Debug.Print "Took"; Loops; "loops"
          '
          ' Time timeGetTime
          '
            Debug.Print
            Loops = 0
            Count1 = timeGetTime()
            Do
              Count2 = timeGetTime()
              Loops = Loops + 1
            Loop Until Count1 <> Count2
            Debug.Print "timeGetTime minimum resolution: "; _
                        (Count2 - Count1); "ms"
            Debug.Print "Took"; Loops; "loops"
          End Sub
                            
  2. Run the function from the Debug/Immediate window. Your output should appear similar to the following:

    Start Value: 3516284.3498
    End Value: 3516284.3521
    QueryPerformanceCounter minimum resolution: 1/1193182 sec
    API Overhead: 1.92761875388667E-05 seconds

    GetTickCount minimum resolution: 10 ms
    Took 650 loops

    timeGetTime minimum resolution: 10 ms
    Took 1565 loops

Multiple statements execute before either GetTickCount or timeGetTime record a change. The actual number of loops will vary depending on the background tasks the operating system is executing.

On the other hand, QueryPerformanceCounter changes value between successive API calls, indicating its usefulness in high-resolution timing. The resolution in this case is on the order of a microsecond. Because the resolution is system-dependent, there are no standard units that it measures. You have to divide the difference by the QueryPerformanceFrequency to determine the number of seconds elapsed. In the case above, the overhead for just calling the API is about 19 microseconds. This would have to be subtracted when timing other code as follows:

   Private Sub Time_Addition()
   Dim Ctr1 As Currency, Ctr2 As Currency, Freq As Currency
   Dim Overhead As Currency, A As Long, I As Long
     QueryPerformanceFrequency Freq
     QueryPerformanceCounter Ctr1
     QueryPerformanceCounter Ctr2
     Overhead = Ctr2 - Ctr1        ' determine API overhead
     QueryPerformanceCounter Ctr1  ' time loop
     For I = 1 To 100
       A = A + I
     Next I
     QueryPerformanceCounter Ctr2
     Debug.Print "("; Ctr1; "-"; Ctr2; "-"; Overhead; ") /"; Freq
     Debug.Print "100 additions took";
     Debug.Print (Ctr2 - Ctr1 - Overhead) / Freq; "seconds"
   End Sub
                

Sample output:

( 3630876.6256 - 3630876.6388 - 0.0013 ) / 119.3182
100 additions took 9.97333181358753E-05 seconds


NOTE: Because currency variables are used, the values returned are 10000 times smaller than the actual counters. Because the calculation of seconds involves a division operation, this factor is cancelled out.

REFERENCES

Microsoft Developer Network; topics: timeGetTime GetTickCount QueryPerformanceCounter QueryPerformanceFrequency

Keywords: kbhowto kbprogramming KB172338