为什么我的CPU突然以两倍的速度工作?

时间:2014-11-21 21:40:49

标签: c performance cpu-architecture

我一直在尝试使用简单的分析器来衡量学校服务器上某些C代码的效率,而且我遇到了奇怪的情况。在很短的时间(半秒)之后,处理器突然开始执行指令两倍的速度。我已经测试了几乎我能想到的每一个可能的原因(缓存,核心负载平衡,CPU频率因睡眠状态而改变),但一切看起来都很正常。

为了它的价值,我在学校的Linux服务器上进行了这项测试,所以我可能不知道有什么不寻常的配置,但是正在使用的处理器ID没有改变,并且(通过顶部)服务器在我测试时完全空闲。

测试代码:

#include <time.h>
#include <stdio.h>

#define MY_CLOCK CLOCK_MONOTONIC_RAW
// no difference if set to CLOCK_THREAD_CPUTIME_ID

typedef struct {
        unsigned int tsc;
        unsigned int proc;
} ans_t;

static ans_t rdtscp(void){
        ans_t ans;
        __asm__ __volatile__ ("rdtscp" : "=a"(ans.tsc), "=c"(ans.proc) : : "edx");
        return ans;
}

static void nop(void){
        __asm__ __volatile__ ("");
}

void test(){
        for(int i=0; i<100000000; i++) nop();
}

int main(){
        int c=10;
        while(c-->0){
                struct timespec tstart,tend;
                ans_t start = rdtscp();
                clock_gettime(MY_CLOCK,&tstart);
                test();
                ans_t end = rdtscp();
                clock_gettime(MY_CLOCK,&tend);
                unsigned int tdiff = (tend.tv_sec-tstart.tv_sec)*1000000000+tend.tv_nsec-tstart.tv_nsec;
                unsigned int cdiff = end.tsc-start.tsc;
                printf("%u cycles and %u ns (%lf GHz) start proc %u end proc %u\n",cdiff,tdiff,(double)cdiff/tdiff,start.proc,end.proc);
        }
}

输出我看到了:

351038093 cycles and 125680883 ns (2.793091 GHz) start proc 14 end proc 14
350911246 cycles and 125639359 ns (2.793004 GHz) start proc 14 end proc 14
350959546 cycles and 125656776 ns (2.793001 GHz) start proc 14 end proc 14
351533280 cycles and 125862608 ns (2.792992 GHz) start proc 14 end proc 14
350903833 cycles and 125636787 ns (2.793002 GHz) start proc 14 end proc 14
350924336 cycles and 125644157 ns (2.793002 GHz) start proc 14 end proc 14
349827908 cycles and 125251782 ns (2.792997 GHz) start proc 14 end proc 14
175289886 cycles and 62760404 ns (2.793001 GHz) start proc 14 end proc 14
175283424 cycles and 62758093 ns (2.793001 GHz) start proc 14 end proc 14
175267026 cycles and 62752232 ns (2.793001 GHz) start proc 14 end proc 14

我使用不同的优化级别(-O0到-O3)得到类似的输出(使用不同数量的测试来提高效率)。

它是否可能与超线程有关,其中物理核心中的两个逻辑核心(服务器正在使用可能具有此效果的Xeon X5560)可以某种方式&#34;合并&#34;形成一个两倍快的处理器?

3 个答案:

答案 0 :(得分:1)

某些系统会根据系统负载调整处理器速度。正如您所指出的那样,在进行基准测试时尤其令人讨厌。

如果您的服务器运行的是Linux,请输入

cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor 

如果这会输出ondemandpowersaveuserspace,那么CPU频率调整就会生效,您将发现很难做基准测试。如果这表示performance,则禁用CPU频率缩放。

答案 1 :(得分:0)

有些CPU在芯片上有优化,它们正在学习代码通常采用的路径。通过成功预测下一个if语句的作用,不需要丢弃队列,并从头开始新加载所有新操作。根据芯片和算法的不同,可能需要5到10个周期,直到成功预测if语句。但不知何故,还有一些理由认为这是因为这种行为的原因。

查看您的输出我会说这也可能只是操作系统的调度和/或那里使用的CPU频率调控器。您是否确定在执行代码期间CPU频率不会发生变化?没有CPU提升? 使用像cpufreq这样的linux工具通常用来调节cpu频率。

答案 2 :(得分:-1)

超线程意味着复制寄存器空间,而不是实际的解码/执行单元 - 所以这不是解决方案。

为了测试微基准方法的准确性,我将执行以下操作:

  1. 以高优先级运行程序
  2. 计算指令数量以确定其是否正确。我会使用 perf stat ./binary 来做到这一点 - 这意味着你需要有穿孔。我会这样做多次并查看时钟和指令指标,看看多个指令如何在一个周期内执行。
  3. 我有一些附加说明

    对于每个 nop,你还要在for循环中进行比较和条件跳转。如果你真的想要执行NOP,我会写一个这样的语句:

    #define NOP5 __asm__ __volatile__ ("nop nop nop nop nop");
    #define NOP25 NOP5 NOP5 NOP5 NOP5 NOP5
    #define NOP100 NOP25 NOP25 NOP25 NOP25
    #define NOP500 NOP100 NOP100 NOP100 NOP100 NOP100
    ...
    for(int i=0; i<100000000; i++)
    {
       NOP500 NOP500 NOP500 NOP500
    }
    

    此构造允许您实际执行NOP的,而不是将i与100M 进行比较。