找出CPU时钟频率(每个核心,每个处理器)

时间:2011-12-02 04:53:56

标签: c++ cpu cpu-speed

像CPUz这样的程序非常擅长提供有关系统的深度信息(总线速度,内存时序等)

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

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

6 个答案:

答案 0 :(得分:30)

我会在这里扩展我的评论。这对我来说太大而且深入,无法在评论中使用。

你要做的事情非常困难 - 由于以下原因导致不切实际:

  • 没有可移植的方法来获得处理器频率。由于SpeedStep和Turbo Boost等效果,rdtsc NOT 始终提供正确的频率。
  • 所有已知的测量频率的方法都需要精确的时间测量。但是,坚定的骗子可以篡改系统中的所有时钟和计时器。
  • 以防篡改的方式准确读取处理器频率和时间将需要内核级访问。这意味着Windows的驱动程序签名。

无法通过便携方式获取处理器频率:

获得CPU频率的“简单”方法是两次调用rdtsc,其间的固定时间长度。然后将差异除去将给出频率。

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

例如,在我的Core i7 2600K上,rdtsc将始终显示3.4 GHz的频率。但实际上,它在1.6 GHz处空闲,并且在4.6 GHz处通过超频的Turbo Boost乘数加载到46x时钟。

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

获得真实频率:

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

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

进入这个阶段后,有多种方法可以读取频率。

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


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

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

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

列表一直在继续。换句话说,你不能相信任何计时器,因为一个有能力的黑客将能够欺骗所有这些计时器。例如,可以通过直接在OS内更改系统时钟来欺骗clock()gettimeofday()。愚弄QueryPerformanceCounter()更难。

真正衡量时间:

上面列出的所有时钟都很容易受到影响,因为它们通常以某种方式从相同的系统基本时钟导出。系统基本时钟通常与系统基准时钟相关联 - 在系统通过超频实用程序启动后可以更改。

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


总结:

构建任何类型的防篡改基准测试几乎肯定需要编写需要Windows签名的内核模式驱动程序。对于休闲基准作家来说,这通常是一种负担。

这导致了防篡改基准的短缺,这可能导致近年来竞争激烈的超频社区整体下滑。

答案 1 :(得分:3)

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

为了找到受限制的时钟速率(感谢microsft,hp和dell)HyperV主机(不可靠的perf计数器)和HyperV来宾(只能获得库存CPU速度,而不是当前速度),我已通过管理,通过试错误和侥幸,创建一个每个时钟只循环一次的循环。

代码如下 - C#5.0,SharpDev,32位,目标3.5,优化(关键),无debuger活跃(关键)

        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));

注释

    如果调试器处于活动状态,
  • 每个循环25条指令
  • 考虑事先运行一个2或3秒的循环来启动处理器(或至少尝试启动,了解这些天服务器受到多大程度的限制)
  • 在64位Core2和Haswell Pentium上测试并与CPU-Z进行比较

答案 2 :(得分:2)

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

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

其他信息herehere

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

答案 3 :(得分:1)

最简单的方法之一就是使用RDTSC,但看到这是反作弊机制,我把它作为内核驱动程序或超级遮阳板常驻代码

你可能还需要推出自己的计时代码**,这也可以用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;
}

**我认为@Mystical会提到安全性,但是由于我从未感受到颠覆低级别系统计时机制的冲动,可能会涉及更多,如果Mystical可以在其上添加一些东西会很好: )

答案 4 :(得分:1)

应该参考这份白皮书:Intel® Turbo Boost Technology in Intel® Core™ Microarchitecture (Nehalem) Based Processors。基本上,在采样周期T内产生几次UCC固定性能计数器的读数。

Relative.Freq = Delta(UCC)  / T

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

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

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

Cyring | CoreFreq GitHub的函数Core_Cycle函数示例。

答案 5 :(得分:1)

您需要使用CallNtPowerInformation。这是来自code sample项目的putil。 通过这种方式,您可以获得当前和最大CPU频率。据我所知,不可能获得每CPU频率。