6

There is programs that is able to limit the CPU usage of processes in Windows. For example BES and ThreadMaster. I need to write my own program that does the same thing as these programs but with different configuration capabilities. Does anybody know how the CPU throttling of a process is done (code)? I'm not talking about setting the priority of a process, but rather how to limit it's CPU usage to for example 15% even if there is no other processes competing for CPU time.

Update: I need to be able to throttle any processes that is already running and that I have no source code access to.

4
  • Does your app need to be compiled for 32-bit (x86), 64-bit (x64), or both? Commented Feb 19, 2012 at 20:55
  • And do you want to do this to your own threads or to threads in other processes? And what is the use case? Commented Feb 19, 2012 at 20:56
  • 6
    The source code for BES is available, why don't you use it? Commented Feb 19, 2012 at 21:21
  • Another important clarification: are you willing to trust the process? In other words, are you concerned with apps that would try to subvert whatever you mechanism you use for throttling? Commented Feb 19, 2012 at 21:50

3 Answers 3

10

You probably want to run the process(es) in a job object, and set the maximum CPU usage for the job object with SetInformationJobObject, with JOBOBJECT_CPU_RATE_CONTROL_INFORMATION.

Sign up to request clarification or add additional context in comments.

2 Comments

Note that this use of a Windows job object seems to have been introduced in the Windows 8 developer preview--it is not available on Windows 7 / Server 2008 or previous releases of Windows.
This is a much better option than the accepted answer.
4

Very simplified, it could work somehow like this:

  1. Create a periodic waitable timer with some reasonable small wait time (maybe 100ms). Get a "last" value for each relevant process by calling GetProcessTimes once.
  2. Loop forever, blocking on the timer.
  3. Each time you wake up:
    • if GetProcessAffinityMask returns 0, call SetProcessAffinityMask(old_value). This means we've suspended that process in our last iteration, we're now giving it a chance to run again.
    • else call GetProcessTimes to get the "current" value
    • call GetSystemTimeAsFileTime
    • calculate delta by subtracting last from current
    • cpu_usage = (deltaKernelTime + deltaUserTime) / (deltaTime)
    • if that's more than you want call old_value = GetProcessAffinityMask followed by SetProcessAffinityMask(0) which will take the process offline.

This is basically a very primitive version of the scheduler that runs in the kernel, implemented in userland. It puts a process "to sleep" for a small amount of time if it has used more CPU time than what you deem right. A more sophisticated measurement maybe going over a second or 5 seconds would be possible (and probably desirable).

You might be tempted to suspend all threads in the process instead. However, it is important not to fiddle with priorities and not to use SuspendThread unless you know exactly what a program is doing, as this can easily lead to deadlocks and other nasty side effects. Think for example of suspending a thread holding a critical section while another thread is still running and trying to acquire the same object. Or imagine your process gets swapped out in the middle of suspending a dozen threads, leaving half of them running and the other half dead.

Setting the affinity mask to zero on the other hand simply means that from now on no single thread in the process gets any more time slices on any processor. Resetting the affinity gives -- atomically, at the same time -- all threads the possibility to run again.

Unluckily, SetProcessAffinityMask does not return the old mask as SetThreadAffinityMask does, at least according to the documentation. Therefore an extra Get... call is necessary.

5 Comments

Sorry to revive a thread, but SetProcessAffinityMask(current_process, 0) returns FALSE with ERROR_INVALID_PARAMETER as the last error-code. What am I doing wrong?
If on windows 8+ it's better to use SetInformationJobObject with JOBOBJECT_CPU_RATE_CONTROL_INFORMATION as described by Jerry Coffin
Is affinity 0x0 really supported? I tried under Windows 7 and the call fails with error 87 (ERROR_INVALID_PARAMETER). I opened the process handle with PROCESS_SET_INFORMATION permission.
I tried using NtSetInformationProcess with ProcessAffinityMask and reference to value 0x0 but that too fails with 0xC000000D (STATUS_INVALID_PARAMETER).
0 is not a valid parameter.
0

CPU usage is fairly simple to estimate using QueryProcessCycleTime. The machine's processor speed can be obtained from HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\\~MHz (where is the processor number, one entry for each processor present). With these values, you can estimate your process' CPU usage and yield the CPU as necessary using Sleep() to keep your usage in bounds.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.