FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Rustの並列処理についての勉強

Rustで作る命令セットシミュレータの続き、どうも高速化のためには並列処理について勉強する必要が生じてきた気がするので、Rustで並列処理を実現するための方法について勉強している。

実現したいこととしては:

  • メインスレッドでエミュレーションが実行され、MemoryMap中のメモリが更新される
  • サブスレッドでメインスレッドを監視し、MemoryMapの特定のアドレスがある値に変更されるとそれを検出し当該処理を行う

というものなのだが、これをやっているうちにかなり泥沼に迷い込んでしまった。まず、MemoryMapをスレッド間で共有することができなかった。MemoryMapは実体がmut *u8なのか?これをスレッド間で安全に共有することができない、という訳か。

そこで少しずつ実装を簡単にしていき、どこまで持って行けばスレッド間で情報を共有できるのかいろいろ調査した。最終的にArcMutexを使った情報共有の方法まで戻ってしまった。もはやMemoryMapは全く関係ない実装になってしまっている。

以下のプログラムは2スレッド間でmemを共有している。メインのスレッドが0.1秒に一回memの値を更新し、サブスレッドがその値を0.01秒に1回監視し、値が100を超えたらその場でプログラムを終了するというものだ。

    let mem = Arc::new(Mutex::new(0));

    thread::spawn({
        let mem_local = Arc::clone(&mem);
        move || {
            loop{
                thread::sleep(Duration::from_millis(10));
                let num = mem_local.lock().unwrap();
                if *num > 100 {
                    println!("break loop");
                    exit(0);
                }
            }
        }
    });

    let mem_local = Arc::clone(&mem);
    for _num in 0..200 {
        thread::sleep(Duration::from_millis(100));
        let mut num = mem_local.lock().unwrap();
        *num = *num + 1;
        println!("counting up ... {}", *num);
    }

結局、Rustでスレッド間データ共有をするためには、MemoryMap以外の方法を使わなければならないという結論に至った。

従って、私の本来の目的を達成するためには大きく方針を転換する必要がある。特定のメモリアドレスへの書き込みをサブスレッドで監視しておき、それを検出したかったのだが、その「特定のメモリアドレス」の場所だけは別のデータ構造、Mutexに置き換えておき、これをサブスレッドから監視しておく必要がある。 QEMUもどきを作っているので、メモリへのアクセスはすべてアセンブリ命令で実現していたのだが、Mutexへの書き込みとなるとさすがにアセンブリで書くわけにはいかない。当該メモリアドレスへのアクセスは常にトラップを起こしてハンドラへ飛び、自力でMutexへ書き込みを行ったうえでサブスレッドがそれを検出する、という方法になりそうだ。 まあ面倒くさいができない話ではない。この方針にもどついて実装を行ってみよう。