在获取CPU频率方面需要一些帮助

本文关键字:帮助 方面需 获取 CPU 频率 | 更新日期: 2023-09-27 18:24:11

我正在尝试制作一个C#软件,读取有关CPU的信息并将其显示给用户(就像CPU-Z一样)。我目前的问题是,我没有找到显示CPU频率的方法。

起初,我尝试了使用Win32_Processor类的简单方法。事实证明,它非常有效,除非CPU超频(或欠锁)。

然后,我发现我的注册表在HKLM''HARDWARE''DESCRIPTION''System''CentralProcessor''0中包含CPU的"标准"时钟(即使超频)。问题是,在现代CPU中,当CPU不需要全功率时,核心乘法器会降低,因此CPU频率也在变化,但注册表中的值保持不变。

我的下一步是尝试使用RdTSC来实际计算CPU频率。我使用C++是因为如果方法有效,我可以将它嵌入C#项目中。我在找到下一个代码http://www.codeproject.com/Articles/7340/Get-the-Processor-Speed-in-two-simple-ways但问题是一样的:该程序只给了我最大频率(就像注册表值中的值,1-2Mhz的差异),而且它似乎加载的CPU比它应该加载的要多(我甚至有CPU负载峰值)。

#include "stdafx.h"
#include <windows.h>
#include <cstdlib>
#include "intrin.h"
#include <WinError.h>
#include <winnt.h>
float ProcSpeedCalc() {
#define RdTSC __asm _emit 0x0f __asm _emit 0x31
    // variables for the clock-cycles:
    __int64 cyclesStart = 0, cyclesStop = 0;
    // variables for the High-Res Preformance Counter:
    unsigned __int64 nCtr = 0, nFreq = 0, nCtrStop = 0;
    // retrieve performance-counter frequency per second:
    if(!QueryPerformanceFrequency((LARGE_INTEGER *) &nFreq))
        return 0;
    // retrieve the current value of the performance counter:
    QueryPerformanceCounter((LARGE_INTEGER *) &nCtrStop);
    // add the frequency to the counter-value:
    nCtrStop += nFreq;

    _asm
    {// retrieve the clock-cycles for the start value:
        RdTSC
        mov DWORD PTR cyclesStart, eax
        mov DWORD PTR [cyclesStart + 4], edx
    }
    do{
    // retrieve the value of the performance counter
    // until 1 sec has gone by:
         QueryPerformanceCounter((LARGE_INTEGER *) &nCtr);
      }while (nCtr < nCtrStop);
    _asm
    {// retrieve again the clock-cycles after 1 sec. has gone by:
        RdTSC
        mov DWORD PTR cyclesStop, eax
        mov DWORD PTR [cyclesStop + 4], edx
    }
    // stop-start is speed in Hz divided by 1,000,000 is speed in MHz
    return    ((float)cyclesStop-(float)cyclesStart) / 1000000;
}

int _tmain(int argc, _TCHAR* argv[])
{
    while(true)
    {
        printf("CPU frequency = %f'n",ProcSpeedCalc());
        Sleep(1000);
    }
    return 0;
}

我还应该提到,我已经在AMD CPU上测试了最后一种方法。我也尝试过RdTSC方法的一些其他代码,但都不能正常工作。

最后,我试着理解用于制作这个程序的代码https://code.google.com/p/open-hardware-monitor/source/browse/,但对我来说太复杂了。

所以,我的问题是:如何使用C++或C#实时确定CPU频率(即使CPU超频)?我知道这个问题被问了很多次,但没有一个真正回答我的问题。

在获取CPU频率方面需要一些帮助

是的,该代码占用并繁忙等待整整一秒钟,这导致该核心在一秒钟内100%繁忙。一秒钟的时间足以让动态时钟算法检测负载并将CPU频率踢出省电状态。如果带有boost的处理器真的显示出高于标记频率的频率,我不会感到惊讶。

不过,这个概念还不错。你要做的是睡眠大约一秒钟。然后,不是假设RDTSC调用正好相隔一秒,而是除以QueryPerformanceCounter指示的实际时间。

此外,我建议在QueryPerformanceCounter调用之前和之后检查RDTSC,以检测RDTSCQueryPerformanceCounter之间是否存在上下文切换,这会打乱您的结果。


不幸的是,新处理器上的RDTSC实际上并不计算CPU时钟周期。因此,这并不能反映动态变化的CPU时钟速率(不过,它确实在没有繁忙等待的情况下测量了标称速率,因此它比问题中提供的代码有了很大的改进)。

  • Bruce Dawson在一篇博客文章中解释了这一点

因此,看起来您毕竟需要访问特定于型号的寄存器。这是无法在用户模式下完成的。OpenHardwareMonitor项目既有可使用的驱动程序,也有用于频率计算的代码


float ProcSpeedCalc()
{
    /*
        RdTSC:
          It's the Pentium instruction "ReaD Time Stamp Counter". It measures the
          number of clock cycles that have passed since the processor was reset, as a
          64-bit number. That's what the <CODE>_emit</CODE> lines do.
    */
    // Microsoft inline assembler knows the rdtsc instruction.  No need for emit.
    // variables for the CPU cycle counter (unknown rate):
    __int64 tscBefore, tscAfter, tscCheck;
    // variables for the Performance Counter 9steady known rate):
    LARGE_INTEGER hpetFreq, hpetBefore, hpetAfter;

    // retrieve performance-counter frequency per second:
    if (!QueryPerformanceFrequency(&hpetFreq)) return 0;
    int retryLimit = 10;    
    do {
        // read CPU cycle count
        _asm
        {
            rdtsc
            mov DWORD PTR tscBefore, eax
            mov DWORD PTR [tscBefore + 4], edx
        }
        // retrieve the current value of the performance counter:
        QueryPerformanceCounter(&hpetBefore);
        // read CPU cycle count again, to detect context switch
        _asm
        {
            rdtsc
            mov DWORD PTR tscCheck, eax
            mov DWORD PTR [tscCheck + 4], edx
        }
    } while ((tscCheck - tscBefore) > 800 && (--retryLimit) > 0);
    Sleep(1000);
    do {
        // read CPU cycle count
        _asm
        {
            rdtsc
            mov DWORD PTR tscAfter, eax
            mov DWORD PTR [tscAfter + 4], edx
        }
        // retrieve the current value of the performance counter:
        QueryPerformanceCounter(&hpetAfter);
        // read CPU cycle count again, to detect context switch
        _asm
        {
            rdtsc
            mov DWORD PTR tscCheck, eax
            mov DWORD PTR [tscCheck + 4], edx
        }
    } while ((tscCheck - tscAfter) > 800 && (--retryLimit) > 0);
    // stop-start is speed in Hz divided by 1,000,000 is speed in MHz
    return (double)(tscAfter - tscBefore) / (double)(hpetAfter.QuadPart - hpetBefore.QuadPart) * (double)hpetFreq.QuadPart / 1.0e6;
}

大多数编译器都提供__rdtsc()内部,在这种情况下,可以使用tscBefore = __rdtsc();而不是__asm块。不幸的是,这两种方法都是特定于平台和编译器的。

答案取决于您真正想知道的内容。

如果您的目标是找到当前运行的某个特定应用程序的操作频率,那么这是一个难题,需要管理员/root权限才能访问特定型号的寄存器,甚至可能访问BIOS。您可以在Windows上使用CPU-Z,也可以在Linux上使用powertop。

然而,如果你只想知道一个或多个负载线程的处理器工作频率,以便例如计算峰值触发器(这就是我关心这一点的原因),那么这可以用或多或少不需要管理员权限的通用代码来完成。

我从Bruce Dawson在http://randomascii.wordpress.com/2013/08/06/defective-heat-sinks-causing-garbage-gaming/.我主要使用OpenMP扩展了他的代码以处理多个线程。

我已经在Linux和Windows上的英特尔处理器上测试了这一点,包括Nahalem、Ivy Bridge和Haswell,其中一个插槽最多四个插槽(40个线程)。结果与正确答案的偏差均小于0.5%。

我在这里描述了如何确定频率——如何语法化——以及如何使用——所以我不重复所有的细节。

您的问题根本无法回答。CPU频率不断变化。有时操作系统知道这些变化,可以告诉你,但有时它不知道。CPU可能会超频(TurboBoost)或欠锁(由于过热)。一些处理器通过以相同的速率运行时钟来降低功率以避免熔化,但只在某些周期内工作,在这一点上,时钟频率的整个概念是没有意义的。

在这篇文章中,我谈论了大量的机器,我分析了在Windows没有注意到的情况下CPU被热调节的地方。

http://randomascii.wordpress.com/2013/08/06/defective-heat-sinks-causing-garbage-gaming/

可以编写一些非常特定于处理器的混乱代码来检测这种情况,但这需要管理员权限。

我的观点是,你问的是一个无法回答的问题,在大多数情况下,这不是你应该问的问题。使用注册表中的值,或者询问Windows CPU运行的频率(请参阅PROCESSOR_POWER_INFORMATION),并称其足够好。