The performance cost of reading a registry key
The registry is a convenient place to record persistent
cross-process data in a uniform and multi-thread-safe manner.
It
roams with the user
if you store it in HKEY_CURRENT_USER,
and individual keys can be secured (even on systems that use FAT,
which doesn't otherwise support security).
But that doesn't mean that it's free.
The cost of opening a key, reading a value, and closing it
is around 60,000 to 100,000 cycles (I'm told).
And that's assuming the key you're looking for is in the cache.
If you open the key and hold it open, then the act of
reading a value costs around 15,000 to 20,000 cycles.
(These numbers are estimates for Windows XP;
actual mileage may vary.)
Consequently, you shouldn't be reading a registry key
in your inner loop.
Not only does it cost you CPU time at query time,
but the constant hammering of the registry means that
the data structures used by the registry to locate and store your
key (including the entry in the registry cache)
are kept in the system working set.
Don't read a registry key on every mouse move;
read the value once and cache the result.
If you need to worry about somebody changing the value
while your program is running, you can establish a protocol
for people to follow when they want to change a setting.
Windows, for example, uses functions such as
SystemParametersInfo to manipulate settings that
are normally cached rather than read from the registry each time
they are needed.
Calling the update function both updates the registry and the
in-memory cache.
If you can't establish a mechanism for coordinating changes
to the setting, you can set a change notification
via the RegNotifyChangeKeyValue function so that
you are notified when the value changes.
Whenever possible, optimize for the common case, not the rare case.
The common case is that the registry value hasn't changed.
By using a notification mechanism, you move the cost of
"But what if the value changed?" out of your inner loop
and into code that doesn't execute most of the time.
(Remember, the fastest code is code that never runs.)
Of course, you don't want to burn a thread waiting on the
notification event.
I use the thread pool.
The RegisterWaitForSingleObject function
lets you tell the thread pool,
"Hey, could you call me when this object is signalled?
Thanks."
The thread pool then does the work of combining this with
all the other handles it has been asked to wait for
into a giant WaitForMultipleObjects call.
That way, one thread can handle multiple waits.
One caveat to heed with the
RegNotifyChangeKeyValue function is that the
notification has thread affinity!
If the thread that calls the
RegNotifyChangeKeyValue function exits,
the notification is raised.
This means that you shouldn't call
the function from a thread pool thread,
since the system will destroy threads in the thread pool
when the work list goes idle and their presence is no longer needed.
If you mess up and call it from a thread pool thread,
you'll find that the event keeps firing spuriously as the
thread pool cleanup code runs,
making the cure as bad as the disease!
Instead, you should create the wait from a persistent thread
(say, the thread that actually cares about the value!)
and register the wait there.
When the event fires on the thread pool,
handle the change, then ask your persistent thread to
start a new cycle of RegNotifyChangeKeyValue.
That way, the event is always associated with your persistent
thread instead of with a transient thread pool thread.