借り初めのひみつきち

仮ブログです。

今週の MYOS

さいきんの MYOS 界隈のトピックです。

WASM 高速化

以前の大規模改修で数倍の高速化に成功した WASM ランタイムですが、まだいくつか課題と高速化の余地があります。

まず、実行時エラーの処理を改善しました。

以前のエラー処理は命令実行時に現在の命令を覚えておいて実際にエラーが発生した場合にエラーコードを投げて呼び出し元が覚えておいた命令と合成してエラー表示していました。 しかし、プログラムの実行時間の大半はエラーを起こさない命令を実行しているので、エラーに関わらず毎回命令を保存する処理は無駄が多く速度低下の原因のひとつにもなっていました。 そもそも呼び出し元がエラーの発生した場所を特定する仕組みだったので実際にはあまりうまく動かないケースがあり、速度低下のこともあって高速化の際にこの機能を無効にしてエラー発生場所が完全にわからない状態になっていました。

新しいエラー処理では、命令の実行時にエラーが発生した際にエラーコードと一緒に現在実行中の命令に関する情報も一緒に呼び出し元に返すことで、正常系の速度低下を引き起こさずに正確なエラー情報を通知できるようになりました。

残っている問題は、エラー時に表示される命令は実行時の命令なので WASM のバイトコードではなく事前検証で変換された中間言語になっていて、一部の命令は元のバイトコードと違う命令になっているので元のバイトコードに戻したいと考えています。

f:id:neriring16:20210526112517p:plain

また、配列アクセスを合理化しました。

Rust は配列アクセスで必ず境界チェックが行われるため、 C 言語で同等のプログラムを書いた場合に比べると遅くなることがあります。*1

WebAssembly インタプリタはほぼ全てのバイトコード命令がなんらかの配列にアクセスします。 一方、 WebAssembly のバイトコードは基本的に事前検証しないと実行できないため、ほとんどの配列インデックスは事前検証で安全性を担保することができます。*2

Rust は unsafe の命令で境界チェックを行わない配列アクセスができます。 unsafe 命令は基本的に使う側が安全性を確認することが必要ですが、実行時にしか安全性を検証できない一部の配列インデックスをのぞいて、事前検証で安全性を確認しているほとんどの配列アクセスは直接アクセスで高速化できます。

これらの改修で改修前よりベンチが 40% 程度高速化できたようです。

rustdoc

MYOS もだいぶ大きくなってきました。 カーネル内部の各モージュール、共通ライブラリ、サンプルアプリなど・・・ どこにどんなクラスがあってどんな使い方をすればいいのか見通しが悪くなってきました。

幸い、 rust には rustdoc という自動的にドキュメントを生成する機能があるのでそれを利用します。

良い子は cargo doc というコマンドでドキュメントを作ることができますが、 MYOS はレポジトリの構造が若干複雑なので色々オプションを指定してあげる必要があります。

ローカルで何度か調整してドキュメントを作ることができるようになったら、次はサーバーにアップロードしてドキュメントを共有できるようにします。 しかし、ドキュメントを生成してサーバーにアップロードする作業は手作業のままだと効率が悪いし最新のアップデートに追従できなくなります。

Github には Github Action というプッシュした場合などに自動的にスクリプトを実行する機能があるので設定します。

.github/workflows/ に以下のような YAML を設定します。*3

name: Rustdoc

on:
  push:
    branches: [ "test/doc" ]

env:
  CARGO_TERM_COLOR: always

jobs:
  rustdoc:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Install Rust toolchain
      uses: actions-rs/toolchain@v1
      with:
        toolchain: nightly
        profile: minimal
        override: true
        components: rustfmt, rust-src

    - name: Build Documentation
      run: |
        cd system
        cargo doc --no-deps --target x86_64-unknown-none.json -p kernel -p megstd -p bootprot -p megosabi

    - name: Deploy Docs
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_branch: gh-pages
        publish_dir: ./system/target/x86_64-unknown-none/doc
        force_orphan: true

すると、 gh-pages というブランチに自動的にデプロイされて Github Pages の機能で HTML 公開されます。

meg-os.github.io

現在はドキュメントプッシュ用のブランチと開発のブランチが異なるので最新に追従しているわけではありませんが、 いずれブランチをもう少し整理して、マージするたびにドキュメントも自動で更新できるように考えています。

*1:コンパイル時に静的検証される場合と実行時にチェックされる場合があります

*2:WASMの仕様上、分岐命令はブロックのネストで表現されているので事前検証なしの実装は困難です。また、スタックの妥当性検証を事前検証しておくことで実行時のスタックチェックを省略できるなど事前検証した方がメリットがあります

*3:他のプロジェクトに転用する場合はこれをそのままコピペしてもうまく動かないので各自調整してください