C++ 找出 CPU 时钟频率(每个内核、每个处理器)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8351944/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:24:38  来源:igfitidea点击:

Finding out the CPU clock frequency (per core, per processor)

c++cpucpu-speed

提问by kidoman

Programs like CPUz are very good at giving in depth information about the system (bus speed, memory timings, etc.)

CPUz 之类的程序非常擅长提供有关系统的深度信息(总线速度、内存时序等)

However, is there a programmatic way of calculating the per core (and per processor, in multi processor systems with multiple cores per CPU) frequency without having to deal with CPU specific info.

但是,是否有一种编程方法可以计算每个内核(以及每个处理器,在每个 CPU 具有多个内核的多处理器系统中)频率,而无需处理 CPU 特定信息。

I am trying to develop a anti cheating tool (for use with clock limited benchmark competitions) which will be able to record the CPU clock during the benchmark run for all the active cores in the system (across all processors.)

我正在尝试开发一个反作弊工具(用于时钟有限的基准测试竞赛),它能够在系统中所有活动内核(跨所有处理器)的基准测试运行期间记录 CPU 时钟。

采纳答案by Mysticial

I'll expand on my comments here. This is too big and in-depth for me to fit in the comments.

我将在这里扩展我的评论。这对我来说太大而深入,无法放入评论中。

What you're trying to do is very difficult - to the point of being impractical for the following reasons:

您尝试做的事情非常困难 - 由于以下原因而变得不切实际:

  • There's no portable way to get the processor frequency. rdtscdoes NOTalways give the correct frequency due to effects such as SpeedStep and Turbo Boost.
  • All known methods to measure frequency require an accurate measurement of time. However, a determined cheater can tamper with all the clocks and timers in the system.
  • Accurately reading either the processor frequency as well as time in a tamper-proof way will require kernel-level access. This implies driver signing for Windows.
  • 没有可移植的方法来获取处理器频率。rdtsc不要总是给正确的频率,由于效果,如SpeedStep技术和Turbo Boost。
  • 所有已知的测量频率的方法都需要准确的时间测量。然而,一个坚定的作弊者可以篡改系统中的所有时钟和计时器。
  • 以防篡改的方式准确读取处理器频率和时间将需要内核级访问。这意味着 Windows 的驱动程序签名。


There's no portable way to get the processor frequency:

没有可移植的方式来获取处理器频率:

The "easy" way to get the CPU frequency is to call rdtsctwice with a fixed time-duration in between. Then dividing out the difference will give you the frequency.

获取 CPU 频率的“简单”方法是调用rdtsc两次,其间具有固定的持续时间。然后除以差异将为您提供频率。

The problem is that rdtscdoes not give the true frequency of the processor. Because real-time applications such as games rely on it, rdtscneeds to be consistent through CPU throttling and Turbo Boost. So once your system boots, rdtscwill always run at the same rate (unless you start messing with the bus speeds with SetFSBor something).

问题是rdtsc没有给出处理器的真实频率。因为游戏等实时应用依赖于它,rdtsc需要通过 CPU 节流和 Turbo Boost 保持一致。因此,一旦您的系统启动,rdtsc将始终以相同的速率运行(除非您开始使用SetFSB或其他东西来弄乱总线速度)。

For example, on my Core i7 2600K, rdtscwill always show the frequency at 3.4 GHz. But in reality, it idles at 1.6 GHzand clocks up to 4.6 GHzunder load via the overclocked Turbo Boost multiplier at 46x.

例如,在我的 Core i7 2600K 上,rdtsc将始终显示频率为3.4 GHz。但实际上,它通过超频的 Turbo Boost 倍频器在 0 处闲置1.6 GHz并加速到4.6 GHz欠载46x

But once you find a way to measure the true frequency, (or you're happy enough with rdtsc), you can easily get the frequency of each core using thread-affinities.

但是,一旦您找到一种测量真实频率的方法(或者您对 感到满意rdtsc),您就可以使用thread-affinities轻松获得每个内核的频率。

Getting the True Frequency:

获取真实频率:

To get the true frequency of the processor, you need to access either the MSRs (model-specific registers) or the hardware performance counters.

要获得处理器的真实频率,您需要访问 MSR(特定于模型的寄存器)或硬件性能计数器。

These are kernel-level instructions and therefore require the use of a driver. If you're attempting this in Windows for the purpose of distribution, you will therefore need to go through the proper driver signing protocol. Furthermore, the code will differ by processor make and model so you will need different detection code for each processor generation.

这些是内核级指令,因此需要使用驱动程序。如果您出于分发目的在 Windows 中尝试此操作,则您需要通过正确的驱动程序签名协议。此外,代码会因处理器品牌和型号而异,因此您需要为每一代处理器提供不同的检测代码。

Once you get to this stage, there are a variety of ways to read the frequency.

一旦到了这个阶段,有多种方法可以读取频率。

On Intel processors, the hardware counters let you count raw CPU cycles. Combined with a method of precisely measuring real time (next section), you can compute the true frequency. The MSRs give you access to other information such as the CPU frequency multiplier.

在 Intel 处理器上,硬件计数器可让您计算原始 CPU 周期。结合精确测量实时的方法(下一节),您可以计算出真实频率。通过 MSR,您可以访问其他信息,例如 CPU 倍频器。



All known methods to measure frequency require an accurate measurement of time:

所有已知的频率测量方法都需要准确测量时间:

This is perhaps the bigger problem. You need a timer to be able to measure the frequency. A capable hacker will be able to tamper with all the clocks that you can use in C/C++. This includes all of the following:

这也许是更大的问题。您需要一个计时器才能测量频率。一个有能力的黑客将能够篡改您可以在 C/C++ 中使用的所有时钟。这包括以下所有内容:

  • clock()
  • gettimeofday()
  • QueryPerformanceCounter()
  • etc...
  • clock()
  • gettimeofday()
  • QueryPerformanceCounter()
  • 等等...

The list goes on and on. In other words, you cannot trust any of the timers as a capable hacker will be able to spoof all of them. For example clock()and gettimeofday()can be fooled by changing the system clock directly within the OS. Fooling QueryPerformanceCounter()is harder.

这份清单不胜枚举。换句话说,您不能相信任何计时器,因为有能力的黑客将能够欺骗所有计时器。例如clock()gettimeofday()可以通过直接在操作系统内更改系统时钟来愚弄。骗人QueryPerformanceCounter()更难。

Getting a True Measurement of Time:

获得真正的时间测量:

All the clocks listed above are vulnerable because they are often derived off of the same system base clock in some way or another. And that system base clock is often tied to the system base clock - which can be changed after the system has already booted up by means of overclocking utilities.

上面列出的所有时钟都很脆弱,因为它们通常以某种方式从同一个系统基本时钟派生而来。并且该系统基本时钟通常与系统基本时钟相关联 - 可以在系统启动后通过超频实用程序进行更改。

So the only way to get a reliable and tamper-proof measurement of time is to read external clocks such as the HPETor the ACPI. Unfortunately, these also seem to require kernel-level access.

因此,获得可靠且防篡改的时间测量的唯一方法是读取外部时钟,例如HPETACPI。不幸的是,这些似乎也需要内核级访问。



To Summarize:

总结:

Building any sort of tamper-proof benchmark will almost certainly require writing a kernel-mode driver which requires certificate signing for Windows. This is often too much of a burden for casual benchmark writers.

构建任何类型的防篡改基准几乎肯定需要编写内核模式驱动程序,该驱动程序需要为 Windows 进行证书签名。对于随意的基准测试作者来说,这通常是太多的负担。

This has resulted in a shortage of tamper-proof benchmarks which has probably contributed to the overall decline of the competitive overclocking community in recent years.

这导致了防篡改基准的短缺,这可能是近年来竞争性超频社区整体下滑的原因之一。

回答by Patrick

I realise this has already been answered. I also realise this is basically a black art, so please take it or leave it - or offer feedback.

我意识到这已经得到了回答。我也意识到这基本上是一种黑色艺术,所以请接受或留下它 - 或提供反馈。

In a quest to find the clock rate on throttled (thanks microsft,hp, and dell) HyperV hosts (unreliable perf counter), and HyperV guests (can only get stock CPU speed, not current), I have managed, through trial error and fluke, to create a loop that loops exactly once per clock.

为了在节流(感谢 microsft、hp 和戴尔)HyperV 主机(不可靠的性能计数器)和 HyperV 来宾(只能获得库存 CPU 速度,而不是当前速度)上找到时钟频率,我通过试错和侥幸,创建一个循环,每个时钟只循环一次。

Code as follows - C# 5.0, SharpDev, 32bit, Target 3.5, Optimize on (crucial), no debuger active (crucial)

代码如下 - C# 5.0, SharpDev, 32bit, Target 3.5, Optimize on (crucial), no debuger active (crucial)

        long frequency, start, stop;
        double multiplier = 1000 * 1000 * 1000;//nano
        if (Win32.QueryPerformanceFrequency(out frequency) == false)
            throw new Win32Exception();

        Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
        const int gigahertz= 1000*1000*1000;
        const int known_instructions_per_loop = 1; 

        int iterations = int.MaxValue;
        int g = 0;

        Win32.QueryPerformanceCounter(out start);
        for( i = 0; i < iterations; i++)
        {
            g++;
            g++;
            g++;
            g++;
        }
        Win32.QueryPerformanceCounter(out stop);

        //normal ticks differs from the WMI data, i.e 3125, when WMI 3201, and CPUZ 3199
        var normal_ticks_per_second = frequency * 1000;
        var ticks = (double)(stop - start);
        var time = (ticks * multiplier) /frequency;
        var loops_per_sec = iterations / (time/multiplier);
        var instructions_per_loop = normal_ticks_per_second  / loops_per_sec;

        var ratio = (instructions_per_loop / known_instructions_per_loop);
        var actual_freq = normal_ticks_per_second / ratio;

        Console.WriteLine( String.Format("Perf counhter freq: {0:n}", normal_ticks_per_second));
        Console.WriteLine( String.Format("Loops per sec:      {0:n}", loops_per_sec));
        Console.WriteLine( String.Format("Perf counter freq div loops per sec: {0:n}", instructions_per_loop));
        Console.WriteLine( String.Format("Presumed freq: {0:n}", actual_freq));
        Console.WriteLine( String.Format("ratio: {0:n}", ratio));

Notes

笔记

  • 25 instructions per loop if debugger is active
  • Consider running a 2 or 3 seconds loop before hand to spin up the processor (or at least attempt to spin up, knowing how heavily servers are throttled these days)
  • Tested on a 64bit Core2 and Haswell Pentium and compared against CPU-Z
  • 如果调试器处于活动状态,每个循环 25 条指令
  • 考虑在手前运行 2 或 3 秒循环来启动处理器(或至少尝试启动,知道这些天服务器受到多大程度的限制)
  • 在 64 位 Core2 和 Haswell Pentium 上测试并与 CPU-Z 进行比较

回答by Olof Forshell

I've previously posted on this subject (along with a basic algorithm): here. To my knowledge the algorithm (see the discussion) is very accurate. For example, Windows 7 reports my CPU clock as 2.00 GHz, CPU-Z as 1994-1996 MHz and my algorithm as 1995025-1995075 kHz.

我之前发布过关于这个主题的帖子(以及一个基本算法):here。据我所知,算法(见讨论)非常准确。例如,Windows 7 报告我的 CPU 时钟为 2.00 GHz,CPU-Z 为 1994-1996 MHz,我的算法为 1995025-1995075 kHz。

The algorithm performs a lot of loops to do this which causes the CPU frequency to increase to maximum (as it also will during benchmarks) so speed-throttling software won't come into play.

该算法执行大量循环来执行此操作,这会导致 CPU 频率增加到最大值(在基准测试期间也是如此),因此速度限制软件不会发挥作用。

Additional info hereand here.

此处此处的其他信息。

On the question of speed-throttling I really don't see it as a problem unless an application uses the speed values to determine elapsed times and that the times themselves are extremely important. For example, if a division requires x clock cycles to complete it doesn't matter if the CPU is running at 3 GHz or 300 MHz: it will still need x clock cycles and the only difference is that it will complete the division in a tenth of the time at @ 3 GHz.

关于速度限制的问题,我真的不认为这是一个问题,除非应用程序使用速度值来确定经过的时间并且时间本身非常重要。例如,如果一个除法需要 x 个时钟周期来完成,那么 CPU 运行在 3 GHz 还是 300 MHz 并不重要:它仍然需要 x 个时钟周期,唯一的区别是它将在十分之一内完成除法在@ 3 GHz 的时间。

回答by Necrolis

One of the most simple ways to do it is using RDTSC, but seeing as this is for anti-cheating mechanisms, I'd put this in as a kernel driver or a hyper-visor resident piece of code.

最简单的方法之一是使用RDTSC,但鉴于这是用于反作弊机制,我会将其作为内核驱动程序或管理程序驻留代码段放入。

You'd probably also need to roll your own timing code**, which again can be done with RDTSC(QPC as used in the example below uses RDTSC, and its in fact very simple to reverse engineer and use a local copy of, which means to tamper with it, you'd need to tamper with your driver).

您可能还需要滚动自己的计时代码**,这也可以使用RDTSC(下面示例中使用的 QPC 使用RDTSC,实际上很容易逆向工程并使用其本地副本,这意味着篡改它,您需要篡改您的驱动程序)。

void GetProcessorSpeed()
{
    CPUInfo* pInfo = this;
    LARGE_INTEGER qwWait, qwStart, qwCurrent;
    QueryPerformanceCounter(&qwStart);
    QueryPerformanceFrequency(&qwWait);
    qwWait.QuadPart >>= 5;
    unsigned __int64 Start = __rdtsc();
    do
    {
        QueryPerformanceCounter(&qwCurrent);
    }while(qwCurrent.QuadPart - qwStart.QuadPart < qwWait.QuadPart);
    pInfo->dCPUSpeedMHz = ((__rdtsc() - Start) << 5) / 1000000.0;
}

** I this would be for security as @Mystical mentioned, but as I've never felt the urge to subvert low level system timing mechanisms, there might be more involved, would be nice if Mystical could add something on that :)

** 正如@Mystical 提到的那样,我这样做是为了安全,但因为我从来没有感受到颠覆低级系统计时机制的冲动,可能会有更多的参与,如果 Mystical 可以添加一些东西,那就太好了:)

回答by CyrIng

One should refer to this white paper: Intel? Turbo Boost Technology in Intel? Core? Microarchitecture (Nehalem) Based Processors. Basically, produce several reads of the UCC fixed performance counter over a sample period T.

应该参考这份白皮书:英特尔?英特尔的 Turbo Boost 技术?核?基于微架构(Nehalem)的处理器。基本上,在采样周期 T 内对 UCC 固定性能计数器进行多次读取。

Relative.Freq = Delta(UCC)  / T

Where:
   Delta() = UCC @ period T
                 - UCC @ period T-1

Starting with Nehalem architecture, UCC increase and decrease the number of click ticks relatively to the Unhalted state of the core.

从 Nehalem 架构开始,UCC 相对于核心的 Unhalted 状态增加和减少点击滴答的数量。

When SpeedStep or Turbo Boost are activated, the estimated frequency using UCC will be measured accordingly; while TSC remains constant. For instance, Turbo Boost in action reveals that Delta(UCC) is greater or equal to Delta(TSC)

当 SpeedStep 或 Turbo Boost 被激活时,将相应地测量使用 UCC 的估计频率;而 TSC 保持不变。例如,Turbo Boost 在行动中显示 Delta(UCC) 大于或等于 Delta(TSC)

Example in function Core_Cyclefunction at Cyring | CoreFreqGitHub.

Cyring 的函数Core_Cycle函数示例 | CoreFreqGitHub。

回答by Giampaolo Rodolà

You need to use CallNtPowerInformation. Here's a code samplefrom putilproject. With this you can get current and max CPU frequency. As far as I know it's not possible to get per-CPU frequency.

您需要使用CallNtPowerInformation。这是来自putil项目的代码示例。有了这个,您可以获得当前和最大 CPU 频率。据我所知,不可能获得每个 CPU 的频率。