Timer 汇编CPU频率测量算法

Timer 汇编CPU频率测量算法,timer,system,cpu,assembly,Timer,System,Cpu,Assembly,用于测量处理器频率的常用算法有哪些?我不确定为什么需要组装。如果您在具有/proc文件系统的计算机上,则运行: > cat /proc/cpuinfo 可能会给你所需要的。这是类似的意图,但如今CPU要复杂得多。超标量CPU可以在每个时钟上发出多条指令,这使得任何基于计数时钟周期的测量都极不准确 CPU频率也根据提供的负载和/或温度而变化。CPU当前运行在800 MHz的事实并不意味着它将始终运行在800 MHz,它可能会根据需要调节油门 如果你真的需要知道时钟频率,它应该作为一个参数传

用于测量处理器频率的常用算法有哪些?

我不确定为什么需要组装。如果您在具有/proc文件系统的计算机上,则运行:

> cat /proc/cpuinfo

可能会给你所需要的。

这是类似的意图,但如今CPU要复杂得多。超标量CPU可以在每个时钟上发出多条指令,这使得任何基于计数时钟周期的测量都极不准确

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

如果你真的需要知道时钟频率,它应该作为一个参数传入。电路板上的EEPROM将提供基频,如果时钟可能发生变化,您需要能够读取CPU电源状态寄存器(或进行操作系统调用),以了解当时的频率


说了这么多,可能还有其他方法来完成你想要做的事情。例如,如果您想对特定代码路径所需的时间进行高精度测量,CPU可能具有以固定频率运行的性能计数器,这比读取滴答计数寄存器更好地衡量墙上时钟时间。

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

实际频率由下式给出:

你可以用这个流程来阅读它们

; 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中运行


我不知道操作系统是否提供了一个接口来阅读这些内容,尽管它们的主要用途是电源管理,所以它可能不提供这样的接口。

我将在这个答案中列出各种细节,但到底是什么

几年前,我不得不在基于Windows的PC机上解决这个问题,所以我在处理英特尔x86系列处理器,如486、奔腾等。这种情况下的标准算法是执行一长串除法指令,因为这些指令通常是英特尔集合中CPU占用最多的单指令。因此,内存预取和其他体系结构问题不会对指令执行时间产生实质性影响——预取队列总是满的,指令本身不会触及任何其他内存

您可以使用在您运行的环境中可以访问的最高分辨率时钟来计时。(在我的例子中,我是在一台兼容PC的电脑上运行的,所以我直接在主板上编程定时器芯片。不建议在真正的操作系统中使用,现在通常有一些合适的API可以调用)

您必须处理的主要问题是不同的CPU类型。当时有英特尔、AMD和Cyrix等一些较小的供应商生产x86处理器。与DIV指令相比,每个模型都有自己的性能特征。我的汇编计时函数只返回一定数量的DIV指令在紧循环中执行的时钟周期数

因此,我所做的是从运行我想要计时的每种处理器型号的实际PC机上收集一些计时(该函数的原始返回值),并根据已知的处理器速度和处理器类型将其记录在电子表格中。实际上,我有一个命令行工具,它只是我计时功能的一个薄壳,我会把一张磁盘带到计算机商店,从显示模型中获取计时信息!(当时我在一家非常小的公司工作)

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

这里是诀窍:我总是讨厌当你运行一个实用程序,它会宣布你的CPU是99.8兆赫或任何东西。很明显,它是100兆赫,测量中只有一个小的舍入误差。在我的电子表格中,我记录了每个处理器供应商销售的实际速度。然后,我将使用实际计时图来估计任何已知速度的预计计时。但是我会沿着这条线建立一个点表,在那里计时应该转到下一个速度

换句话说,如果100个记号来做所有重复的除法意味着500兆赫,200个记号意味着250兆赫,那么我会建立一个表,表示低于150的任何东西都是500兆赫,高于150的任何东西都是250兆赫。(假设这是该芯片供应商提供的仅有的两种速度)。这很好,因为即使电脑上的一些奇怪的软件使我的计时时间延迟,最终的结果通常仍然是死机

当然,现在,在超频、电源管理的动态时钟速度和其他此类欺骗手段盛行的时代,这样的方案将不太实用。至少在运行计时功能之前,您需要先做一些事情来确保CPU处于其动态选择的最高速度

好的,我现在就回去把孩子们赶出我的草坪。

在谷歌上快速搜索,显示CPUID应该可以让你访问CPU的最大频率。

“lmbench”提供了一种适用于不同架构的可移植CPU频率算法

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

当我们能够得到循环计数相对素数的循环时,这种方法应该总是有效的


在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;
}
#包括
#包括
#包括
uint64_t rdtsc(无效){
uint64_t结果;
__asm_________挥发性____(“rdtsc”):“=A”(结果);
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;
> 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
#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
CPU_freq = tsc_freq * (aperf_t1 - aperf_t0) / (mperf_t1 - mperf_t0)