码迷,mamicode.com
首页 > 其他好文 > 详细

Rust中文翻译10

时间:2015-07-03 16:02:57      阅读:111      评论:0      收藏:0      [点我收藏+]

标签:rust   编程语言   

我们的第三个工程,会秀一下Rust语言的其中一个最棒的优点:没有实际上的运行时环境.
随着组织的增加,他们依赖于等多的编程语言.不同的编程语言有各自的有点和缺点,一个全语言栈的可以使你使用一种语言的优点的同时,用另一种语言来代替它的缺点.

许多编程语言的一个通病就是运行时环境下的性能很差.通常来说,使用一种慢速的语言,代价换来的是生产力的提高.为了缓和这个问题,有一种方法是使用C语言来编写你的系统,然后调用这些C语言代码就像它是由高层次语言写的.这被称为"外部函数接口(foreign function interface)",缩写为FFI.

Rust在两个方向上都支持FFI:它可以方便的调用C语言,并且更为关键的是,他可以像C语言一样被方便的调用.Rust语言没有垃圾回收机制,同时运行时消耗又很小,这使得Rust成为了被嵌入其他语言中的完美候选人.

本章专门讲述FFI,它的一些特性全书中也都有描述,但是在这里,我们使用3个例子来说明FFI特性:Ruby,Python和JavaScript.

3.3.1 问题
我们本来有很多可以选择的例子,但是我们选择了一个Rust语言相较于其他语言的一个明显的优势领域:数值计算和线程.

Page 56

许多编程语言为了一致性的考虑,将数值存放在堆上,而不是栈上.尤其是那些面向对象的语言并且使用垃圾回收的语言,堆是他们的默认选择.有时候会有优化,将特定的数值放在栈上,但是这是基于一个优化器来完成的,那就是我们需要保证我们使用的是基本数值类型而不是对象类型.

第二,许多语言都有一个"全局解释器锁(global interpreter lock)",这会限制多线程的运行.好的方面看这样更安全,但是坏的方面就是本来可以多线程的任务无法多线程执行.

为了强调这两个问题,我们来创建一个重度使用这两个问题的小程序.因为我们关注的焦点是将Rust语言嵌入其他语言中,而不是问题本身,我们使用一个玩具例子:

     启动10个线程,其中,每个线程从1数到500万.当是个线程结束的时候打印"done!".

我在我的电脑上选择500万.下面是Ruby的例子:

threads = []
10.times do
  threads << Thread.new do
    count = 0

    5_000_000.times do
      count += 1
    end
  end
end

threads.each {|t| t.join }
puts "done!"

试着运行这个例子,然后选择一个数字可以让你的机器跑上几秒钟.这取决于你的硬件,你可以增加这个数字.
在我的机器上,运行这个程序耗时2.156秒.如果我使用了某些进程监控工具,例如top,我可以发现它仅使用了一个cpu核心.这就是GIL在起作用.

这虽然是一个虚拟的程序,但是在真实世界中你可以想象出一个和他类似的问题.我们的目的是,使用切换繁忙线程来模拟并发.

Page 57

3.3.2 一个Rust库

让我们用Rust重写这个问题.首先用Cargo创建一个新的工程:

$ cargo new embed
$ cd embed

这个问题用Rust写很简单:

use std::thread;

#[no_mangle]
pub extern fn process() {
    let handles: Vec<_> = (0..10).map(|_| {
        thread::spawn(|| {
            let mut _x = 0;
            for _ in (0..5_000_001) {
                _x += 1;
            }
        })
    }).collect();

    for h in handles {
        h.join().ok().expect("Could not join a thread!");
    }
}
部分代码看起来有些眼熟.我们循环切换了10个线程,将它们收集到一个handles的向量中.在每个线程里面,我们循环500万个次,每次给_x变量加1.为什么是下划线?如果去掉就会有这个结果:
技术分享
技术分享
Page 58

第一个警告是因为我们在构建一个库.如果我们添加了一个测试方法,这个警告就会消失.但是现在没有测试方法.

第二个和x于_x有关.因为我们并没有真正让x做任何事情,我们得到了这个警告.在此例中,这是正常的,我们就是要浪费CPU循环.在x前面加上下划线就会去掉这个警告.

最后,我们join每一个线程.

现在,我们有了一个Rust库,但是没有任何C语言程序调用它.如果我现在把它接入其他程序中它还不能工作.我们仅需做两点小改动就可以了.第一点是代码的开头部分:
#[no_mangle]
pub extern fn process() {

我们需要增加一个新的attribute, no_mangle.当你创建一个Rust库的时候,它会在编译时改变函数的名字.这么做的原因不在本书的讨论范围内,但是为了使其他语言能够调用我们的库,我们不能改变函数的名字.这个attribute就是关闭这个行为的.

另一个改动是pub extern.pub意味着这个函数将被从本模块外部调用,extern是说它会被C语言调用.就这样!并没有改变很多代码.

第二件事是我们需要修改Cargo.toml文件.在底部增加这个:
[lib]
name = "embed"
crate-type = ["dylib"]

这样就会告诉Rust我们想要将我们的库编译成一个标准动态库.默认的,Rust会编译成一个"rlib",一个rust格式的库.

现在我们编译一下:
技术分享
技术分享
我们选择用cargo build --release,这样会有编译优化.我们想尽可能的执行越快越好.你可以在target/release目录找到输出文件.
技术分享
技术分享
这个embed.dll就是我们的"共享对象"库.我们可以和使用其他C语言写的共享对象一样使用它!

现在我们已经有了Rust库,让我们在Ruby中使用它.

Page 59

3.3.3 Ruby

打开embed.rb文件,输入下述代码:
require ‘ffi‘
require ‘Time‘
start = Time.now

module Hello
  extend FFI::Library
  ffi_lib ‘target/release/embed.dll‘
  attach_function :process, [], :void
end

Hello.process
diff = Time.now - start
puts diff
puts "done!"

在正常运行前我们需要安装ffi gem:
$gem install ffi

最后,运行这个ruby脚本:
$ ruby embed.rb
0.002
done!

WOW,真快!在我的系统上只用了0.002秒.ruby脚本需要运行2秒多.我们来看一下代码:

require ‘ffi‘
我们需要首先引入ffi的gem包.这可以让我们的Rust代码像C语言的库一样被使用.

Page 60

module Hello
  extend FFI::Library
  ffi_lib ‘target/release/embed.dll‘
ffi的作者强烈建议使用一个module来包含我们从共享对象引入的代码.其中,我们需要使用FFI::Library模块,然后调用ffi_lib来加载我们的共享对象.我们只需传递路径就可以了,这里是target/release/embed.dll

attach_function :process, [], :void

attach_function方法是FFI包提供的.他的功能是将Rust中的process方法和ruby中的同名方法进行关联.因为prcess方法没有参数,所以第二个参数是空数组,又因为它没有返回值,所以最后一个传入的参数是:void.

Hello.process

这就是真正调用Rust代码的地方.通过调用attach_function和以及之前和Rust模块绑定来实现这一点.这看起来像是一个Ruby方法,但实际上调用了Rust方法!

puts "done!"

最后,和我们之前的需求一样,我们打印"done!".
就是这么简单!两种语言之间的桥梁非常简单的实现了,并且带给我们很多性能提升.
接下来,我们来看看Python!

Rust中文翻译10

标签:rust   编程语言   

原文地址:http://blog.csdn.net/zcmit/article/details/46740665

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!