BogoMIPS與calibrate_delay

     在分析Arm+linux啓動信息的時候。發現有一個信息居然耗費了2s的時間,這簡直是不能忍受的。這個耗時大鱷是什麼東西哪,請看分析信息:linux

[    0.000000] console [ttyMT0] enabled緩存

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

[    2.102188] pid_max: default: 32768 minimum: 301函數

針對上述信息,有不少疑惑,一點一點來分析。oop

 

1.何爲BogoMIPS

BogoMIPS (Bogo--Bogus--僞的,MIPS--millions of instruction per second) 按照字面的解釋是「不太真實的MIPS」。之因此不太真實,那是由於其計算方法並不十分精確。BogoMIPS的值在系統系統時,在一閃而過的啓動信息裏能夠看到;也能夠dmesg看到;還能夠經過查看/proc/cpuifo看到。BogoMIPS 的值是 linux 內核經過在一個時鐘節拍裏不斷的執行循環指令而估算出來,它實際上反應了 CPU 的速度。ui

 

2.怎麼計算BogoMIPS

「Calibrating delay loop... 1694.10 BogoMIPS」來自文件init/ calibrate.c中的函數calibrate_delay(),該函數主要做用根據不一樣的配置計算BogoMIPS的值。spa

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)\n",
           lpj/(500000/HZ),
           (lpj/(5000/HZ)) % 100, lpj);</strong>
 
    loops_per_jiffy = lpj;
    printed = true;
}


這其中有兩個全局變量須要分析,他們是preset_lpjlpj_fine。定義在文件init/calibrate.c中:rest

unsigned long lpj_fine;code

unsigned long preset_lpj;htm

在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說了這麼多,終於將該函數分析完了。

相關文章
相關標籤/搜索