这两个星期一直在研究rust,之前自己做的这个算法一个典型的生产者和消费者的题目和实现是用java写的,所以希望能用rust也实现了一遍,实际情况下比较一下两者有什么不一样。
下面是一些感受
1. rust快
同样的leader和follower都是500的话,理论上是要开1000个线程的。在我的外星人电脑上面,java要用到26秒,而rust2秒就跑完了。想象一下,本来要买10台服务器的,现在买一台就可以了。当然了,得准备一份优厚的待遇来招呼这个还不存在的rust攻城狮。
========
2015年6月4日注:这个是我之前写错了。在java的程序里加了sleep,把sleep去掉后,java需要5秒钟,不是26秒。不过毕竟rust刚出来,应该还有不少优化的空间。
2015年6月13日注:把java和rust的所有打印调试信息去掉之后,并编译为release版本,两者的运行时间都非常短。只有去到leader和follower都等于2000的时候,才看得到有运行时间,惊讶的是java和rust一样快,我之前的测试方法不够准确。
2015年9月2日注:今天仔细地做了测试对比,环境是外星人mx17(i7-4700MQ 2.4g + 8g 内存),每个数据都是采样10次取平均,下面是运行时间的对比
leader和follower的数量 实现方式 |
500条 | 1000条 | 2000条 |
Java(LinkedList+锁) | 878ms | 1278ms | 2547ms |
Java(LinkedBlockingQueue) | 1129ms | 1698ms | 2839ms |
rust | 931ms | 2401ms | 6708ms |
1. rust由于ownership机制,在实现中会比java不得不发送一次响应请求invitationresult到channel,所以性能上反而比java落后2倍以上。
2. rust的消息体invitation实现copy和clone与否,运行效率都差不多。
3. rust使用sync_channel代替channel后,性能下降较大。
java毕竟流行了十几年,jvm已经得到了很大的优化。rust作为新生的语言,应该还有很大的优化空间。
2. rust没有java的object.wait()方法
java的object有内置的wait和notify来进行线程间的通知和阻塞等待。而rust也有类似的,叫Convdar,但试用了一下,感觉不好用,换成用channel来做线程间发送数据,一样可以。
3. rust创建一个结构时,所有成员都要初始化,rust没有空指针
rust是没有类class的概念,只有结构struct,函数或方法function和接口trait。在例子里面,receiver在创建Follower和Leader的时候是未知的,但rust一定要我给个值,我如果换成是Option
4. rust的match模式匹配挺好用,经常用于错误值检查,比java的try catch来得优雅和高效,错误值返回也是我一直想要的功能
例如:
str => match str.parse::
Err(e) => { println!("{}",e);return;}
Ok(i)=> i
}
}
5. rust里,如果一个变量需要多线程同时访问,通常就定义成Arc
如下面加粗的两行
let mut followers = (0..follower_cnt).map(|i|
Arc::new(Mutex::new(Follower::new(i,dance_types.len() as i32,leader_cnt)))
).collect::
......
for follower in &followers {
let follower = follower.clone();
thread::spawn(move || {
let mut follower = follower.lock().unwrap();
follower.run();
});
}
6. rust的语法比java要复杂得多,代码会相对整洁。
如果学习java的难度系数是1,那么rust可以说是10,我用java第一次写这个算法的时候,包括构思只用了3个小时,但是我用rust,即使重写的话,用了整整1天,我认为语法难和难以理解的编译错误将会阻碍rust将来普及的最重要因素。当然rust看上去也更简洁易懂。代码量的话两者差不多,都是330行左右。不过如果弄懂了iteration,Arc,Mutex和Borrow,OwnerShip的概念后,就会写得更快了,而且目前还是在没有完善IDE的情况下。
7. rust有自带的库依赖和更新机制
rust的cargo比java的maven,结合得更加紧密,有了这个东西,不用在网上大海捞针般的查找各种依赖库和处理跟它们版本有关的依赖关系了。
8. 100%的线程间共享数据安全
rust的borrow和owership机制,除了能避免内存泄露,也是线程间访问数据的依据。通过Arc,Mutex和Channel,保证同时只有一个线程能拥有一个对象的写权限,同时当写被借出时,是没有办法借出读的。这全都是通过编译器在编译时就确定下来的。只要代码编译通过,那么程序就是并发安全的。
9. 跟C++对比100%没有内存泄露
我刚毕业时在华为也干过1年的c++,哎,又想起那段岁月。好了,c++最让人恼火的是什么?内存泄露,即使有1%的可能泄露也是泄露。都知道99.99%跟100%是有很大差距的。就让我想起一个笑话。一个同学挑战学霸:你看我这次考试考了95分,你考了100分,我跟你也只是5分的差距啊。学霸淡然一笑,说道:你考95分,是因为你只能考95分,我考100分,是因为卷面只有100分。
10. rust的缺点
1. borrow和ownership的编译错误将会使得在实际编码中非常恼火(即使像我脾气这么好的),并不得不修改在其他语言中是没问题的实现方法。
2. 编译速度偏慢,要知道程序员一天要编译多少次代码来验证改动啊,Golang这么流行,一个原因就是编译超快,的确省了不少时间,像php,phython这种更不用说了,这么渣的性能都这么火,就是因为不用编译,部署方便。这里关于c++的编译速度又有一个笑话,说google的工程师开始编译他们的服务器后,会出去转个圈,喝个咖啡,到处找人聊技术聊人生,聊累了再回到座位上睡上一觉,醒了后看一看电脑,运气好的话,应该能编译有一半了。
3. 后台不够硬导致rust不一定能流行开来,不能流行开来就意味着没有大量的技术人员和代码库。看看java有干爹Sun,Golang有google,swift有ObjectC,连COBOL都有IBM。 Mozilla显然被这几个大款比了下去。rust现在连IDE都没有资源去开发。都得靠各种工具插件凑起来弄一套才有个很基本的IDE。
总结:
rust超快的接近c++的运行速度,没有GC也没有内存泄露,安全的并发,和简洁的代码风格会是吸引我的几大特点,毕竟rust目标是替代C++的。只要代码能编译通过,都是没有问题的,适合对写程序有洁癖的人。随着对它越来越熟悉,你会慢慢爱上它。
代码:
extern crate rand;
//extern crate time;
use std::io;
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc::{Sender,Receiver,channel};
use std::fmt;
use rand::distributions::{IndependentSample, Range};
struct DanceType {
id : i32,
name : String
}
impl fmt::Display for DanceType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "", self.id,self.name));
Ok(())
}
}
struct Leader{
id :i32,
dance_confirmed: Vec,
senders: Vec>>>,
receiver: Receiver
}
impl fmt::Display for Leader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "", self.id));
Ok(())
}
}
impl Leader {
fn new(id :i32,dance_cnt: i32) -> Leader{
let (_,rx) = channel();
let mut leader = Leader{id:id,
dance_confirmed: Vec::::new() ,
senders: Vec::>>>::new(),
receiver: rx
};
for _ in 0..dance_cnt {
leader.dance_confirmed.push(-1);
}
leader
}
fn run(&mut self ){
println!("{} is ready", self);
let mut rng = rand::thread_rng();
//随机选取dance_type
let mut dances = (0i32..self.dance_confirmed.len() as i32).map(move |x| x).collect::>();
let between = Range::new(0 as usize, dances.len());
for i in (0..dances.len()){
let a = between.ind_sample(&mut rng);
let tmp = dances[i];
dances[i] = dances[a as usize];
dances[a as usize] = tmp;
}
for dance_type_id in &dances {
//随机选取follower
let mut senders = (0..self.senders.len() as i32).collect::>();
let between2 = Range::new(0 as usize, senders.len());
for i in (0..senders.len()){
let a = between2.ind_sample(&mut rng);
let tmp = senders[i];
senders[i] = senders[a as usize];
senders[a as usize] = tmp;
}
for follower_id in &senders{
//发送邀请
let inv = Invitation{leader_id: self.id,
follower_id: *follower_id,
dance_type_id: *dance_type_id
} ;
println!("{}发送邀请{}...",self,inv);
match self.senders[*follower_id as usize].lock().unwrap().send(inv){
Err(e) => { println!("{}发送邀请失败,原因:{}",self,e);continue;}
_ => {}
}
//接受并处理结果
let res = match self.receiver.recv(){
Err(e) => { println!("{}接收回应失败,原因:{}",self,e);continue;}
Ok(res) => res
};
match res {
InviResult::Init => println!("{}等待超时{}", self,inv),
InviResult::Accept => {
self.dance_confirmed[inv.dance_type_id as usize] = inv.follower_id;
println!("{}收到了接受的回应{}====", self, inv);
break;
}
InviResult::Reject => println!("{}收到了拒绝邀请的回应{}====", self, inv)
}
}
}
}
}
struct Follower{
id :i32,
dance_confirmed: Vec,
leader_dance: Vec,
senders: Vec>>>,
receiver: Receiver,
finish: bool
}
impl fmt::Display for Follower {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "", self.id));
Ok(())
}
}
impl Follower{
fn new(id :i32,dance_cnt :i32,leader_cnt :i32 ) -> Follower{
let (_,rx) = channel();
let mut follower = Follower{id:id,
dance_confirmed: Vec::::new(),
leader_dance: Vec::::new(),
senders: Vec::>>>::new(),
receiver: rx,
finish: false
};
for _ in 0..dance_cnt {
follower.dance_confirmed.push(-1);
}
for _ in 0..leader_cnt {
follower.leader_dance.push(0);
}
follower
}
fn reply(&self, inv :&Invitation, res :&mut InviResult){
if self.dance_confirmed[inv.dance_type_id as usize] >=0 {
*res = InviResult::Reject;
println!("{}拒绝邀请,因为已和{}参与过舞蹈{}",self,self.dance_confirmed[inv.dance_type_id as usize],inv.dance_type_id);
}
else if self.leader_dance[inv.leader_id as usize] >= 2 {
*res = InviResult::Reject;
println!("{}拒绝邀请,因为已接受过{}的{}次邀请",self,inv.leader_id,2);
}
else{
*res = InviResult::Accept;
println!("{}接受邀请{}",self, *inv);
}
}
fn run(&mut self){
self.finish = false;
println!("{} is ready",self);
while !self.finish {
let inv = match self.receiver.recv() {
Err(e) =>{println!("{}接收时发生错误,原因:{}",self,e);continue;}
Ok(inv) => {
match inv.leader_id{
-1 => {self.finish=true;continue;}
_ => inv
}
}
};
println!("{}收到邀请{}",self,inv);
let mut res = InviResult::Init;
self.reply(& inv,&mut res);
if res == InviResult::Accept {
self.dance_confirmed[inv.dance_type_id as usize] = inv.leader_id;
self.leader_dance[inv.leader_id as usize] +=1;
}
match self.senders[inv.leader_id as usize].lock().unwrap().send(res){
Err(e) => { println!("{}发送回应失败,原因:{}",self,e);continue;}
_ => {}
}
}
}
}
//impl <'a > Follower<'a > {
// fn new(id : i32, danceParty: & DanceParty) -> &'a Follower<'a >{
// &'a Follower{id:id,danceParty:danceParty,dance_confirmed:dance_confirmed, }
// }
//}
#[derive(Debug,PartialEq)]
enum InviResult{
Init,
Reject,
Accept
}
#[derive(Debug,Clone,Copy)]
struct Invitation{
leader_id : i32,
follower_id : i32,
dance_type_id: i32
}
impl fmt::Display for Invitation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "", self.leader_id, self.follower_id, self.dance_type_id));
Ok(())
}
}
fn main() {
let dance_types = [
DanceType{id:0,name:"Waltz".to_string()},
DanceType{id:1,name:"Tango".to_string()},
DanceType{id:2,name:"Foxtrot".to_string()},
DanceType{id:3,name:"Quickstep".to_string()},
DanceType{id:4,name:"Rumba".to_string()},
DanceType{id:5,name:"Samba".to_string()},
DanceType{id:6,name:"ChaCha".to_string()},
DanceType{id:7,name:"Jive".to_string()}
];
let leader_cnt : i32 = {
let mut leader_cnt_input = String::new();
println!("Please input the leader number(type 'q' to quit):");
match io::stdin().read_line(&mut leader_cnt_input) {
Err(e) => { println!("输入有误{}",e);return;}
Ok(_)=> {
match leader_cnt_input.trim(){
"q" => return,
str => match str.parse::(){
Err(e) => { println!("{}",e);return;}
Ok(i)=> i
}
}
}
}
};
let follower_cnt : i32 = {
let mut follower_cnt_input = String::new();
println!("Please input the follower number(type 'q' to quit):");
match io::stdin().read_line(&mut follower_cnt_input) {
Err(e) => { println!("输入有误{}",e);return;}
Ok(_)=> {
match follower_cnt_input.trim(){
"q" => return,
str => match str.parse::(){
Err(e) => { println!("{}",e);return;}
Ok(i)=> i
}
}
}
}
};
let followers = (0..follower_cnt).map(|i|
Arc::new(Mutex::new(Follower::new(i,dance_types.len() as i32,leader_cnt)))
).collect::>();
let leaders = (0..leader_cnt).map(|i|
Arc::new(Mutex::new(Leader::new(i,dance_types.len() as i32)))
).collect::>();
for follower in &followers {
let (tx,rx) = channel();
follower.lock().unwrap().receiver = rx;
let sender = Arc::new(Mutex::new(tx));
for leader in &leaders {
leader.lock().unwrap().senders.push(sender.clone());
}
}
for leader in &leaders {
let (tx,rx) = channel();
leader.lock().unwrap().receiver = rx;
let sender = Arc::new(Mutex::new(tx));
for follower in &followers {
follower.lock().unwrap().senders.push(sender.clone());
}
}
let follower_handlers = followers.iter().map(|follower| {
let follower = follower.clone();
thread::spawn(move || {
let mut follower = follower.lock().unwrap();
follower.run();
})
}
).collect::>();
let leader_handlers = leaders.iter().map(|leader| {
let leader = leader.clone();
thread::spawn(move || {
let mut leader = leader.lock().unwrap();
leader.run();
})
}
).collect::>();
for handler in leader_handlers {
match handler.join(){
Err(e) => println!("线程结束时出错,原因:{:?}",e),
Ok(_) => {}
}
}
println!("leader已全部结束!");
if followers.len() > 0 && leaders.len() > 0 {
let leader = leaders[0].clone();
let leader = leader.lock().unwrap();
for sender in &leader.senders {
match sender.lock().unwrap().send(Invitation{leader_id :-1,follower_id :-1, dance_type_id :-1}){
Err(e) => {println!("发送空邀请时错误,原因:{}",e);}
_ => {}
}
}
}
for handler in follower_handlers {
match handler.join(){
Err(e) => println!("线程结束时出错,原因:{:?}",e),
Ok(_) => {}
}
}
println!("follower已全部结束!");
for i in (0..leader_cnt) {
let leader = leaders[i as usize].lock().unwrap();
println!("Leader :{:?}" ,i);
for j in (0..leader.dance_confirmed.len()) {
let follower_id = leader.dance_confirmed[j];
if follower_id < 0 {
println!("{:35} with --", dance_types[j]);
}
else {
println!("{:35} with {:?}", dance_types[j], follower_id);
}
}
println!("");
}
}
最后别忘了在cargo.toml加上下面几行
[dependencies]
num = "*"
rand = "*"