首先,吹逼可以不用打草稿,能干过v8我还在这个时候写代码?
正题
说明
环境是node,别的系统配置没意义 ,相对比对,实现一个pow函数,然后和v8的比下,顺带试试各种优化性能的江湖传说
第一个实现
function pow(x,e){
if(e==0)return 1;
else if(e==1)return x;
else if(e%2)return pow(x*x,(e-1)/2)*x;
else return pow(x*x,e/2);
};
很眼熟,基本和c里面差不多
然后本着递归不靠谱,先实现一个用栈模拟递归的数组实现
第二个实现
function pow(x,e){
var i = e;
var m=[];
var index =0 ;
if(e==0)return 1;
if(e==1)return x;
while(1){
//console.log(i);
if(i==1){
m.push(x);
break ;
}
else if(i==2){
m.push(x*x);
break;
}
else{
if(i%2){
m.push(x);
i=(i-1)/2 ;
}
else {
m.push(1);
i/=2;
}
}
};
for(i=(m.length-1);i>0;i--)
{
m[i-1]=m[i]*m[i]*m[i-1];
}
return m[0];
};
代码量一下子上来,然后测试,大概就是给个时间间隔,中间跑很多次函数结果:
native:70,code1:11000;code2:13000
嗯,这是个假数组实现,还不如递归,可见一般情况下,递归还是不错的选择,但是,如果再想下,思考点什么邪术,就可以干一波大事了
首先考虑一下,pow的指数值其实是局限的,或者说大了也没啥卵用,然后呢,这种二分对干的情况,,不由得想起了二进制,的确可以,细节略过,反正就是对比一下数组模拟,来个二进制模拟,这里用了es6的位操作(c里面很常见的套路),然后用了系统计算函数(罪过罪过,说不定就是靠这个函数优化的性能),然后有了下面版本
第三个实现
function pow(x,e){
var res =x;
if(e==0)return 1;
if(e==1)return x;
if(x==1)return 1;
var k = 32-Math.clz32(e)-1;
//console.log('fd'+res);
for(var i =0 ; i<k;i++)
{
if(e&(1<<(k-i-1))){
res=(res*res*x) ;}
else {
res*=res;
}
}
return res ;
};
效果是:native:90,code3:1000
真是飞跃。。。。
接着实验各种邪术江湖传闻,比较靠谱的是:
避免全局查找,将res等变量声明为全局,大概慢两倍或者更多
然后之前数组实现时,将push换为索引,没啥大的改变。
将内部var声明为let,大概也是慢两倍多,安全和效率会有冲突
而使用arguments来直接用实参比用形参访问慢(要计算)
然后有些不靠谱(可能我说的不对):
明确循环终止条件,while比for(;;)好什么的,,没啥用
异步,这个不考虑,没啥用
然后,发现了一个天大的秘密,用几个函数一层层套,居然执行更快了,,,,不过3层左右就可以了,套个9层还是会变慢
然后还有if..eles 比switch ..case好,可能IE会好,不过因此变成了个逼格不错的样子
第四个实现
function pow(x,e){
var res =x;
if(e===0)return 1;
if(e===1)return x;
if(x===1)return 1;
//获得e的二进制有效长
var k = 32-Math.clz32(e)-1;//-1为res有初始赋值
var i = 0 ;
for(var i =0 ; i<k;i++)
{
res=e&(1<<(k-i-1))?(res*res*x):(res*res);
}
return res ;
};
果然搞个这个条件选择就是感觉不一样了,但其实也没怎么快