如何以C编程方式查找CPU频率

我试图找出是否有任何想法来了解我的C代码正在运行的系统的CPU频率。

为了澄清,我正在寻找一个抽象的解决方案(一个不会与特定架构或操作系统相关联的解决方案),它可以让我了解我的代码正在执行的计算机的工作频率。 我不需要确切,但我想要在球场(即我有一个2.2GHz处理器,我希望能够在我的程序中告诉我,我在几百那个MHz)

有没有人有想法使用标准C代码?

您如何找到CPU频率取决于架构和操作系统,并且没有抽象的解决方案。

如果我们20多年前你使用没有上下文切换的操作系统并且CPU按顺序执行给出的指令,你可以在循环中编写一些C代码并计时,然后根据编译成的程序集计算运行时的指令数。 这已经假设每条指令需要1个时钟周期,这是自流水线处理器以来的一个相当差的假设。

但任何现代操作系统都会在多个进程之间切换 即使这样,您也可以尝试为一系列相同for循环运行计时(忽略页面错误所需的时间以及处理器可能停滞的多种其他原因)并获得中值。

即使以前的解决方案有效,您也可以使用多个问题处理器。 使用任何现代处理器,重新订购指令,在同一时钟周期内发布一堆指令,甚至在核心之间拆分它们都是公平的游戏。

有可能找到一个通用的解决方案,可以正确地获得一个线程或多个线程的工作频率。 这不需要管理员/ root权限或访问模型特定的寄存器。 我已经在Linux和Windows上对Intel处理器进行了测试,包括Nahalem,Ivy Bridge和Haswell,一个插槽最多四个插槽(40个线程)。 结果与正确答案的偏差均小于0.5%。 在我向您展示如何做之前,让我展示一下结果(来自GCC 4.9和MSVC2013):

 Linux: E5-1620 (Ivy Bridge) @ 3.60GHz 1 thread: 3.789, 4 threads: 3.689 GHz: (3.8-3.789)/3.8 = 0.3%, 3.7-3.689)/3.7 = 0.3% Windows: E5-1620 (Ivy Bridge) @ 3.60GHz 1 thread: 3.792, 4 threads: 3.692 GHz: (3.8-3.789)/3.8 = 0.2%, (3.7-3.689)/3.7 = 0.2% Linux: 4xE7-4850 (Nahalem) @ 2.00GHz 1 thread: 2.390, 40 threads: 2.125 GHz:, (2.4-2.390)/2.4 = 0.4%, (2.133-2.125)/2.133 = 0.4% Linux: i5-4250U (Haswell) CPU @ 1.30GHz 1 thread: within 0.5% of 2.6 GHz, 2 threads wthin 0.5% of 2.3 GHz Windows: 2xE5-2667 v2 (Ivy Bridge) @ 3.3 GHz 1 thread: 4.000 GHz, 16 threads: 3.601 GHz: (4.0-4.0)/4.0 = 0.0%, (3.6-3.601)/3.6 = 0.0% 

我从这个链接中得到了这个想法http://randomascii.wordpress.com/2013/08/06/defective-heat-sinks-causing-garbage-gaming/

要做到这一点,你首先要做的就是你20年前做的事情。 你用一个循环编写一些代码,你知道它的延迟和时间。 这是我用过的:

 static int inline SpinALot(int spinCount) { __m128 x = _mm_setzero_ps(); for(int i=0; i 

这具有承载循环依赖性,因此CPU无法对此进行重新排序以减少延迟。 每次迭代总是需要3个时钟周期。 操作系统不会将线程迁移到另一个核心,因为我们将绑定线程。

然后在每个物理核心上运行此function。 我用OpenMP做到了这一点。 必须为此绑定线程。 在使用GCC的linux中,您可以使用export OMP_PROC_BIND=true来绑定线程,并假设您有ncores物理核心也可以export OMP_NUM_THREADS=ncores 。 如果您想以编程方式绑定并查找英特尔处理器的物理内核数量,请参阅此编程检测数量的物理处理器核心或超级线程和线程关联性与Windows-msvc-和 - openmp 。

 void sample_frequency(const int nsamples, const int n, float *max, int nthreads) { *max = 0; volatile int x = 0; double min_time = DBL_MAX; #pragma omp parallel reduction(+:x) num_threads(nthreads) { double dtime, min_time_private = DBL_MAX; for(int i=0; i 

最后在循环中运行采样器并打印结果

 int main(void) { int ncores = getNumCores(); printf("num_threads %d, num_cores %d\n", omp_get_max_threads(), ncores); while(1) { float max1, median1, max2, median2; sample_frequency(1000, 1000000, &max2, &median2, ncores); sample_frequency(1000, 1000000, &max1, &median1,1); printf("1 thread: %.3f, %d threads: %.3f GHz\n" ,max1, ncores, max2); } } 

我没有在AMD处理器上测试过这个。 我认为带有模块的AMD处理器(例如Bulldozer)必须绑定到每个模块而不是每个AMD“核心”。 这可以通过GCC export GOMP_CPU_AFFINITY来完成。 您可以在https://bitbucket.org/zboson/frequency上找到一个完整的工作示例,它可以在英特尔处理器上的Windows和Linux上运行,并且可以正确地找到英特尔处理器的物理内核数量(至少自Nahalem以来)并将它们绑定到每个物理核心(不使用MSVC没有的OMP_PROC_BIND )。

为了完整起见,已经有一个简单,快速,准确的用户模式解决方案,具有巨大的缺点:它仅适用于Intel Skylake,Kabylake和更新的处理器。 确切的要求是CPUID级别16h支持。 根据英特尔软件开发人员手册325462第59版,第770页:

  • CPUID.16h.EAX =处理器基频(MHz);

  • CPUID.16h.EBX =最大频率(以MHz为单位);

  • CPUID.16h.ECX =总线(参考)频率(以MHz为单位)。

Visual Studio 2015示例代码:

 #include  #include  int main(void) { int cpuInfo[4] = { 0, 0, 0, 0 }; __cpuid(cpuInfo, 0); if (cpuInfo[0] >= 0x16) { __cpuid(cpuInfo, 0x16); //Example 1 //Intel Core i7-6700K Skylake-H/S Family 6 model 94 (506E3) //cpuInfo[0] = 0x00000FA0; //= 4000 MHz //cpuInfo[1] = 0x00001068; //= 4200 MHz //cpuInfo[2] = 0x00000064; //= 100 MHz //Example 2 //Intel Core m3-6Y30 Skylake-U/Y Family 6 model 78 (406E3) //cpuInfo[0] = 0x000005DC; //= 1500 MHz //cpuInfo[1] = 0x00000898; //= 2200 MHz //cpuInfo[2] = 0x00000064; //= 100 MHz //Example 3 //Intel Core i5-7200 Kabylake-U/Y Family 6 model 142 (806E9) //cpuInfo[0] = 0x00000A8C; //= 2700 MHz //cpuInfo[1] = 0x00000C1C; //= 3100 MHz //cpuInfo[2] = 0x00000064; //= 100 MHz printf("EAX: 0x%08x EBX: 0x%08x ECX: %08x\r\n", cpuInfo[0], cpuInfo[1], cpuInfo[2]); printf("Processor Base Frequency: %04d MHz\r\n", cpuInfo[0]); printf("Maximum Frequency: %04d MHz\r\n", cpuInfo[1]); printf("Bus (Reference) Frequency: %04d MHz\r\n", cpuInfo[2]); } else { printf("CPUID level 16h unsupported\r\n"); } return 0; } 

CPU频率与硬件相关,因此没有可用于获取它的通用方法,它还取决于您使用的操作系统。

例如,如果您使用的是Linux,则可以读取文件/ proc / cpuinfo ,也可以解析dmesg启动日志以获取此值,或者如果您希望可以看到Linux内核如何处理这些内容并尝试自定义代码满足您的需求:

https://github.com/torvalds/linux/blob/master/arch/x86/kernel/cpu/proc.c

问候。

我想从软件中获取时钟频率的一种方法是将硬件参考手册(HRM)的硬编码硬编码到软件中。 您可以从软件中读取时钟配置寄存器。 假设您知道源时钟频率,软件可以使用时钟寄存器中的乘法器和除数值,并应用HRM中提到的适当公式来推导时钟频率。