4
1

More than 3 years have passed since last update.

「実践Rustプログラミング入門」に入門する(4日目)RubyからRustを呼ぶ

Last updated at Posted at 2020-09-04

概要

「実践Rustプログラミング入門」pp.421-427を参照して、RubyからRustの関数を呼ぶ。
そういえば今日(2020年9月4日(金))はRuby Kaigi

Windows用の準備

RubyInstaller for Windowsから、Windows用のRubyをダウンロードする。
本記事執筆時点最新はRuby+Devkit 2.7.1-1 (x64)

01_RubyInstaller2_01.png

"If unsure press ENTER"とあるのでENTERで進む。

02_RubyInstaller2_02.png

"Install MSYS2 and MINGW developent toolchain succeeded"とのことなので、ENTERは、3を押したのと同じことだったか。
ここでENTERを押して、インストール終了。

Windows PowerShell上でrubyの動作を確認する。

PS > ruby -v
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]

VSCodeでもTERMINALを開きなおせば確認できる。

p.421 サンプル:RubyからRustの関数を呼ぶ。

p.421の実験を行うためのプロジェクトディレクトリを作る。

PS > cargo new ffitest
     Created binary (application) `ffitest` package

ffitestの下にsampleディレクトリを作り、Rubyのコードを書いて、Rubyの処理を実験する。

ffitest/sample/add_array.rb
def add_array(n,x)
    a = Array.new(n,0)
    x.times do
        for i in 0..x-1
            a[i] += 1
        end
    end
    a.sum
end

puts add_array(ARGV[0].to_i, ARGV[1].to_i)

Measure-Commandは、Linuxでのtime相当のコマンド。

PS ffitest\sample> Measure-Command {ruby add_array.rb 10000 10000}       


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 8
Milliseconds      : 53
Ticks             : 80530169
TotalDays         : 9.32062141203704E-05
TotalHours        : 0.00223694913888889
TotalMinutes      : 0.134216948333333
TotalSeconds      : 8.0530169
TotalMilliseconds : 8053.0169

TotalSecondsから、約8秒かかっていることがわかる。

つづいてRust側。src/main.rsをsrc/add_array.rsにRenameする。

src/add_array.rs
fn add_array(n: u64, x: u64) -> u64 {
    let mut a = vec![0u64; n as usize];
    for _ in 0..x {
        for i in 0..n as usize {
            a[i] += 1;
        }
    }
    a.iter().sum()
}

use std::env;
fn main() {
    let args: Vec<_> = env::args().collect();
    let n = args[1].parse::<u64>().unwrap();
    let x = args[2].parse::<u64>().unwrap();
    println!("{}", add_array(n, x));
}

Cargo.tomlに、add_arrayの実行設定を追記する。

Cargo.toml
[[bin]]
name = "add_array"
path = "src/add_array.rs"

p.422最下段のビルド&実行確認を行う。

PS ffitest> cargo build --release
   Compiling ffitest v0.1.0 (ffitest)
    Finished release [optimized] target(s) in 1.29s     
PS ffitest> Measure-Command {./target/release/add_array 10000 10000}     


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 93
Ticks             : 935455
TotalDays         : 1.0827025462963E-06
TotalHours        : 2.59848611111111E-05
TotalMinutes      : 0.00155909166666667
TotalSeconds      : 0.0935455
TotalMilliseconds : 93.5455

0.09秒。さすがに速い。

p.423 ライブラリ化

ライブラリのプロジェクトディレクトリを作成する。

PS > cargo new --lib addarray
     Created library `addarray` package

Cargo.tomlに動的ライブラリ指定を行う。

addarray/Cargo.toml
[lib]
crate-type = ["cdylib"]

ここまでの作業で、src/lib.rsが作成されている。中身は以下の通り。

src/lib.rs
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

上記のコードは消去し、p.424の2つめのlib.rs通りに記述する。

src/lib.rs
#[no_mangle]
pub extern "C" fn add_array(n: u64, x: u64) -> u64 {
    let mut a = vec![0u64; n as usize];
    for _ in 0..x {
        for i in 0..n as usize {
            a[i] += 1;
        }
    }
    a.iter().sum()
}

ビルドする。いろいろ思考錯誤した結果、--target=x86_64-pc-windows-msvcとすると、今回のRubyで動かせるDLLになるようだ(調査不足)。

PS > cargo build --release --target=x86_64-pc-windows-msvc       
   Compiling addarray v0.1.0 (addarray)
    Finished release [optimized] target(s) in 0.87s   

出来たライブラリを確認する。

PS addarray> ls .\target\x86_64-pc-windows-msvc\release\


    ディレクトリ: addarray\target\x86_64-pc-windows-msvc\release


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/09/04     --:--                .fingerprint
d-----       2020/09/04     --:--                build
d-----       2020/09/04     --:--                deps
d-----       2020/09/04     --:--                examples
d-----       2020/09/04     --:--                incremental
-a----       2020/09/04     --:--              0 .cargo-lock
-a----       2020/09/04     --:--            109 addarray.d
-a----       2020/09/04     --:--         128512 addarray.dll
-a----       2020/09/04     --:--            980 addarray.dll.exp
-a----       2020/09/04     --:--           1942 addarray.dll.lib
-a----       2020/09/04     --:--         937984 addarray.pdb

addarray.dllができていればOK。

p.425 Rubyからの呼び出し

Rubyのffiをインストールする。

PS > gem install ffi
Fetching ffi-1.13.1-x64-mingw32.gem
Successfully installed ffi-1.13.1-x64-mingw32
Parsing documentation for ffi-1.13.1-x64-mingw32
Installing ri documentation for ffi-1.13.1-x64-mingw32
Done installing documentation for ffi after 1 seconds
1 gem installed

Rubyのソースコードは、addarray/sample/add_array_rs.rbとして作成する。DLLのパス指定に注意。相対パス指定していたらハマったので、絶対パス指定にした。

addarray/sample/add_array_rs.rb
require 'ffi'

module AddArray
    extend FFI::Library
    ffi_lib 'C:\your\path\addarray\target\x86_64-pc-windows-msvc\release\addarray.dll'
    attach_function :add_array, [:uint64, :uint64], :uint64
end

puts AddArray::add_array(ARGV[0].to_i, ARGV[1].to_i)

p.426 実行確認

PS addarray\sample> Measure-Command {ruby .\add_array_rs.rb 10000 10000} 


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 311
Ticks             : 3111198
TotalDays         : 3.60092361111111E-06
TotalHours        : 8.64221666666667E-05
TotalMinutes      : 0.00518533
TotalSeconds      : 0.3111198
TotalMilliseconds : 311.1198

0.3秒。0.09秒からすると遅くはなるが、もともと8秒だったのだから素晴らしい改善。

番外編:Windows用Ruby&Rust連携のための環境設定

Windows上で動作させる場合は、Rubyの環境に合わせてRust側が適切なDLLを作成しなければならない。という落とし穴にハマり、調査した雑記。

MINGW64のtargetを追加する場合。

PS > rustup target add x86_64-pc-windows-gnu
info: downloading component 'rust-std' for 'x86_64-pc-windows-gnu'
info: installing component 'rust-std' for 'x86_64-pc-windows-gnu'
info: Defaulting to 500.0 MiB unpack ram
 14.1 MiB /  14.1 MiB (100 %)  10.9 MiB/s in  1s ETA:  0s

rustupを使って、targetの一覧を確認する。

PS addarray> rustup show
Default host: x86_64-pc-windows-msvc
rustup home:  .rustup

installed targets for active toolchain
--------------------------------------

i686-pc-windows-gnu
x86_64-pc-windows-gnu
x86_64-pc-windows-msvc

active toolchain
----------------

stable-x86_64-pc-windows-msvc (default)
rustc 1.45.2 (d3fb005a3 2020-07-31)

cargo buildすると、「x86_64-w64-mingw32-gccがみつからない」と怒られるケースがあった。PATHを追加する。

PS > $ENV:Path="C:\Ruby27-x64\msys64\mingw64\bin;"+$ENV:Path

ビルドする。

PS addarray> cargo build --release --target=x86_64-pc-windows-gnu --verbose
       Fresh addarray v0.1.0 (addarray)
    Finished release [optimized] target(s) in 0.02s

addarray.dllができている。

PS addarray> ls .\target\x86_64-pc-windows-gnu\release


    ディレクトリ: addarray\target\x86_64-pc-windows-gnu\release


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/09/04     --:--                .fingerprint
d-----       2020/09/04     --:--                build
d-----       2020/09/04     --:--                deps
d-----       2020/09/04     --:--                examples
d-----       2020/09/04     --:--                incremental
-a----       2020/09/04     --:--              0 .cargo-lock
-a----       2020/09/04     --:--            108 addarray.d
-a----       2020/09/04     --:--        3689956 addarray.dll
-a----       2020/09/04     --:--           2056 libaddarray.dll.a

add_array_rs.rb修正&動作確認

改めて、Rubyコード内のDLL指定を変更する。

add_array_rs.rb
require 'ffi'

module AddArray
    extend FFI::Library
    ffi_lib 'C:\your\path\addarray\target\x86_64-pc-windows-gnu\release\addarray.dll'
    attach_function :add_array, [:uint64, :uint64], :uint64
end

puts AddArray::add_array(ARGV[0].to_i, ARGV[1].to_i)

反省

  1. Windowsで実験するときは、実行環境に注意する。mingwや32ビット、64ビットなど、いろいろと落とし穴がある。
4
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1