获得CPU频率需要一些帮助

The*_*ner 5 c# c++ visual-c++

我正在尝试制作一个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-2 Mhz的差异),它看起来像它加载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;
}
Run Code Online (Sandbox Code Playgroud)

我还要提一下,我已经在AMD CPU上测试了最后一种方法.我还尝试了一些其他代码用于RdTSC方法,但没有一个正常工作.

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

所以,我的问题是:如何使用C++或C#实时确定CPU频率(即使CPU超频)?我知道很多次都问过这个问题,但实际上没有人回答我的问题.

Ben*_*igt 8

是的,该代码处于忙碌状态并等待整整一秒,这导致该核心在100秒内忙碌一秒钟.一秒钟就足以让动态时钟算法检测负载并使CPU频率超出节能状态.如果具有增强功能的处理器实际上显示频率高于标记频率,我不会感到惊讶.

然而,这个概念并不坏.你要做的是一段时间约一秒钟.然后,不是假设RDTSC调用恰好相隔一秒,而是除以指示的实际时间QueryPerformanceCounter.

另外,我建议RDTSCQueryPerformanceCounter调用之前和之后检查两者,以检测是否存在上下文切换RDTSC以及QueryPerformanceCounter哪些会弄乱您的结果.


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

所以看起来你毕竟需要访问特定于模型的寄存器.这不能从用户模式来完成. 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;
}
Run Code Online (Sandbox Code Playgroud)

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