除法转乘法算法

Linux内核中一般不使用除法运算,原因在于kernel搞个除法不太方便,应该是效率比较低,涉及到一系列的浮点运算

下面是在学习Linux内核中时间管理时见到的一个算法,特摘出来留个念想......


先贴算法:

void
clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 minsec)
{
	u64 tmp;
	u32 sft, sftacc= 32;

	/*
	 * Calculate the shift factor which is limiting the conversion
	 * range:
	 */
	tmp = ((u64)minsec * from) >> 32;
	while (tmp) {
		tmp >>=1;
		sftacc--;
	}

	/*
	 * Find the conversion shift/mult pair which has the best
	 * accuracy and fits the maxsec conversion range:
	 */
	for (sft = 32; sft > 0; sft--) {
		tmp = (u64) to << sft;
		do_div(tmp, from);
		if ((tmp >> sftacc) == 0)
			break;
	}
	*mult = tmp;
	*shift = sft;
}

精髓是这样的:

   将

  1. times_elapse = cycles_interval / frequency

  2. 转化为:
  1. times_elapse = cycles_interval * mult >> shift
因为这样的转化必定存在误差,所以需要一个精度来定义误差的范围


参数释义:

u32 *mult   <span style="white-space:pre">	</span>存放获取到的mult的值
u32 *shift<span style="white-space:pre">	</span><span style="font-family: Arial, Helvetica, sans-serif;">存放获取到的shift的值</span>
u32 from<span style="white-space:pre">	</span>frequency的值
u32 to<span style="white-space:pre">		</span>
u32 minsec

demo:

#include<stdio.h>
#include<stdlib.h>

typedef unsigned int u32;
typedef unsigned long long u64;

#define NSEC_PER_SEC 1000000000L

void
clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec)
{
    u64 tmp;
    u32 sft, sftacc= 32;

    /*
     * * Calculate the shift factor which is limiting the conversion
     * * range:
     * */
    tmp = ((u64)maxsec * from) >> 32;
    while (tmp) {
            tmp >>=1;
            sftacc--;
        }

    /*
     * * Find the conversion shift/mult pair which has the best
     * * accuracy and fits the maxsec conversion range:
     * */
    for (sft = 32; sft > 0; sft--) {
            tmp = (u64) to << sft;
            tmp += from / 2;
            //do_div(tmp, from);
            tmp = tmp/from;
            if ((tmp >> sftacc) == 0)
                break;
        }
    *mult = tmp;
    *shift = sft;
}


int main()
{ 

    u32 tsc_mult;
    u32 tsc_shift ;

    
    u32 tsc_frequency = 2127727000/1000; //TSC frequency(KHz)
    clocks_calc_mult_shift(&tsc_mult,&tsc_shift,tsc_frequency,NSEC_PER_SEC/1000,600*1000); //NSEC_PER_SEC/1000是因为TSC的注册是clocksource_register_khz

    fprintf(stderr,"mult = %d shift = %d\n",tsc_mult,tsc_shift);
    return 0;
}

600是根据TSC clocksource的MASK算出来的的入参,感兴趣可以自己推算看下结果:

  1. mult = 7885042 shift = 24
    root@manu:~/code/c/self/time# python
    Python 2.7.3 (default, Apr 10 2013, 05:46:21) 
    [GCC 4.6.3] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> (2127727000*7885042)>>24
  2. 1000000045L
    >>> 











  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值