Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PyInstaller: DirectML版で良いGPUが使われるように、編集したbootloaderを使う #502

Closed
1 of 3 tasks
qryxip opened this issue Nov 2, 2022 · 5 comments · Fixed by #596
Closed
1 of 3 tasks
Labels
OS 依存:win Windows に依存した現象 優先度:中 重要 機能向上

Comments

@qryxip
Copy link
Member

qryxip commented Nov 2, 2022

内容

bootloaderをカスタムしてPyInstallerを使うことで、 VOICEVOX/voicevox_core#309 を解決します。

以下、 VOICEVOX/voicevox_core#309VOICEVOX/voicevox_core#322 での議論内容を簡潔にまとめます。

Discordでも話題に出たと思うのですが、現行のDirectML版のVOICEVOXにはそこそこ重大な問題があります。複数のGPUがあるPCでは統合GPUの方(例: Intel Graphics)が使われてしまう、という問題です。

この問題はC++実装のコアに特有のものではなく、Rust実装のものにおいても同様です。

ここでDirectMLがどのGPUを選択するのかを調べるべく、私の使っているAlienware m15 Ryzen Edition R5でNVIDIA Control Panelを開き、アプリケーションでどちらのGPUを使うかの設定を"Auto Select"にしてみます。

image

この状態でONNX Runtimeのドキュメントに従ってWin32 APIでGPUを列挙してみると、GPUの名前とメモリ量が得られます (結果は簡略化しています)。

0: AMD Radeon(TM) Graphics (VRAM: 0.48GiB)
1: NVIDIA GeForce RTX 3070 Laptop GPU (VRAM: 7.85GiB)
2: Microsoft Basic Render Driver (VRAM: 0.00GiB)

試しにNVIDIA Control PanelでGeForceを使うように設定すると、GeForceが最初に来るようになります。

0: NVIDIA GeForce RTX 3070 Laptop GPU (VRAM: 7.85GiB)
1: AMD Radeon(TM) Graphics (VRAM: 0.48GiB)
2: Microsoft Basic Render Driver (VRAM: 0.00GiB)

コアでDirectMLのONNX Runtimeを使う際、現在はこの0番目(0-based)のGPUを指定しています。

選択肢1: コア側で一番メモリが多いGPUを選択する

ここで0番目ではなくメモリが最大のデバイスを指定する、というのが考えられる一つ目の選択肢です。

選択肢2: エンジン側でCのグローバル変数を定義する

ユーザーにNVIDIA Control Panelを操作させる以外にも、デバイス情報の順番を変化させる方法があります。Cレベルでグローバル変数を定義するとGeForceが最初に来るようになります。 グローバル変数を定義して強いGPUが0番目に来るようにしてから、それを選ぶというのが二つ目の選択肢です。

ただこっちの方法だと、コア側で対処することはできません。プロセスの開始時にドライバーから読める必要があるからです。VOICEVOXの場合はPyInstaller側で対応する必要があります。

比較

これで2つの選択肢があるわけですが、この2つを比較してみます。

  1. UX

    まず「大多数の人が意図した挙動になり、意図しない挙動をする場合は変更可能になっている」というのが理想ではあります。

    しかしどちらの方法においても、ユーザー側から挙動を変更することはできません (グローバル変数を宣言した場合、NVIDIA Control Panelから統合GPUの方を使うことはできなくなります)。その点で両者に違いはあまり無いと考えられます。

    コア側での対応だとユーザーが選択できるように将来拡張できるのですが、エディタのUIまで考えて実装しなければならないことを考えるとコストが大きいかと思われます。

    そもそも外付けGPUではなく統合GPUの方を使いたいという人がさほどいないであろうことを考えると、ユーザー側での変更可能性はUXにはあまり影響しないという見方もできます。

  2. 実装コスト & メンテナンス性

    コア側での対応だと、ユーザーの環境の多様性を考えると不安が残ります。今はまだx86_64のWindowsのDirectMLに対応するだけなのですが、将来サポート対象を拡大することを考えると、これだけのOSに対し

    • Windows x86_64 (第一級サポート)
    • Windows aarch64
    • macOS x86_64
    • macOS aarch64
    • Linux x86_64
    • Linux aarch64
    • Android?
    • iOS/iPadOS?

    これだけの数のEP (execution provider)があることも考えなくてはなりません。

    一方でエンジン側でやるときのコストですが、あまりイメージが掴めていません。このissue内で伺えたらと思っています。

以上より実装してメンテするコストが低いのであれば、PyInstaller側でグローバル変数を定義するのがいいのではないか、というのが私と @Hiroshiba さんの考えです。

実現方法

bootloaderをビルドする方法が公式で説明されているため、以下のコードを加えたbootloaderを使うようにすればいいのではないかと考えています。

+#ifdef _WIN32
+_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+_declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
+#endif

やっている人の例

OSの種類/ディストリ/バージョン

  • Windows
  • macOS
  • Linux

その他

#439

@github-actions github-actions bot added the OS 依存:win Windows に依存した現象 label Nov 2, 2022
@Hiroshiba
Copy link
Member

詳細なissueありがとうございます!!

PyInstallerをforkしてmain.cを書きかえ、そのPyInstallerをビルドに使用する必要があるという感じでしょうか。
たしかにメンテコストは多少掛かりますが、通るのが難しい道ではない気がするので、やってみたいです!

まあまずはPyInstallerがmainブランチに入ってからかなと思います。
あるいは https://github.com/VOICEVOX/voicevox_engine/tree/pyinstaller にPR送っても良いかも・・・?

とりあえずforkしてみました!デフォルトのdevelopブランチでリリースしてるっぽい・・・?
https://github.com/VOICEVOX/pyinstaller

@Hiroshiba
Copy link
Member

こちらのissue、pyinstaller版がマージされたので続行できそうですね!

GPU版VOICEVOX遅くない?みたいなツイートをちょくちょく見かけます。結構損していそうなので、優先度ラベルを中にしたいと思います。
https://twitter.com/118Og_Natrium/status/1604807079168204800

@y-chan
Copy link
Member

y-chan commented Jan 28, 2023

bootloaderに変更を加え、高性能グラフィックスがデバイス一覧の先頭に来るように制御できたVOICEVOX Engineがビルドできました。
Google Drive

ローカルでIssueに書かれている通りの手法でビルドしたものとなります。

一応、詳細な手段を書いておきます。

  1. VOICEVOX Engineのビルド環境を整えます(pip install -r requirements-dev.txtなど)
  2. PyInstallerをクローンし、requirements-dev.txtに書かれているとおりのバージョン(v5.3タグ)をチェックアウトします。
  3. PyInstallerのbootloader/src/main.cの28行目あたりに、追記すべき事項を追記します。
  4. bootloaderディレクトリでpython ./waf allとし、bootloaderをビルドします。この時、必要であればターゲットアーキテクチャを指定します(--target-arch=64bitなど、多分GitHub Actions上では必要ないです)
  5. PyInstallerの最上層でpip install -U .とし、既存の(pip install -r requirements-dev.txtでインストールした)PyInstallerを上書きインストールします。このように上書きインストールはできるので、requirements-dev.txtに手は加えなくていいかなと思います。
  6. あとは通常通りVOICEVOX Engineをビルドします。

以上です。

ここまでの議論では、VOICEVOX Org側でPyInstallerをフォークして保守することが挙げられていましたが、2行の変更で済むので、このリポジトリ上でパッチファイルを保持し、GitHub Actions上で実行する方が保守性が高いかも?と思いました。

@Hiroshiba
Copy link
Member

その形でもいいのかなと思いました!
パッチとBashスクリプト書くだけなので、たしかに取り回しはずっと簡単そうですね!!

@qryxip
Copy link
Member Author

qryxip commented Jan 28, 2023

パッチというより、独立した.cファイルを放り込めばちゃんとコンパイルしてリンクしてくれるみたいです。

#ifdef _WIN32
#include <windows.h>
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
#endif

qryxip added a commit to qryxip/voicevox_engine that referenced this issue Jan 29, 2023
以下のコメントが非常に参考になった。 @y-chan に感謝する。
VOICEVOX#502 (comment)

Co-authored-by: Yuto Ashida <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
OS 依存:win Windows に依存した現象 優先度:中 重要 機能向上
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants