组装CPU频率测量算法

问题描述 投票:16回答:10

用于测量处理器频率的常用算法是什么?

timer system cpu assembly
10个回答
18
投票

Core Duo之后的Intel CPU支持两个特定于模型的寄存器,称为IA32_MPERF和IA32_APERF。MPERF以CPU支持的最大频率计数,而APERF以实际当前频率计数。

实际频率是由:

“FREQ

您可以通过此流程阅读它们

; read MPERF
mov ecx, 0xe7
rdmsr
mov mperf_var_lo, eax
mov mperf_var_hi, edx

; read APERF
mov ecx, 0xe8
rdmsr
mov aperf_var_lo, eax
mov aperf_var_hi, edx

但是请注意,rdmsr是特权指令,只能在环0中运行。

我不知道操作系统是否提供读取这些的接口,尽管它们的主要用途是用于电源管理,因此它可能不提供这样的接口。


0
投票

在Intel CPU上,获取当前(平均)CPU频率的常用方法是从一些CPU计数器中进行计算:

CPU_freq = tsc_freq * (aperf_t1 - aperf_t0) / (mperf_t1 - mperf_t0)

TSC(时间戳计数器)可以使用专用的x86指令从用户空间读取,但是其频率必须通过对时钟进行校准来确定。最好的方法是到get the TSC frequency from the kernel(已经完成校准)。

aperf和mperf计数器是特定于模型的寄存器MSRs,需要具有root用户特权才能访问。同样,还有专用的x86指令用于访问MSR。

由于mperf计数器速率与TSC速率直接成正比,并且aperf速率与CPU频率成正比,因此可以通过上式获得CPU频率。

当然,如果CPU频率在t0 - t1时间增量中发生了变化(例如,由于适当的频率缩放而引起的变化,则可以通过这种方法获得平均CPU频率。

我写了一个小的实用程序cpufreq,可用于测试此方法。

另请参见:


6
投票

我将在这个答案中告诉我自己各种各样的细节,但是到底是什么...

几年前,我不得不在基于Windows的PC上解决此问题,所以我正在处理Intel x86系列处理器,例如486,Pentium等。在这种情况下,标准算法是执行一系列DIVide指令,因为这些指令通常是Intel集中CPU最多的单个指令。因此,内存预取和其他体系结构问题不会严重影响指令的执行时间-预取队列始终已满,并且指令本身不会接触任何其他内存。

您将使用可在运行环境中访问的最高分辨率时钟对它进行计时。(在我的情况下,我在兼容PC的启动时间附近运行,因此我直接在主板上对计时器芯片进行编程。不建议在实际操作系统中使用,通常这些天通常有一些适当的API可以调用)。

您需要处理的主要问题是不同的CPU类型。当时有英特尔,AMD和一些较小的供应商(如Cyrix)生产x86处理器。相对于DIV指令,每个模型都有其自己的性能特征。我的汇编计时功能只会返回由一定数量的紧紧循环中完成的DIV指令占用的多个时钟周期。

因此,我要做的是从运行我想计时的每个处理器模型的实际PC上收集一些计时(该函数的原始返回值),并根据已知的处理器速度和处理器类型将这些计时记录在电子表格中。实际上,我有一个命令行工具,它只是计时功能的薄壳,我可以将磁盘放入计算机存储中,并从显示模型中获得计时! (当时我在一家非常小的公司工作)。

使用这些原始时序,我可以绘制一个理论图,说明对于该特定CPU的任何已知速度,我应该获得什么时序。

这是诀窍:我总是讨厌何时运行实用程序,它会宣布您的CPU为99.8 Mhz或任何其他值。显然,它是100 Mhz,测量中的舍入误差很小。在我的电子表格中,我记录了每个处理器供应商出售的实际速度。然后,我将使用实际时序图来估计任何已知速度的预计时序。但是我会沿时间线建立一个点表,其中计时应舍入到下一个速度。

换句话说,如果100次滴答作完所有重复的除法运算就意味着500 Mhz,而200次滴答就意味着250 Mhz,那么我将建立一个表,表示低于150的任何值为500 Mhz,高于250的任何值为250 Mhz。 (假设那是该芯片供应商仅有的两种速度)。很好,因为即使PC上有些奇怪的软件拖延了我的工作时间,最终结果仍然经常会落空。

当然,在当今超频,用于电源管理的动态时钟速度以及其他类似技巧的今天,这种方案将变得不那么实用。在运行计时功能之前,至少需要做一些事情来确保CPU首先处于其动态选择的最高速度。

[好,我现在回去把孩子赶出草坪。


4
投票

自奔腾以来,在x86 Intel CPU上的一种方法是使用RDTSC指令的两次采样,并使用已知的壁挂时间延迟循环,例如:

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>

uint64_t rdtsc(void) {
    uint64_t result;
    __asm__ __volatile__ ("rdtsc" : "=A" (result));
    return result;
}

int main(void) {
    uint64_t ts0, ts1;    
    ts0 = rdtsc();
    sleep(1);
    ts1 = rdtsc();    
    printf("clock frequency = %llu\n", ts1 - ts0);
    return 0;
}

((在具有GCC的32位平台上)

RDTSC如果在CR4中设置了TSC标志,则在环3中可用,这是常见的但不能保证。这种方法的一个缺点是,如果频率缩放变化发生在延迟内,则很容易受到频率变化的影响。为了减轻这种负担,您可以执行使CPU保持忙碌并不断轮询系统时间以查看延迟时间是否已过的代码,以使CPU保持可用的最高频率状态。


2
投票

我使用以下(伪)算法:

basetime=time();    /* time returns seconds */

while (time()==basetime);
stclk=rdtsc();    /* rdtsc is an assembly instruction */

basetime=time();
while (time()==basetime
endclk=rdtsc();

nclks=encdclk-stclk;

此时,您可能已经确定时钟频率,但是即使它看起来正确,也可以改进。

所有PC都包含一个PIT(可编程间隔计时器)设备,该设备包含用于(过去)用于串行端口和系统时钟的计数器。它的频率为1193182 Hz。系统时钟计数器设置为最高倒计时值(65536),导致系统时钟滴答频率为1193182/65536 => 18.2065 Hz或每54.925毫秒一次。

因此,时钟增加到下一秒所需的滴答数将取决于。通常需要18个刻度,有时需要19个刻度。这可以通过执行两次以上算法并存储结果来进行处理。这两个结果将等于两个18个滴答序列或一个18和一个19滴答声。将不会连续出现两个19s。因此,通过取两个结果中较小的一个,您将获得18滴答秒。通过乘以18.2065并除以18.0来调整此结果,或者使用整数算术乘以182065来乘以90000并除以180000。90000是180000的一半,可以四舍五入。如果选择整数路由计算,请确保使用的是64位乘法和除法。

您现在将具有以Hz为单位的CPU时钟速度x,可以将其转换为kHz((x + 500)/ 1000)或MHz((x + 5000000)/ 1000000)。 500和500000分别是1000和1000000的一半,并在那里取整。要计算MHz,请勿经过kHz值,因为可能会出现舍入问题。使用Hz值和第二种算法。


1
投票

这是BogoMIPS之类的目的,但是如今CPU变得更加复杂。超标量CPU可以每个时钟发出多个指令,从而基于计数时钟周期进行任何测量来执行高度不准确的指令块。

CPU频率也根据提供的负载和/或温度而变化。 CPU当前以800 MHz运行的事实并不意味着它将始终以800 MHz运行,它可能会根据需要加速或下降。

如果您确实需要知道时钟频率,则应将其作为参数传递。板上的EEPROM将提供基本频率,如果时钟变化,您将需要能够读取CPU的电源状态寄存器(或进行OS调用)以找出该时刻的频率。

话虽这么说,可能还有其他方法可以完成您想做的事情。例如,如果要对特定代码路径花费多长时间进行高精度测量,则CPU可能具有以固定频率运行的性能计数器,这是比读取滴答计数寄存器更好的挂钟时间度量。


1
投票

“ lmbench”提供了一种可移植到不同体系结构的cpu频率算法。

它运行一些不同的循环,并且处理器的时钟速度是各个循环的执行频率的最大公约数。

当我们能够获得循环计数相对较多的循环时,此方法应始终有效。

http://www.bitmover.com/lmbench/


1
投票

一种选择是通过在每个循环中运行具有已知指令的代码来检测CPU频率

此功能包含在7zip中,因为我认为大约是v9.20。

> 7z b
7-Zip 9.38 beta  Copyright (c) 1999-2014 Igor Pavlov  2015-01-03

CPU Freq:  4266  4000  4266  4000  2723  4129  3261  3644  3362

最终数字是正确的(并且在我的PC和许多其他计算机上,我发现它是正确的-测试运行非常快,因此turbo可能不会启动,并且服务器大多设置为“平衡/省电”模式可能会给出大约1GHz的读数)

源代码位于GitHub(官方源可从7-zip.org下载)

最重要的部分是:

#define YY1 sum += val; sum ^= val;
#define YY3 YY1 YY1 YY1 YY1
#define YY5 YY3 YY3 YY3 YY3
#define YY7 YY5 YY5 YY5 YY5
static const UInt32 kNumFreqCommands = 128;

EXTERN_C_BEGIN

static UInt32 CountCpuFreq(UInt32 sum, UInt32 num, UInt32 val)
{
  for (UInt32 i = 0; i < num; i++)
  {
    YY7
  }
  return sum;
}

EXTERN_C_END

0
投票

我不确定为什么您需要为此组装。如果您使用的是具有/ proc文件系统的计算机,请运行:

> cat /proc/cpuinfo

可能会给您您所需要的。


0
投票

AMDIntel上的快速搜索显示CPUID应该使您可以访问CPU的最大频率。

© www.soinside.com 2019 - 2024. All rights reserved.