Geoff Chappell - Software Analyst
The numproc option sets the number of logical processors to use.
To have the BCDEDIT tool set the numproc option for the currently running operating system, run the command
bcdedit /set numproc number
where number is any decimal integer. To set the option at the Edit Boot Options Menu, add
which is also the syntax for the option as a BOOT.INI switch in earlier Windows versions.
Microsoft’s documentation is unclear whether the numproc option counts logical or physical processors. In its form as a BOOT.INI switch for earlier versions of Windows, this option was barely documented (see Available switch options for the Windows XP and the Windows Server 2003 Boot.ini files):
This switch sets the number of processors that Windows will run at startup. With this switch, you can force a multiprocessor system to use only the quantity of processors (number) that you specify. This switch can help you troubleshoot performance problems and defective CPUs.
As a Boot Configuration Data (BCD) option, there seems to be no formal documentation, but the command bcdedit /? types osloader says of the option
Uses only the specified number of processors.
and the System Configuration applet among the Administrative Tools exposes the option as “Number of processors” in the dialog box that is reached from the Advanced Options button on the Boot tab.
See that in all these cases, Microsoft avoids the question of whether the number is of logical processors or physical.
The answer for the original Windows Vista is: both. This answer is of course ridiculous, but the fact is that the kernel at one time compares number with a count of licensed processors, meaning physical, and at later times with a count that mixes logical and physical processors. This different interpretation at different times is of itself a logic failure, but this is no mere coding error during programming. It’s systemic. While the legalese of the license terms and the documentation of the numproc option both skirt the logical-or-physical question, the presence of a coding error here is an unsurprising, and even inevitable, consequence of managerial inattention.
The practical consequence for users is that whether you have multiple physical processors or just have multiple logical processors on the one physical processor, setting numproc may not have just the intended effect of constraining Windows to use only the set number of logical processors. It may also cause Windows to act as if your processors do not support so-called large pages. To see why, look a little more closely at those two comparisons.
The first comparison is made as soon as the kernel interprets the /NUMPROC switch during initialisation and finds a non-zero number. It compares number (read into an internal variable named KeNumprocSpecified) with the count of licensed processors that has just been read from the Kernel-RegisteredProcessors license value (into an internal variable named KeRegisteredProcessors). If number is smaller, the count in KeRegisteredProcessors is reduced to match.
This means that number counts whatever sort of processor is counted for licensing, but it also means that numproc has an effect that the terse documentation does not even hint at. If the numproc value is non-zero but is less than the license value, then the count read from the license value is lost. It is not directly true that numproc sets “the number of processors that Windows will run at startup” as claimed by the documentation. What numproc actually sets is the number of processors that Windows will believe are licensed to run.
To point out this difference may look like splitting hairs. If the kernel will anyway ignore unlicensed processors, then what better way is there to remove some unwanted processors from use than to treat them as unlicensed? This coding, which dates at least from Windows 2000, may even have been thought efficient, and with good reason at the time. Unfortunately, years later, for Windows Server 2003 SP2, someone has coded for the kernel to notice the presence of an unlicensed processor and to apply a penalty. So now the difference is very real. For instance, if you have two physical processors and you set numproc to 1, then the second of your processors is not just unused, it is treated as unlicensed and you lose large-page support (which is your undocumented punishment for trying to run an unlicensed processor).
Look now at how the kernel initialises logical processors when it discovers them from the HAL (in the internal routine KeStartAllProcessors). Each newly enumerated logical processor may be in the same physical package as one that has already been enumerated, or not. When it is not, it must be the first logical processor for a new physical processor and it is counted against the license value. If the number of accepted processors (represented by the documented, exported variable KeNumberProcessors) has already reached the license value (represented by the internal variable KeRegisteredProcessors), then this newly enumerated physical processor is unlicensed and cannot be accepted for use. The kernel continues enumerating, but only in the hope of discovering additional logical processors for physical processors that have been accepted as licensed.
If a newly enumerated logical processor is in the same physical package as one that has already been enumerated, then in the typical case with no /NUMPROC switch the kernel simply increments the KeRegisteredProcessors variable and accepts the processor (thus also incrementing KeNumberProcessors). Of itself, this is a fine way to implement that additional logical processors of a licensed physical processor are automatically licensed. However, this coding has as a side-effect that it makes a hybrid of the KeRegisteredProcessors variable. It counts some physical processors and some logical. It ends up as the number of physical processors that are allowed by license, whether present or not, plus the number of extra logical processors in any physical processors that actually are present. Equivalently, it is the number of logical processors that are present, plus 1 for each physical processor that would be licensed if it were present. For instance, on a machine with a single dual-core processor and a license to use two processors, this variable ends up containing 3.
The inherent danger in what might otherwise be an abstract point of logic is made real by a small variation that executes only if given a non-zero /NUMPROC argument. If a newly enumerated logical processor is in the same physical package as one that has already been enumerated, it is accepted as automatically licensed only if the current value of KeRegisteredProcessors is less than the /NUMPROC argument. This is the second comparison referred to above. If KeRegisteredProcessors is already as great as the /NUMPROC argument, then the newly enumerated logical processor is treated as the first logical processor in a new physical processor—and, worse, it may then be treated as an unlicensed physical processor. Most notably, if you have one physical processor, then all logical processors in excess of the /NUMPROC argument are treated as unlicensed.
It may help to have an easy demonstration that something really is wrong. First, write a nearly trivial program that tests for large-page support. All that’s needed is to call the documented GetLargePageMinimum function and report what’s returned. Second, on a machine with one multi-core processor, i.e., with one physical processor containing two or more logical processors, start the original Windows Vista normally, then run the program to confirm that large pages are ordinarily supported. Third, restart Windows but press F10 to get the Edit Boot Options menu. Add /NUMPROC=1 to the active options, then continue. When Windows, now limited to one processor, has started, re-run the program and see that you have lost the use of large pages.
To run just the first logical processor on a machine with one multi-core processor, there is an alternative. Use the BCD option onecpu or its equivalent /ONECPU switch in the Edit Boot Options menu. This is interpreted by the HAL to mean that only one logical processor should be disclosed. The kernel never learns that a second logical or physical processor is available. That’s not exactly the same as might be expected from setting numproc to 1, but it’s as close as you’ll get until Microsoft acknowledges the problem and fixes it.
That Microsoft might acknowledge the problem is at best fanciful, especially since even a working numproc is barely documented, but at least the problem has got fixed, starting with Windows Vista SP1. It seems unlikely that Microsoft learned of the coding error and decided it was important enough to fix. More plausible is that the coding error was noticed incidentally during revisions to support dynamic processors and the new /BOOTPROC switch. Although the original Windows Vista does have code for dynamic processors, most notably a new exported function (KeStartDynamicProcessor), the coding is plainly incomplete. This won’t have mattered for the original Windows Vista, no editions of which were licensed for dynamic processors, but it will have needed attention for Windows Vista SP1, since it doubles as Windows Server 2008 which is licensed for dynamic processors. One of the things that the original Windows Vista leaves undone is to check that starting a dynamic processor does not exceed various limits. Among these are the license limit and the numproc value, and so the earlier coding will naturally have got revised. Sadly, it’s all too plausible that the defectiveness of the earlier coding was not even noticed.
In Windows Vista SP1 and higher, numproc is unambiguously a count of logical processors, including any that are started dynamically. To set a (lower) number of logical processors that may start duing initialisation, add a /BOOTPROC=number switch at the Edit Boot Options Menu or in the loadoptions option.