zoukankan      html  css  js  c++  java
  • BogoMIPS与calibrate_delay

         在分析Arm+linux启动信息的时候。发现有一个信息竟然耗费了2s的时间,这简直是不能忍受的。这个耗时大鳄是什么东西哪,请看分析信息:

    [    0.000000] console [ttyMT0] enabled

    [    2.057770] Calibrating delay loop... 1694.10 BogoMIPS (lpj=4235264)

    [    2.102188] pid_max: default: 32768 minimum: 301

    针对上述信息,有很多疑惑,一点一点来分析。

    1.何为BogoMIPS

    BogoMIPS (Bogo--Bogus--伪的,MIPS--millions of instruction per second) 按照字面的解释是“不太真实的MIPS”。之所以不太真实,那是因为其计算方法并不十分精确。BogoMIPS的值在系统系统时,在一闪而过的启动信息里可以看到;也可以dmesg看到;还可以通过查看/proc/cpuifo看到。BogoMIPS 的值是 linux 内核通过在一个时钟节拍里不断的执行循环指令而估算出来,它实际上反应了 CPU 的速度。

     

    2.怎么计算BogoMIPS

    “Calibrating delay loop... 1694.10 BogoMIPS”来自文件init/ calibrate.c中的函数calibrate_delay(),该函数主要作用根据不同的配置计算BogoMIPS的值。

    void __cpuinit calibrate_delay(void)
    {
        unsigned long lpj;
        static bool printed;
     
        if (preset_lpj) {
           lpj = preset_lpj;
           if (!printed)
               pr_info("Calibrating delay loop (skipped) "
                  "preset value.. ");
        } else if ((!printed) && lpj_fine) {
           lpj = lpj_fine;
           pr_info("Calibrating delay loop (skipped), "
               "value calculated using timer frequency.. ");
        } else if ((lpj = calibrate_delay_direct()) != 0) {
           if (!printed)
               pr_info("Calibrating delay using timer "
                  "specific routine.. ");
        } else {
           if (!printed)
              <strong> pr_info("Calibrating delay loop... ");
             </strong>lpj = calibrate_delay_converge();
        }
        if (!printed)
    <strong>       pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)
    ",
               lpj/(500000/HZ),
               (lpj/(5000/HZ)) % 100, lpj);</strong>
     
        loops_per_jiffy = lpj;
        printed = true;
    }


    这其中有两个全局变量需要分析,他们是preset_lpjlpj_fine。定义在文件init/calibrate.c中:

    unsigned long lpj_fine;

    unsigned long preset_lpj;

    在linux gcc言,unsigned long变量默认赋值为0。

        另外,printed表示信息仅仅打印一次。

     

    1. 若preset_lpj不为0

    preset_lpj初值为0。

    preset_lpj的赋值是有函数lpj_setup设置而来,该参数是有kernel bootloader设置而来。

    unsigned long preset_lpj;

    static int __init lpj_setup(char *str)

    {

        preset_lpj = simple_strtoul(str,NULL,0);

        return 1;

    }

     

    __setup("lpj=", lpj_setup);

    若preset_lpj不为0,表示lpj的值已经由用户预置,无需内核再行计算,直接赋值给lpj既可。

    2. 否则,若printed为0 且 lpj_fine不为0

    printed默认为0,只需观察lpj_fine的值既可以。

    lpj_fine很简单,如果其不为0,表示该变量是有timer来计算的,无需再行计算,赋值给lpj既可。

    3. 否则,若 calibrate_delay_direct()不等于0

       查找配置文件,可以发现很多时候ARCH_HAS_READ_CURRENT_TIMER配置项都是没有设置的,因为calibrate_delay_direct()会直接返回0。

    4. 其他

        需要分析calibrate_delay_converge(),该函数是主力函数。

    /*
     * This is the number of bits of precision for the loops_per_jiffy.  Each
     * time we refine our estimate after the first takes 1.5/HZ seconds, so try
     * to start with a good estimate.
     * For the boot cpu we can skip the delay calibration and assign it a value
     * calculated based on the timer frequency.
     * For the rest of the CPUs we cannot assume that the timer frequency is same as
     * the cpu frequency, hence do the calibration for those.
     */
    #define LPS_PREC 8
     
    static unsigned long __cpuinit calibrate_delay_converge(void)
    {
        /* First stage - slowly accelerate to find initial bounds */
        unsigned long lpj, lpj_base, ticks, loopadd, loopadd_base, chop_limit;
        int trials = 0, band = 0, trial_in_band = 0;
     
        lpj = (1<<12);/* 初始化为4096 */
        /* wait for "start of" clock tick */
       /* ticks保存当前jiffies的值。在while()中,只要ticks == jiffies,那么就一直执行空语句,也就是,只要时钟节拍还没更新则一直等待;注:系统用jiffies全局变量记录了从系统开始工作到现在为止,所经过的时钟节拍数 */
        ticks = jiffies;
        while (ticks == jiffies)
           ; /* nothing */
        /* Go .. */
       /* 估算一个时钟节拍内可执行的循环次数 */
        ticks = jiffies;
        do {
           if (++trial_in_band == (1<<band)) {
               ++band;
               trial_in_band = 0;
           }
           __delay(lpj * band);
           trials += band;
        } while (ticks == jiffies);
        /*
         * We overshot, so retreat to a clear underestimate. Then estimate
         * the largest likely undershoot. This defines our chop bounds.
         */
        trials -= band;
        loopadd_base = lpj * band;
        lpj_base = lpj * trials;
     
    recalibrate:
        lpj = lpj_base;/* lpj取估算值为初值,精确度大约为tick/2(若band=2) */
        loopadd = loopadd_base;
     
        /*
         * Do a binary approximation to get lpj set to
         * equal one clock (up to LPS_PREC bits)
         */
    /* 采用二分法的方式,无限靠近真值 */
        chop_limit = lpj >> LPS_PREC; /* 用于控制循环计算的次数 */
        while (loopadd > chop_limit) {
           lpj += loopadd;
           ticks = jiffies;
           while (ticks == jiffies)
               ; /* nothing */
           ticks = jiffies;
           __delay(lpj);
           if (jiffies != ticks)   /* longer than 1 tick */
               lpj -= loopadd;
           loopadd >>= 1;
        }
        /*
         * If we incremented every single time possible, presume we've
         * massively underestimated initially, and retry with a higher
         * start, and larger range. (Only seen on x86_64, due to SMIs)
         */
        /* 若每一次都是递增的(可能低估了lpj),则需要使用较大的初值和步幅 */
        if (lpj + loopadd * 2 == lpj_base + loopadd_base * 2) {
           lpj_base = lpj;
           loopadd_base <<= 2;
           goto recalibrate;
        }
     
        return lpj;
    }

    3.BogoMIPS的用途

    对于特定的CPU,BogoMips可用来查看它是否是个合适的值.它的时钟频率和它潜在的CPU缓存。但是它不可在不同的CPU间进行比较演示。

    请参考百度百科和wiki百科

    http://baike.baidu.com/view/1086713.htm

    http://zh.wikipedia.org/zh-cn/BogoMips

    Ok说了这么多,终于将该函数分析完了。


  • 相关阅读:
    「BZOJ 1000」A+B Problem
    「HNOI 2008」越狱
    蓝桥杯 拼音字母
    蓝桥杯 抽签
    蓝桥杯 快速排序
    [蓝桥杯] 最大比例
    [蓝桥杯] 交换瓶子
    [蓝桥杯] 四平方和
    [蓝桥杯] 剪邮票
    [蓝桥杯] 方格填数
  • 原文地址:https://www.cnblogs.com/youngerchina/p/5624439.html
Copyright © 2011-2022 走看看