BenchmarkDotNet
BenchmarkDotNet copied to clipboard
Fix "Unknown Processor" on Windows if WMIC is not present
This PR fixes #2747 by adding the WmiLightCpuDetector detector class that detects CPU info using the WmiLight library.
This PR also updates WmicCpuDetector to also check for the presence of WMIC.exe at the expected file path to check for applicability so that it isn't used on systems without WMIC.exe .
The output is as expected when tested. For testing I used the BenchmarkDotNet Samples project.
// * Summary *
BenchmarkDotNet v0.15.1-develop (2025-06-04), Windows 11 (10.0.26100.4061/24H2/2024Update/HudsonValley)
12th Gen Intel Core i7-1270P 2.20GHz, 1 CPU, 16 logical and 12 physical cores
.NET SDK 9.0.300
[Host] : .NET 8.0.16 (8.0.1625.21506), X64 RyuJIT AVX2 [AttachedDebugger]
DefaultJob : .NET 8.0.16 (8.0.1625.21506), X64 RyuJIT AVX2
@dotnet-policy-service agree
Implementation is incomplete. Mark it ready for review when it's ready.
@timcassell It's ready for review but I've noticed in the WmicCpuDetector parser something. Should the Max CPU Frequency be added to, with += rather than assignment, if multiple processors are detected? I'm inclined to say no but wanted to check.
if (processor.TryGetValue(WmicCpuInfoKeyNames.MaxClockSpeed, out string frequencyValue)
&& int.TryParse(frequencyValue, out int frequency)
&& frequency > 0)
{
sumMaxFrequency += frequency;
}
It looks like that is used to calculate an "average" across CPUs sumMaxFrequency / processorsCount. I don't understand what the purpose of that actually is, as it seems like a meaningless value to me. But @AndreyAkinshin probably has a better grasp of it.
Estimating the CPU's maximum frequency is not always straightforward. One of the main challenges is that some modern CPUs consist of heterogeneous cores — that is, cores with different nominal and maximum frequencies. To represent this accurately, we would technically need to track a full list of cores with individual frequency metrics. However, this seems unnecessarily complex for our use case.
First, such a detailed model would usually result in a verbose and redundant dataset — in most real-world scenarios, many cores share identical characteristics. Second, we’d still need to reduce this data to a single representative number for downstream analyses (e.g., in ZeroMeasurementAnalyzer), which defeats the purpose of maintaining fine-grained data.
After some consideration, I propose the following pragmatic approach:
-
MaxFrequency should be defined as the maximum of all reported per-core frequencies. This may not accurately reflect the maximum frequency of all cores (especially low-power ones in heterogeneous setups), but it aligns with our goal of capturing the upper bound of the CPU’s capabilities.
-
NominalFrequency should be defined as the minimum of all reported frequencies (excluding non-positive or clearly incorrect values). This gives us a baseline that roughly reflects the “typical” operating frequency. When different from
MaxFrequency, it provides a useful range to understand performance scaling behavior.
What do you think?
After some consideration, I propose the following pragmatic approach:
* **MaxFrequency** should be defined as the _maximum_ of all reported per-core frequencies. This may not accurately reflect the maximum frequency of all cores (especially low-power ones in heterogeneous setups), but it aligns with our goal of capturing the upper bound of the CPU’s capabilities. * **NominalFrequency** should be defined as the _minimum_ of all reported frequencies (excluding non-positive or clearly incorrect values). This gives us a baseline that roughly reflects the “typical” operating frequency. When different from `MaxFrequency`, it provides a useful range to understand performance scaling behavior.What do you think?
Makes sense. Should this be worked on in this PR or a new one? And how do we want to format the resulting clockspeeds? Something like: CPU Name, Number of Processors, Logical Processors, Nominal Frequency GHz, Maximum Frequency GHz ?
Should this be worked on in this PR or a new one?
Since we are introducing a new detector, it makes sense to implement a solution that addresses this concern. Let's do it in this PR.
And how do we want to format the resulting clockspeeds?
The detector should not be concerned about the formatting. Its purpose is solely to detect the CPU properties and return a data model.
The detector should not be concerned about the formatting. Its purpose is solely to detect the CPU properties and return a data model.
Understood.
LGTM
@alastairlundy could you please resolve the conflict with the master branch @timcassell what do you think?
Since the max/nominal frequency calculations are changed here, all the other cpu detectors need to be updated as well.
Since the max/nominal frequency calculations are changed here, all the other cpu detectors need to be updated as well.
The macOS detector & parser are able to get the nominal frequency already without any averaging so I think that's fine to leave as is.
I copied the Min/Max of nominal and max frequency from PowershellWmiCpuDetector to MosCpuDetector just now, and added it before to WmicCpuDetector. So all of the Windows supported Cpu detectors now behave the same (for detecting nominal and max frequency).
The LinuxCpuDetector however seems to follow a completely different methodology for getting nominal clockspeed.
@timcassell Do you want me to update the LinuxCpuDetector to also get nominal clockspeed the same way?
Yeah the Linux one seems strange, but I think it should also be updated to match behavior.
Maybe MacOS detector could use a null check, but other than that LGTM.
@alastairlundy Please fix failing tests.
@alastairlundy Please fix failing tests.
The CPU detection and parsing tests pass now.
LGTM. @alastairlundy, please resolve the conflicts with the latest master, and we will be ready for the merge.
LGTM. @alastairlundy, please resolve the conflicts with the latest master, and we will be ready for the merge.
It's resolved
@alastairlundy GitHub indicates that there are still conflicts preventing an automatic merge. The easiest way to resolve this issue is to squash your commits, rebase onto the latest master, and force-push your branch.
No problem with squash and merge