概要
Plotters は環境によっては次のように Illegal instruction でクラッシュする。今回は sse41
のCPU命令が使えない環境であることが原因だった。
$ cargo run
...
Compiling pathfinder_simd v0.5.1
Compiling pathfinder_geometry v0.5.1
Compiling font-kit v0.10.1
Compiling plotters v0.3.1
Compiling quick-start v0.1.0 (.../quick-start)
Finished dev [unoptimized + debuginfo] target(s) in 26.58s
Running `target/debug/quick-start`
Illegal instruction (core dumped)
環境
- CPU ... AMD Phenom(tm) II X4 910e Processor
- WSL (Ubuntu 18.04 LTS)
- rustc 1.57.0
- plotters v0.3.1
- pathfinder_geometry v0.5.1
- pathfinder_simd v0.5.1
回避方法
Cargo.toml の pathfinder_simd
のクレートに対して pf-no-simd
を指定する。
@@ -1,7 +1,8 @@
[package]
name = "quick-start"
version = "0.1.0"
edition = "2021"
[dependencies]
+pathfinder_simd = { version = "0.5", features = ["pf-no-simd"] }
plotters = "0.3"
解説
-
plotters
はpathfinder_geometry
を依存関係に持つ -
pathfinder_geometry
はpathfinder_simd
の v0.5を依存関係にもつ -
pathfinder_simd
は何も指定しない場合はsse41
のCPU命令を含む形でビルドする
pathfinder_simd
のソースコードを見るにpf-no-simd
の feature でこの動作を変えられる。
ただ、Crate pathfinder_geometry や Crate pathfinder_simd のサイトにはそんな解説はないので、隠し機能的な扱いと思われる。
GitHub の pathfinder/simd/Cargo.toml のCargo.toml をみると pf-no-simd
という features があることが分かる。
[package]
name = "pathfinder_simd"
...
[features]
pf-no-simd = []
...
また、 GitHub の pathfinder/geometry/Cargo.toml からは、バージョン 0.5 を依存関係にもつので同じバージョンで pf-no-simd
を指定してビルドすればよいと推測できる。
[package]
name = "pathfinder_geometry"
...
[dependencies.pathfinder_simd]
path = "../simd"
version = "0.5"
参考: 調べ方
参考1: Illegal instruction
がどのクレートで発生したのか確認する方法
cargo build
や cargo run
はデバッグモードでコンパイルされているので、 gdb
を使えば調べることができる。
gdb
に深い意味はない。Rust流のもっと適した方法があるはずだが、これを調べた時点では知らないので今ある知識を活用する意図で gdb
を使った。
bt
でバックトレースが確認できるが #0
で sse41
に関する命令が原因と推測できるし、 #1
でそれが pathfinder_simd
によって呼び出されたことが分かる。
$ gdb target/debug/quick-start
...
(gdb) run
...
(gdb) bt
#0 0x00000000080dc4f5 in core::core_arch::x86::sse41::_mm_max_epi32 (a=..., b=...)
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/../../stdarch/crates/core_arch/src/x86/sse41.rs:287
#1 0x00000000080db055 in pathfinder_simd::x86::I32x4::max (self=..., other=...)
...
参考2: CPUのサポートする命令の確認方法
cat /proc/cpuinfo
や lshw
を使って sse41
の有無を確認できる。
-
/proc/cpuinfo
を使う方法
$ grep flags /proc/cpuinfo
flags : fpu vme de pse tsc msr ... sse sse2 ... sse4a ...
-
lshw
を使う方法
$ sudo lshw -class processor
[sudo] password for ********:
*-cpu
...
capabilities: fpu fpu_exception wp vme de pse tsc msr
... sse sse2 ... sse4a ...
参考3: 事例で調べる
CPU名が分れば、カタログスペックやらで確認出来たりもする。
以下、「amd phenom sse」あたりで検索して出てきたサイトで SIMD 命令の対応状況が表にまとめられている。
参考: 検証に使ったソース
Crate plotters > Quick startにあるそのままのコードです。 plotters-doc-data/
のディレクトリは事前に作っておく必要がありそう。
use plotters::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let root = BitMapBackend::new("plotters-doc-data/0.png", (640, 480)).into_drawing_area();
root.fill(&WHITE)?;
let mut chart = ChartBuilder::on(&root)
.caption("y=x^2", ("sans-serif", 50).into_font())
.margin(5)
.x_label_area_size(30)
.y_label_area_size(30)
.build_cartesian_2d(-1f32..1f32, -0.1f32..1f32)?;
chart.configure_mesh().draw()?;
chart
.draw_series(LineSeries::new(
(-50..=50).map(|x| x as f32 / 50.0).map(|x| (x, x * x)),
&RED,
))?
.label("y = x^2")
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED));
chart
.configure_series_labels()
.background_style(&WHITE.mix(0.8))
.border_style(&BLACK)
.draw()?;
Ok(())
}
実行結果