In Windows, threads run in units of “quantums”. After a thread completes its quantum, Windows may choose to run another thread based on priority or thread states.
This quantum settings is located in the registry called Win32PrioritySeparation. It is a wacky matrix that is represented in a bitfield.
Window XP and Vista uses short variable quantum settings. Thread owned by a process with a foreground window are assigned 18 quantums, and background window (e.g. services) are assigned 6 quantums. The Window Server edition uses 36 quantums for all threads.
So how long exactly is one quantum?
One Quantum
Although the length of a quantum is not exposed to developers, Windows Internalexplained that the value is located in a kernel variable called KiCyclesPerClockQuantum. You can extract the value through Windbg with a command “dd nt!KiCyclesPerClockQuantum l1“.
Alternatively, the book devised a method to calculate the value manually. Below is a program I wrote following the described algorithm.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
try
{
CPdhQuery procInfo_frequency(std::tstring(
_T("\Processor Information(0,0)\Processor Frequency"))
);
// Step 1: Get the CPU speed in MHz
__int64
cpuSpeedHz =
static_cast
<
__int64
>(
procInfo_frequency.CollectSingleData()
);
// Step 2: Convert it to Hz
cpuSpeedHz *= 1000000;
DWORD
timeAdjustment = 0;
DWORD
clockInterval100Ns = 0;
BOOL
timeAdjustmentDisabled = 0;
// Step 3: Get the frequency of the clock interrupt. This value is
// dependent on your processor type.
GetSystemTimeAdjustment(
&timeAdjustment,
&clockInterval100Ns,
&timeAdjustmentDisabled);
// Step 4: Get the rate of the clock fires per second.
double
clockIntervalPerSecond =
static_cast
<
double
>(clockInterval100Ns)/10000000;
// Step 5: Get the number of cycles elapsed per clock interval.
double
cyclesPerClockInterval = cpuSpeedHz * clockIntervalPerSecond;
// Step 6: A quantum is 1/3 of a clock interval.
__int64
clockCyclePerQuantum =
static_cast
<
__int64
>(cyclesPerClockInterval / 3);
// Step 7: The quantum length in time
double
quantumLengthSec =
static_cast
<
double
>(clockCyclePerQuantum) /
static_cast
<
double
>(cpuSpeedHz);
tcout
<< _T("Clock Cycles Per Quantum = ")
<< clockCyclePerQuantum
<< std::endl;
tcout
<< _T("Duration Per Quantum = ")
<< quantumLengthSec
<< _T(" second")
<< std::endl;
}
catch
(CPdhQuery::CException
const
&e)
{
tcout << e.What() << std::endl;
}
|
1
2
|
Clock Cycles Per Quantum = 13873688
Duration Per Quantum = 0.00520003 second
|
Thoughts
The quantum value provides insight on how often a thread may be preempted.
This information can be surprising useful. I recently used it to roughly estimate a thread’s response time, and correctly determined a device driver issue.
The output of my program differs slightly (~3%) from the readings in the kernel. It appears that the processor frequency in performance counter is different from the reading in PRCB.