calibrate_delay() 函数用来计算 BogoMIPS 的值。函数代码定义在 init/calibrate.c 中,如下所示:/*
* This is the number of bits of precision for the loops_per_jiffy. Each
* bit takes on average 1.5/HZ seconds. This (like the original) is a little
* better than 1%
*/
#define LPS_PREC 8
void __devinit calibrate_delay(void)
{
unsigned long ticks, loopbit;
int lps_precision = LPS_PREC;
if (preset_lpj) {
loops_per_jiffy = preset_lpj;
printk("Calibrating delay loop (skipped)... "
"%lu.%02lu BogoMIPS preset\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100);
} else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) {
printk("Calibrating delay using timer specific routine.. ");
printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100,
loops_per_jiffy);
} else {
loops_per_jiffy = (1<<12);
printk(KERN_DEBUG "Calibrating delay loop... ");
while ((loops_per_jiffy <<= 1) != 0) {
/* wait for "start of" clock tick */
ticks = jiffies;
while (ticks == jiffies)
/* nothing */;
/* Go .. */
ticks = jiffies;
__delay(loops_per_jiffy);
ticks = jiffies - ticks;
if (ticks)
break;
}
/*
* Do a binary approximation to get loops_per_jiffy set to
* equal one clock (up to lps_precision bits)
*/
loops_per_jiffy >>= 1;
loopbit = loops_per_jiffy;
while (lps_precision-- && (loopbit >>= 1)) {
loops_per_jiffy |= loopbit;
ticks = jiffies;
while (ticks == jiffies)
/* nothing */;
ticks = jiffies;
__delay(loops_per_jiffy);
if (jiffies != ticks) /* longer than 1 tick */
loops_per_jiffy &= ~loopbit;
}
/* Round the value and print it */
printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100,
loops_per_jiffy);
}
} 在 printk(KERN_DEBUG "Calibrating delay loop... "); 语句上面部分代码不需要关注,真正计算部分为下边的代码。现在来看看该函数时如何算出 BogoMIPS 值的。
loops_per_jiffy 变量顾名思义是每个 jiffy 中多少个 loops 循环 (关于 jiffy 概念可参考:http://www.groad.net/bbs/read.php?tid-3056.html)。
[Plain Text] 纯文本查看 复制代码 loops_per_jiffy = (1<<12);
右移 12 位后, loops_per_jiffy 的值为 4096 。
[Plain Text] 纯文本查看 复制代码 while ((loops_per_jiffy <<= 1) != 0)
在 while 循环里 loops_per_jiffy 的值 x 2
[Plain Text] 纯文本查看 复制代码 [mw_shl_code=text,true]ticks = jiffies;
while (ticks == jiffies)
/* nothing */;
ticks 变量用来保存当前的 jiffies 值。在底下的 while() 里,只要 ticks == jiffies ,那么就一直执行空语句(;)。换句话就是说,时钟节拍还没更新就一直执行空语句等待。
[Plain Text] 纯文本查看 复制代码 ticks = jiffies;
__delay(loops_per_jiffy);
OK,时钟节拍更新了(等时钟节拍更新是因为希望我们的计算有一个新的开始),那么我们开始执行 __delay() 函数(__delay() 函数不断对传进的 loops_per_jiffy 做减 1 运算,它是一小段汇编程序,具体分析参考:http://www.groad.net/bbs/read.php?tid-3052.html)。
[Plain Text] 纯文本查看 复制代码 ticks = jiffies - ticks;
if (ticks)
break;
当一个 __delay() 完毕后再检查一下 jiffies 值是否更新,如果相减的结果不为零,则表示更新了,这时就会跳出这个循环。如果没有更新,loops_per_jiffy 再乘以 2 从而加大 __delay() 的延迟时间。也就是说,通过不断的加大 __delay() 的时间而达到一个时钟节拍时间的粗略估算。
现在假设上面的循环已经推出,开始执行下面的语句。
[Plain Text] 纯文本查看 复制代码 loops_per_jiffy >>= 1;
这里 loops_per_jiffy 右移一位除以 2 的目的是修正上面的估算。例如,我们假设一个时钟节拍所经过的时间为 t ,一个基本的 __delay() (对 4096 进行减 1 延迟所需要的时间)为 T1。再假设 t 的范围落在 2T1 < t < 4T1 。这里,我们会选择 2T1 ,目的是以 2T1 这个时间作为新的起点,然后根据下面的算法逐步的逼近 t 。
[Plain Text] 纯文本查看 复制代码 loopbit = loops_per_jiffy;
将 得到的 "新起点" loops_per_jiffy 保存到 loopbit 中。
[Plain Text] 纯文本查看 复制代码
while (lps_precision-- && (loopbit >>= 1)) {
loops_per_jiffy |= loopbit;
从上面知道,lps_precision 的初始值为 8 ,这个值是一个精度。我们就是利用这个精度,从而不断的逼近 t 。为了更清晰,逼近值见下表(新起点就设为 2T1,也就是 4096*2=8192,即二进制 10000000000000) :
10000000000000 | 11000000000000 | 10100000000000 | 10010000000000 | 10001000000000 | 10000100000000 | 10000010000000 | 10000001000000 | 上面就是一个逼近的规律了。每一次的逼近,就是 loopbit >>=1; ,然后 loops_per_jiff |= loopbit; 。
[Plain Text] 纯文本查看 复制代码 ticks = jiffies;
while (ticks == jiffies)
/* nothing */;
ticks = jiffies;
__delay(loops_per_jiffy);
这一段代码和上面分析的一样。
[Plain Text] 纯文本查看 复制代码 if (jiffies != ticks) /* longer than 1 tick */
loops_per_jiffy &= ~loopbit;
如果发现 jiffies 不等于 ticks 了,那就说明 jiffies 已经加 1,也就是说我们之前的 loops_per_jiffy 的值(上表中的一个)设大了。这不要紧,我们继续将其减小,然后再重复去测试。如果说,精度 8 已经减完了( lps_precision--) 还是达不到要求,那么我们也只好认为最低的精度(比如是 10000001000000 )就是所需要的值了。
从上面的分析我们可以发现,这样的计算方法并不是很精确的,但是至少可以让我们大概明了我们的 CPU 快到了什么样的程度。
[Plain Text] 纯文本查看 复制代码 printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100,
loops_per_jiffy);
计算 MogoMIPS 的方法:
BogoMIPS = loops_per_jiffy * HZ * 循环延迟所消耗指令
循环延迟是指 __delay() 这个循环,它的基本延迟就是 decl 和 jns 这两条指令。所以根据上面的公式有:loops_per_jiffy * HZ * 2 / 1000000 底下除数 1000000 就是 MIPS ,表示 100W 条指令,所以又得到: loops_per_jiffy/(500000/HZ) |