知识点:有的场景,特别是可以乱序执行的场景,f64除法快于u32除法(至少对Rust和我的笔记本CPU(i7-8750H)来说,是这样的)
如果被除数和除数都不大,且除数固定,可以用神奇的方法进行优化以获得速度上的提升
事实上,编译器会针对固定的除数进行优化,但如果除数来自某个变量,编译期由于不知道变量的取值,无法进行进一步的优化(哪怕是帮你把u32转化成f64)。
学到一种神奇的优化算法。。
//如果除数b固定,可以优化除法:
a/b=a*(1/b)=a*(1<>t
于是如果已知a,b均小于21bit,我们可以预先处理b,比如用计算((4398046511104_u64+b-1)/b as u64)并将结果储存至c,
之后,对小于1<<21的a,可以用a*c>>42代替a/b
测试:
externcraterayon;userayon::prelude::*;fn main(){letb=std::time::Instant::now();letr1=(0u32..65536).into_par_iter().chunks(4096).map(|x|{x.iter().map(|&i|{(1u32..65536).into_iter().map(|j|{i/j}).sum::()asusize}).sum::()}).sum::();println!("{:?}",b.elapsed());letb=std::time::Instant::now();leta:Vec=(0..1).chain((1..2097152).map(|x|((4398046511104_u64+x-1)/xasu64)asusize)).collect();letr2=(0usize..65536).into_par_iter().chunks(4096).map(|x|{x.iter().map(|&i|{(1usize..65536).into_iter().map(|j|{i*a[j]>>42}).sum::()}).sum::()}).sum::();println!("{:?}",b.elapsed());letb=std::time::Instant::now();letr3=(0u32..65536).into_par_iter().chunks(4096).map(|x|{x.iter().map(|&i|{(1u32..65536).into_iter().map(|j|{(iasf64/jasf64)asu32}).sum::()asusize}).sum::()}).sum::();println!("{:?}",b.elapsed());println!("{}",r1+r2-r3<<1);}
输出
1.778011042s
859.130591ms
1.003195896s
0
可以看出,我们的优化算法快于将整数转化成f64算除法,快于直接计算整数除法。
完整测试(需要算很长时间)
extern crate rayon;
use rayon::prelude::*;
fn main(){
let a:Vec=(0..1).chain((1..2097152).map(|x|((4398046511104_u64+x-1)/x as u64) as usize)).collect();
(0usize..2097152).into_par_iter().chunks(4096).for_each(|x|{
x.iter().for_each(|&i|{
for j in 1usize..2097152{
if (i as f64/j as f64) as usize != i*a[j]>>42{
println!("{}/{}={} rather than {}",i,j,i/j,i*a[j]>>42)
}
}
});
println!("{}",x.last().unwrap())
})
}
计算并不会报错。