rust 動的ディスパッチと静的ディスパッチの調査

# rust 動的ディスパッチと静的ディスパッチの調査

##環境
rustc 1.27.1 

##概要
動的ディスパッチと静的ディスパッチってどっちが速いの？って思ったから検証してみました。
検証自体は、コンパイラの事詳しくないので正直ガバガバだと思いますが、rustのいい練習になったので記事にします。

### 動的ディスパッチ
動的ディスパッチの特徴を下記に記します。

- コードが膨張しないが低速な仮想関数の呼び出しが必要
- 正確な型は実行時になって初めて判明する
- インライン化（コンパイラによる最適化手法）ができない

詳しく知りたい人は、同じくrustのチュートリアルページにある、トレイトオブジェクトのページを見てください。
[トレイトオブジェクト](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/trait-objects.html)


### 静的ディスパッチ
静的ディスパッチの特徴を下記に記します。

- 呼び出される関数はコンパイル時に分かっている
- インライン化可能
- 同じ関数をそれぞれの型毎に幾つもコピーするためバイナリが膨張する

詳しく知りたい人は、同じくrustのチュートリアルページにある、トレイトオブジェクトのページを見てください。
[トレイトオブジェクト](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/trait-objects.html)


### プログラムを組む１

クロージャを作成し、それを動的ディスパッチと静的ディスパッチの２通りの方法で呼び出し、時間を計測してみます。

```rust:端末で入力するバージョン(rust)
fn main(){
    // クロージャの作成
    let add_one = |x: i32| x + 1;
    
    loop{
        println!("試行回数を入力してください");

        let mut  trial_count =  String::new();
        io::stdin().read_line(&mut trial_count)
                .expect("端末からの読み取りに失敗");
        
        let trial_count: u64 = match trial_count.trim().parse(){
            Ok(num) => num,
            Err(_) => continue,
        };
        println!("{}回ループさせます", trial_count);

        // 静的ディスパッチ
        fn static_fanc<F>(some_closure: F) -> i32
            where F : Fn(i32) -> i32 {
            some_closure(1)
        }

        let start = Instant::now();
        for  _ in 0..trial_count{
            static_fanc(add_one);
        }
        println!("静的なディスパッチ：{:?}", start.elapsed());

        // 動的ディスパッチ
        fn dynamic_fanc(some_closure: &Fn(i32) -> i32) -> i32{
            some_closure(1)
        }

        let start = Instant::now();
        for  _ in 0..trial_count {
            dynamic_fanc(&add_one);
        }
        println!("動的ディスパッチ：{:?}", start.elapsed());
    }
}
```
以下は、10000と入力した例

```console:出力
試行回数を入力してください
10000
10000回ループさせます
静的なディスパッチ：Duration { secs: 0, nanos: 1666373 }
動的ディスパッチ：Duration { secs: 0, nanos: 1439735 }
```

### プログラムを組む2

わかりにくいので、csvにしてグラフにすることにしました。
試行回数は、とりあえず５万回。

```rust:csvを出力するバージョン（rust）
use std::time::{Instant};
use std::io::prelude::*;
use std::fs::File;

fn main(){
    let s = format!("rust.csv");
    let mut buffer = File::create(s).unwrap();

    // クロージャの作成
    let add_one = |x: i32| x + 1;

    for trial_count in 1..50000{

        // 静的ディスパッチ
        fn static_fanc<F>(some_closure: F) -> i32
            where F : Fn(i32) -> i32 {
            some_closure(1)
        }

        let start = Instant::now();
        for  _ in 0..trial_count {
            static_fanc(add_one);
        }
        let static_time = start.elapsed();

        // 動的ディスパッチ
        fn dynamic_fanc(some_closure: &Fn(i32) -> i32) -> i32{
            some_closure(1)
        }

        let start = Instant::now();
        for  _ in 0..trial_count {
            dynamic_fanc(&add_one);
        }
        let dynamic_time = start.elapsed();

        if trial_count % 1000  == 0 {
            write!(buffer, "{}, {}, {}\n", 
                        trial_count, 
                        static_time.subsec_nanos() / 1_000,
                        dynamic_time.subsec_nanos() / 1_000).unwrap();
        }
    }
}
```

次に、pythonを使います。

```python:グラフ描画(python)
import csv
import numpy as np
import matplotlib.pyplot as plt

try:
    with open("./rust.csv", 'rt') as fin:
        reader = csv.reader(fin)
        trial_count = [i[0] for i in reader]; fin.seek(0)
        static_time = [i[1] for i in reader]; fin.seek(0)
        dynamic_time = [i[2] for i in reader]
except:
    print("エラー発生。プログラム終了。")
    sys.exit()

# 描画範囲の指定
print(trial_count)
print(static_time)

x = list(map(int, trial_count))
y1 =list(map(int, static_time))
y2 = list(map(int, dynamic_time))

# グラフ描画設定
plt.plot(x, y1, color="b", label="static")
plt.plot(x, y2, color="r", label="dynamic")
plt.ylabel("time(micro)") 
plt.xlabel("trial_count")
plt.title('static & dynamic')

# ラベルの描画
plt.legend()
# グラフの描画実行
plt.show()
```

### グラフの表示
上記プログラムで作成したグラフを以下に掲載します。
１回目
![figure_1-1.png](https://qiita-image-store.s3.amazonaws.com/0/195114/8abf583e-c9dc-856f-c40a-57a75b1aac77.png)
２回目
![figure_1-2.png](https://qiita-image-store.s3.amazonaws.com/0/195114/98b60731-4510-bb44-a92b-e3a6fff01984.png)
３回目
![figure_1-3.png](https://qiita-image-store.s3.amazonaws.com/0/195114/f0f21578-199a-ee09-b5ad-40273ba27fa4.png)

３つのグラフに規則性が見られません。
なんか、凄い跳ね上がってる所が多々あるぐらいです。
単純なプログラムでは、あまり変わらないということでしょうか。