Performance counter value may unexpectedly leap forward (274323)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API)

This article was previously published under Q274323

SYMPTOMS

The result that is returned by the QueryPerformanceCounter function may unexpectedly leap forward from time to time. This leap may represent several seconds.

CAUSE

This problem occurs as a result of a design defect in the peripheral component interconnect (PCI) to Industry Standard Architecture (ISA) bridge of some chipsets. This bridge is commonly referred to as the south bridge. The jump occurs under a heavy PCI bus load, when the operating system receives a series of unexpected results from the bridge. The operating system detects the unexpected results and computes an amount to add to the performance counter. This causes the returned result from QueryPerformanceCounter to jump forward.

RESOLUTION

Programs should watch for an unexpected jump by comparing the change in time as determined by successive calls to QueryPerformanceCounter with the change in time as determined by successive calls to the GetTickCount function. If there is a significant jump that is based on QueryPerformanceCounter(), but no similar increase that is based on GetTickCount, then it can be assumed that the performance counter just jumped forward. The code sample at the end of this article demonstrates how to do this.

STATUS

This operating system's behavior is by design. The performance counter adjustment is necessary when the operating system obtains unreliable data from the chipset.

MORE INFORMATION

Design defects within a hardware chip are known as errata. These design defects may cause the product to deviate from published specifications. For information on errata in specific chipsets, please contact the hardware vendor for the chipset.

A machine's PCI components are identified by a PCI identifier within the following key in the Windows registry:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\PCI

The names of subkeys have the following form

VEN_xxxx&DEV_yyyy&SUBSYS_aaaaaaaa&REV_bb

where xxxx:yyyy represents the PCI identifier.

Currently, chips with the following PCI identifiers are known to exhibit a jump in performance counter value:
PCI IDHardware Vendor
1039:0530Silicon Integrated Systems (SiS)
1039:0620Silicon Integrated Systems (SiS)
10B9:0533Acer Labs, Inc. (ALi)
10B9:1533Acer Labs, Inc. (ALi)
1106:0596VIA Technologies, Inc. (VIA)
1106:0686VIA Technologies, Inc. (VIA)
1166:004FServerworks Corporation
1166:0050Serverworks Corporation
8086:7110Intel Corporation
This list will be updated as Microsoft observes the behavior in other chipsets.

Sample Code

The following sample code demonstrates the method described above for detecting performance counter leaps. If the code is run on a computer with the PIIX4 chipset, for example, this console application will sporadically report leaps in the performance counter value.
#include <windows.h>
#include <stdio.h>

void main() {

   LARGE_INTEGER liFrequency;
   LARGE_INTEGER liCurrent;
   LARGE_INTEGER liLast;
   LARGE_INTEGER liRecent[10];
   DWORD dwCurrent;
   DWORD dwLast;
   DWORD dwPerfElapsed;
   DWORD dwTickElapsed;
   int i = 0;
   int j;

   // Save the performance counter frequency for later use.
   if (!QueryPerformanceFrequency(&liFrequency))
      printf("QPF() failed with error %d\n", GetLastError());

   // Query the performance counter value and tick count.
   dwCurrent = GetTickCount();
   if (!QueryPerformanceCounter(&liCurrent))
      printf("QPC() failed with error %d\n", GetLastError());

   liLast = liCurrent;
   dwLast = dwCurrent;

   while (TRUE) {

      // Query the performance counter value and tick count.
      if (!QueryPerformanceCounter(&liCurrent))
         printf("QPC() failed with error %d\n", GetLastError());
      dwCurrent = GetTickCount();

      // Store the performance counter value in the list of recent values.
      liRecent[i].QuadPart = liCurrent.QuadPart;
      i = (i+1) % 10;

      // Convert difference in performance counter values to milliseconds.
      dwPerfElapsed = (DWORD) (((liCurrent.QuadPart - liLast.QuadPart) 
            * 1000) / liFrequency.QuadPart);
      
      dwTickElapsed = dwCurrent - dwLast;

      // Check for a discrepancy greater than 500 milliseconds.
      if (abs(dwPerfElapsed - dwTickElapsed) > 500) { 

         // Print the previous 9 performance-counter values.
         for (j=9; j>0; j--) {
            printf("      Previous %d:  %I64X\n", j, liRecent[i].QuadPart);
            i = (i+1) % 10;
         }

         // Print the leap value.
         printf( "LEAP: Current:     %I64X  delta = %I64X\n", 
               liCurrent.QuadPart, liCurrent.QuadPart - liLast.QuadPart);
         
         // Retrieve and print the next 9 performance-counter values.
         for (j=1; j<=9; j++) {
            
            QueryPerformanceCounter(&liCurrent);
            printf("      Next     %d:  %I64X\n", j, liCurrent.QuadPart);
            
            liRecent[i].QuadPart = liCurrent.QuadPart;
            i = (i+1) % 10;
         }

         printf("\n");
      }

      liLast = liCurrent;
      dwLast = dwCurrent;
   }

   return;
}

Modification Type:MajorLast Reviewed:5/19/2005
Keywords:kbAPI kbKernBase kbPerfMon kbprb kbSCRAPKeep KB274323