"Introduction to Practical Rust Programming" Refer to pp.421-427 and call Rust functions from Ruby. By the way, today (Friday, September 4, 2020) is Ruby Kaigi.
Download Ruby for Windows from RubyInstaller for Windows. At the time of writing this article, the latest is [Ruby + Devkit 2.7.1-1 (x64)](https://github.com/oneclick/rubyinstaller2/releases/download/RubyInstaller-2.7.1-1/rubyinstaller-devkit-2.7. 1-1-x64.exe).
There is "If unsure press ENTER", so press ENTER to proceed.
Since it says "Install MSYS2 and MINGW developent toolchain succeeded", was ENTER the same as pressing 3? Now press ENTER to complete the installation.
Check the operation of ruby on Windows PowerShell.
PS > ruby -v
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]
You can check it with VS Code by reopening TERMINAL.
Create a project directory for the experiment on p.421.
PS > cargo new ffitest
Created binary (application) `ffitest` package
Create a sample directory under ffitest, write Ruby code, and experiment with Ruby processing.
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 is equivalent to time
on Linux command.
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
From TotalSeconds, you can see that it takes about 8 seconds.
Next is the Rust side. Rename src / main.rs to src / add_array.rs.
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));
}
Add the execution setting of add_array to Cargo.toml.
Cargo.toml
[[bin]]
name = "add_array"
path = "src/add_array.rs"
p.422 Perform build & execution confirmation at the bottom.
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 seconds. It's really fast.
Create a project directory for the library.
PS > cargo new --lib addarray
Created library `addarray` package
[Dynamic library specification] in Cargo.toml (https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/cdylib-crates-for-c-interoperability.html )I do.
addarray/Cargo.toml
[lib]
crate-type = ["cdylib"]
By the work so far, src / lib.rs has been created. The contents are as follows.
src/lib.rs
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
Delete the above code and write it according to the second lib.rs on p.424.
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()
}
Build. As a result of various thoughts and mistakes, if --target = x86_64-pc-windows-msvc
is set, it seems that it will be a DLL that can be run with Ruby this time (insufficient research).
PS > cargo build --release --target=x86_64-pc-windows-msvc
Compiling addarray v0.1.0 (addarray)
Finished release [optimized] target(s) in 0.87s
Check the created library.
PS addarray> ls .\target\x86_64-pc-windows-msvc\release\
directory: 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
OK if addarray.dll is created.
Install 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
The Ruby source code is created as addarray / sample / add_array_rs.rb. Be careful when specifying the DLL path. I was addicted to specifying a relative path, so I specified an absolute path.
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)
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 seconds. It's slower than 0.09 seconds, but it was originally 8 seconds, so it's a great improvement.
When running on Windows, Rust must create an appropriate DLL according to the Ruby environment. I was addicted to the pitfall and investigated the miscellaneous notes.
When adding a 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
Use rustup to see the list of targets.
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)
When I did cargo build
, I was angry that I couldn't find x86_64-w64-mingw32-gcc. Add PATH.
PS > $ENV:Path="C:\Ruby27-x64\msys64\mingw64\bin;"+$ENV:Path
Build.
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 is created.
PS addarray> ls .\target\x86_64-pc-windows-gnu\release
directory: 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
Change the DLL specification in the Ruby code again.
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)
Recommended Posts