如何解决Java几乎是Rust的两倍?我想念什么?
我移植了一个Java类,该类遍历从n个选择中选择k个元素到Rust的所有可能的无序组合,期望Rust可以帮助我加快计算速度。但是当同时运行两个程序时,事实证明Java快了将近两倍!
由于这听起来根本不对劲,而且由于我刚开始使用Rust,所以我一定做错了事,并且想知道是否有更多Rust经验的人会帮助我弄清楚为什么我的Rust代码如此慢得多。
这是我的通用接口,实现和测试代码的Java代码:
public interface Choice<Type> {
/**
* Returns the number of possible options that this choice provides.
*
* @return the number of choices
*/
public long getChoices();
/**
* Returns the choice with the given index.
*
* @param index - the index of the choice to return
* @return the choice of the given index
*/
public Type getChoice(long index);
}
public class NChooseK implements Choice<int[]> {
private final int n;
private final int k;
private final long count;
public NChooseK(int n,int k) {
if ((n<=0) || (k<0) || (k>n)) throw new IllegalArgumentException();
this.n = n;
this.k = k;
this.count = choose(n,k);
}
@Override
public long getChoices() {
return count;
}
@Override
public int[] getChoice(long m) {
if (k==0) return new int[0];
long count = this.count;
int[] result = new int[this.k];
int n = this.n;
int k = this.k;
long x = (count-1) - m;
while (true) {
if (n == k) {
while (true) {
result[this.k - k] = this.n - k;
if (k==1) return result;
k--;
}
}
count = count * (n-k) / n;
if (x >= count) {
result[this.k - k] = this.n - n;
if (k==1) return result;
x -= count;
count = count * k / (n-k);
k--;
}
n--;
}
}
private long choose(int n,int k) {
if (n<k) return 0;
if (k>n-k) k=n-k;
long b=1;
for (int i=1,m=n; i<=k; i++,m--)
b = b*m/i;
return b;
}
}
public class Test {
public static void main(String[] args) {
NChooseK nck = new NChooseK(26,13);
long choices = nck.getChoices();
System.out.println("Running ("+choices+" choices)...");
long start = System.currentTimeMillis();
for (long index = 0; index<choices; index++) {
int[] choice = nck.getChoice(index);
//System.out.println(ArrayTools.toString(choice));
}
long end = System.currentTimeMillis();
System.out.println("Done ("+((end - start)/1000.0)+"s)!");
}
}
这就是我认为与Rust最接近的翻译:
pub trait Choice<Type> {
/// Returns the number of possibilities for this choice.
fn get_choices(&self) -> u32;
/// Returns the posibility of the given index.
fn get_choice(&self,index: u32) -> Type;
}
use super::choice::Choice;
pub struct NChooseK {
n: u32,k: u32,count: u32,}
impl NChooseK {
pub fn new(n: u32,k: u32) -> Result<NChooseK,&'static str> {
if k > n {
Err("invalid parameters: k cannot be larger than n")
} else {
Ok(NChooseK {
n: n,k: k,count: choose(n,k).unwrap() as u32,})
}
}
}
impl<'a> Choice<Vec<u32>> for NChooseK {
fn get_choices(&self) -> u32 {
self.count
}
fn get_choice(&self,m: u32) -> Vec<u32> {
if self.k == 0 {
return vec![];
}
let mut count = self.count;
let mut result:Vec<u32> = Vec::with_capacity(self.k as usize);
let mut n = self.n;
let mut k = self.k;
let mut x = (count-1) - m;
loop {
if n == k {
loop {
result.push(self.n - k);
if k == 1 {
return result;
}
k -= 1;
}
}
count = count * (n - k) / n;
if x >= count {
result.push(self.n - n);
if k == 1 {
return result;
}
x -= count;
count = count * k / (n - k);
k -= 1;
}
n -= 1;
}
}
}
fn choose(n: u32,mut k: u32) -> Option<u64> {
if k > n-k {
k = n-k;
}
let mut b : u64 = 1;
let mut m = n;
for i in 1..=k {
if b > 0xFFFFFFFFFFFFFFFF / (m as u64) {
return None;
}
b = b * (m as u64) / (i as u64);
m -= 1;
}
Some(b)
}
fn main() {
let nck = NChooseK::new(26,13).unwrap();
let choices = nck.get_choices();
println!("Running ({} choices)...",choices);
let start = time::precise_time_s();
for index in 0..choices {
let choice = nck.get_choice(index);
//println!("{:?}",choice);
}
let end = time::precise_time_s();
println!("Done ({}s)!",end - start);
}
Rust代码大约需要12到12.5秒才能完成对get_choice的约1000万次调用,而Java代码则需要6.5到7秒! WTF?!?!?
这在Windows 7 64位系统上使用rustc v1.45.2和OpenJDK v1.8.0_212-3-redhat。
注意:
- 最初,我有get_choices还在Rust中返回u64,但将其更改为u32,以尝试消除尽可能多的类型转换(尽管实际上并没有什么不同)。
- 我还尝试用i32替换所有u32。也没什么区别。
- 注释
result.push(...)
语句可将运行时间降至9至9.5s。这是一个很大的差异(比我预期的要大!),但仍然比Java慢! - 如果我取消注释内部循环中的print语句并将参数更改为更合理的值(例如,
NChooseK(7,3)
),则两个版本的确产生相同的准确输出值(ArrayTools.toString(...)
只是一个简单的辅助函数,将整数数组的组成部分用逗号连接成一个字符串;由于这里已经有太多代码,因此决定将其省略)
解决方法
在尝试压缩性能时,始终使用long
或cargo build --release
来使cargo run --release
/ rustc
优化代码:
llvm
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。