質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
86.12%
パフォーマンス

コード効率の向上や計算に関する質問には、このタグを使ってください。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

解決済

RustでPythonよりもパフォーマンスが出ない

Yhaya
Yhaya

総合スコア437

パフォーマンス

コード効率の向上や計算に関する質問には、このタグを使ってください。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

4回答

0リアクション

0クリップ

988閲覧

投稿2021/12/03 05:08

編集2021/12/03 06:37

Rustを使った数値計算のプログラムを書いています。その中で、昔書いた Pythonで使う用のコードをRust側で再利用しているのですが、そこでのパフォーマンスがかなり低く、解決方法を探しています。

環境

  • Rust 1.55.0
  • WSL2 (Windows 10)

詳細

Python用に書いたコードは以下のようになっています。

lib.rs

rust

mod kk; use pyo3::prelude::*; use pyo3::wrap_pyfunction; use kk::kk::{real2imag_helper, imag2real_helper, kk_transform}; #[pymodule] fn pykk(_py: Python, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(real2imag))?; m.add_wrapped(wrap_pyfunction!(imag2real))?; Ok(()) } #[pyfunction] fn real2imag(x: Vec<f64>, y: Vec<f64>) -> PyResult<Vec<f64>> { kk_transform(x, y, real2imag_helper) } /// Calculate the real part from the imaginary part /// /// * `x` - independent variable (ex. energy or frequency) /// * `y` - dependent variable (ex. conductivity or permittivity) #[pyfunction] fn imag2real(x: Vec<f64>, y: Vec<f64>) -> PyResult<Vec<f64>> { kk_transform(x, y, imag2real_helper) }

kk.rs

rust

use std::sync::{Mutex, Arc}; use std::thread; use std::f64::consts::PI; use pyo3::prelude::*; use pyo3::exceptions::PyValueError; pub fn kk_transform<F>(x: Vec<f64>, y: Vec<f64>, f: F) -> PyResult<Vec<f64>> where F: Fn(&Vec<f64>, &Vec<f64>, usize) -> f64, F: Send + Copy + 'static { let thread_num = 16; let mut handles: Vec<thread::JoinHandle<()>> = Vec::new(); let mut result: Arc<Vec<Mutex<f64>>> = Arc::new( vec![0.0; y.len()] .into_iter() .map(|x| Mutex::new(x)) .collect() ); for i in 0..thread_num { let x = x.clone(); let y = y.clone(); let result = Arc::clone(&mut result); let handle = thread::spawn(move || { for j in x.len()*i/thread_num..x.len()*(i+1)/thread_num { let mut val = result[j].lock().unwrap(); *val = 2.0 / PI * f(&x, &y, j); } }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } let result = Arc::try_unwrap(result).unwrap(); let mut output = vec![0.0; y.len()]; for (i, val) in result.iter().enumerate() { output[i] = *val.lock().unwrap(); } Ok(output) } #[allow(dead_code)] pub fn real2imag_helper(x: &Vec<f64>, y: &Vec<f64>, num: usize) -> f64 { let mut result = 0.0; let diff = x[1] - x[0]; let base = x[num]; for (xx, yy) in x.iter().zip(y.iter()) { if *xx == base { continue; } result -= base * yy / (xx * xx - base * base) * diff; } result } #[allow(dead_code)] pub fn imag2real_helper(x: &Vec<f64>, y: &Vec<f64>, num: usize) -> f64 { let mut result = 0.0; let diff = x[1] - x[0]; let base = x[num]; for (xx, yy) in x.iter().zip(y.iter()) { if *xx == x[num] { continue; } result += xx * yy / (xx * xx - base * base) * diff; } result }

これをRustで再利用するために、コピペして今書いているプロジェクトのコードに導入しました。

rust

use std::f64::consts::PI; use std::sync::{Arc, Mutex}; use std::thread; pub fn imag2real(x: Vec<f64>, y: Vec<f64>) -> Vec<f64> { kk_transform(x, y, imag2real_helper) } fn kk_transform<F>(x: Vec<f64>, y: Vec<f64>, f: F) -> Vec<f64> where F: Fn(&Vec<f64>, &Vec<f64>, usize) -> f64, F: Send + Copy + 'static, { let thread_num = 16; let mut handles: Vec<thread::JoinHandle<()>> = Vec::new(); let mut result: Arc<Vec<Mutex<f64>>> = Arc::new( vec![0.0; y.len()] .into_iter() .map(|x| Mutex::new(x)) .collect(), ); for i in 0..thread_num { let x = x.clone(); let y = y.clone(); let result = Arc::clone(&mut result); let handle = thread::spawn(move || { for j in x.len() * i / thread_num..x.len() * (i + 1) / thread_num { let mut val = result[j].lock().unwrap(); *val = 2.0 / PI * f(&x, &y, j); // <- ボトルネック!!! } }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } let result = Arc::try_unwrap(result).unwrap(); let mut output = vec![0.0; y.len()]; for (i, val) in result.iter().enumerate() { output[i] = *val.lock().unwrap(); } output } // ここはPythonで使っていたコードから試行錯誤してすこし変えてみたが根本的な解決 // にはなっていない fn imag2real_helper(x: &Vec<f64>, y: &Vec<f64>, num: usize) -> f64 { let diff = x[1] - x[0]; let base = x[num]; let result = x .iter() .zip(y.iter()) .map(|(x, y)| { if *x == base { 0.0 } else { x * y / (x.powf(2.0) - base.powf(2.0)) * diff } }) .sum(); result }

これらのコードで速度を比較してみました。比較した関数は,

  • pyfunctionを付けてPython側で使えるようにした imag2real (一番上のコード)
  • Rust用に書いた imag2real (一番下のコード)

です。速度を計測したところ, Rustで使うように書いた imag2realが2桁ほど遅くなっていました。細かく時間を計測してみると imag2real_helper で全体の9割くらいの時間を使っていたことがわかったのですが、どこを改善すれば速くなるのかわかりません。

よろしくお願いいたします。

参考情報

Python拡張として書いたRustコード:
https://github.com/Hayashi-Yudai/pykk

以下のような質問にはリアクションをつけましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

リアクションが多い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

melian

2021/12/03 06:20

昔に書いたという Python スクリプトでは Numpy を使っていましたか?
Yhaya

2021/12/03 06:28

いえ、RustオンリーでPython拡張を書いておりまして、その拡張機能で作ったメソッドをPythonで呼び出したときの速度を、質問に載せましたコードと比較すると2桁ほど遅いという問題が起きています。 Rustで作ったPython拡張は https://github.com/Hayashi-Yudai/pykk に公開していますので、この問題の解決の参考になるかわかりませんが、よろしかったら見てください。
melian

2021/12/03 06:31

ありがとうございます。読ませていただきます。
dalance

2021/12/03 08:25

コピペしたコードとのことで、2つのコードはほとんど同じですので この2つを比べて原因を究明するのは難しそうです。 同じコードで速度が違うということは、このコードの外の環境による可能性が高いと思います。 実際に実行して速度比較が可能なリポジトリをご用意いただくのがいいかもしれません。
tatsuya6502

2021/12/03 23:58 編集

何点か質問があるのと、確認してほしいことがあります。 コード内の環境に関係するもの ・imag2real_helper() の内容が kk.rs と一番下のコードで変わってますが、なぜでしょうか? ・一番下のコードの imag2real_helper() の内容を kk.rs と同じにすると、性能差はどうなりますか? コード外の環境に関係するもの ・Python拡張ですが、PyPIで公開されているWindows版(.pyd)を使っているのでしょうか? ・もしそうなら、WSL2のRust 1.55.0でLinux版(.so)をビルドしてPythonから実行すると性能差はどうなりますか?
Yhaya

2021/12/04 00:48

imag2real_helperは上のように元の状態から変えることで少し高速化できています。Linux版のビルドは、これから試してみます
dalance

2021/12/04 01:03

Python版はWindowsネイティブ環境、Rustのみ版はWSL2ということですか? その場合、実行中のWindowsから見たCPU使用率はどうでしょう? どちらも16スレッド分使われていますか?
Yhaya

2021/12/04 02:35

調べていたら、Pythonライブラリとしたコードで、Python内で使うところでバグがあったことが分かりました。修正したところ、どちらのコードも同じくらいの速さであることが分かりました。お騒がせしまして申し訳ありませんでした
tatsuya6502

2021/12/04 02:45

ご報告ありがとうございます。解決してよかったです!

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
86.12%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

パフォーマンス

コード効率の向上や計算に関する質問には、このタグを使ってください。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。