Most users of Process Lasso are familiar with its core engine, ProcessGovernor.exe. It is the silent background process that applies all process rules, including ProBalance dynamic priority adjustments. It is implemented as a stand-alone process to allow users to completely close the GUI, further reducing resource consumption and hiding Process Lasso from the user.
This Governor is already highly optimal. Since it has no interaction with the user, it doesn't have to waste memory with pretty icons, and doesn't have to waste CPU cycles refreshing a window. The size of its working set (memory in use) is largely dependent on the number of processes it has discovered on the system. For Windows XP and Windows 2000, it is often 1MB or less. For Windows Vista and Windows 7, its usually close to 2MB.
The number of CPU cycles used is also dependent on the number of processes running on the system, and (most importantly) the refresh rate setting. The refresh rate can be High, Normal, or Low. At High, the Governor acts quickly, but uses double the CPU cycles of the Normal refresh rate. Similarly, a Low refresh rate uses half the CPU cycles of the Normal refresh rate. No matter what, the Governor never uses enough CPU cycles for anyone to really notice. In any configuration, even on the slowest systems, it rarely approaches 0.1% utilization of available CPU cycles in a 1 second interval.
So now we know it uses almost no CPU cycles. That's great, so why bother continuing to work on it? Well, the faster we can get the governor's loop, the quicker it will respond during extreme high load situations.
From now on, when I make new optimizations, I'll write about some of them. I probably won't ever get around to documenting all the important optimizations I've made in the past. So, let's get to it..
For v3.70.2, I've removed occurrences of WaitForSingleObject from the Governor's primary thread. Previously, I was waiting on an 'exit me' event, using the timeout of WaitForSingleObject as the sleep mechanism between iterations.
Additionally, I was also invoking WaitForSingleObject to check if a change notification event was fired for the configuration file. This is, of course, to reload the configuration file when changes are detected.
There's absolutely nothing abnormal or wrong about using WaitForSingleObject (or WaitForMultipleObjects). That API is the mechanism the Windows kernel provides to put a thread to sleep until certain events have fired or a timeout has been reached. It is also the typical way of checking the state of an event object.
However, it turns out that WaitForSingleObject is excessively expensive, inducing an entrance into kernel mode even if there is no contention, and even if its used with a zero timeout to simply check the state of an event object. For a little more insight into the overhead of WaitForSingleObject see this link.
Being so expensive, I decided to remove it from the primary loop of the Governor. First, I switched to a Sleep to pause the primary thread, instead of relying on the timeout of a WaitForSingleObject call. Next, I simply created a new helper thread that does nothing but WaitForMultipleObjects on the events I'm watching (exit and config change). When one gets signaled, I set a global boolean to let the primary thread know the event signalled it next wakes up from its sleep.
Of course, I did have to add a new synchronization object for the code blocks working with the configuration file change boolean. However, that's no problem since a simple critical section with a non-blocking (optional) entrance in the primary thread works great and is highly efficient.
Yep, a simple 2 minute change and a 30 minute article ;p.
The end result is that the primary thread of the Governor no longer invokes WaitForSingleObject. This means no more expensive entrances into the kernel. Now, if only the CPU utilization wasn't already so low that the impact of this change is imperceptible ... ;)
Wednesday, 28 October 2009
Subscribe to:
Post Comments (Atom)
0 comments:
Post a Comment