Synchronizing Access to the Counter Values

[Previous] [Next]

Synchronizing access to the counter values is an issue that every programmer needs to take seriously. To implement counters "correctly," you should wrap each modification of a counter value inside a critical section or something similar. However, the CPU time required to enter and leave a critical section is usually much higher than the CPU time required to change a simple 32-bit or 64-bit value.

Because access synchronization could adversely affect your application's performance quite significantly, I spoke with a developer at Microsoft in charge of Windows performance. He told me that most of the system counters do not synchronize access to the counter value. This, of course, reduces the overhead of properly synchronizing the counter value but means that the value could potentially become corrupted. However, corruption of the value is such an unlikely scenario that the development team decided the speed benefit far outweighed the possibility of inaccurate information. Yes, the System Monitor control could show an incorrect value that would throw off the statistics, but this is a remote possibility.

From my own experience creating and testing performance objects and counters, I concur with the Windows team. That is, properly synchronizing access to a counter value to avoid the potential of an incorrect value is not worth the overhead. However, when designing my C++ class I felt it was necessary to allow you to make this choice for yourself, so my C++ class offers three public functions that allow you to lock and unlock the counters as you see fit:

 void CPerfData::LockCtrs()    const; BOOL CPerfData::TryLockCtrs() const; void CPerfData::UnlockCtrs()  const;  

Most applications will not use these functions; however, I do call these functions inside the implementation of the CPerfData class itself. For example, my implementation of the Collect function always locks and unlocks the counter information when called. I felt that this was necessary because the Collect function has a lot of work to do, and the additional overhead is insignificant compared to the other instructions that will execute when collecting the data.

In addition, I also lock the shared memory block when adding or removing object instances. This prevents the data structures from becoming corrupt and the code from crashing. The HWInputMon.cpp module also demonstrates the use of these functions from inside the _tWinMain function.

One important note: since threads running in different processes access the shared memory block, the easy way to synchronize these threads would be to use a mutex kernel object. However, waiting on a kernel object comes with a significant performance hit. A critical section would afford better performance, but, unfortunately, can be used only to synchronize threads that are running in a single process. Since I need very high-speed mutual-exclusive access to the shared memory buffer from threads in multiple processes, I decided to synchronize access to the shared memory buffer by using my COptex class as described in Programming Applications for Microsoft Windows, Fourth Edition (Jeffrey Richter, Microsoft Press, 1999).

Please examine the accompanying source code for more information about using the CPerfData class and to see how the workspace and its projects are configured.



Programming Server-Side Applications for Microsoft Windows 2000
Programming Server-Side Applications for Microsoft Windows 2000 (Microsoft Programming)
ISBN: 0735607532
EAN: 2147483647
Year: 2000
Pages: 126

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net