Hyper-V上のUbuntuでCUDA版VOICEVOXエンジンを動かす

※2025/06/16　追記
AivisSpeechを動作させるのにCUDA ToolkitとcuDNNが必要だったので導入手順を追記

※2025/01/26　追記
Set-VMGpuPartitionAdapterの「-Passthru」指定では、動作しなくなったので修正

# GPU-PV

GPU-PVはゲストOS側の準仮想化ドライバを通して、ホストOS側のGPUドライバにアクセスさせる機能と理解した

生のデバイスを排他的に割り当てるPCIEパススルー的な機能ではなく、ホストOS側からは一つのプロセスがGPUを使用しているように見える

![{996E0A82-F2FD-4FC4-AB00-ED67C1A90AF6}.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124801/c87b247d-bce9-8bea-e187-527299618c8b.png)
画像引用：
https://developer.nvidia.com/blog/announcing-cuda-on-windows-subsystem-for-linux-2/

今のところ、Linuxの準仮想化ドライバについてはWSL2-Linux-KernelプロジェクトのdxgkrnlモジュールをHyper-V上のLinux仮想マシンにに組み込むアプローチをとることになる

# モチベーション
個人的に運用しているTTSBot(Discord用)の音声変換をVOICEVOXエンジン（GPU版）で処理している
TTSBotの本体はHyper-V上の仮想マシン上だが、音声変換はGPUを使う関係上物理マシンで処理している
気持ち悪い構成なので仮想マシンでVOICEVOXエンジン（GPU版）を使う方法を模索する

# 環境
 - CPU：Intel(R) Core(TM) i7-13700T
 - Mem：32GB
 - GPU：NVIDIA T1000 8GB
 - OS(Host)：Windows 11 Pro (23H2)
 - OS(Guest)：Ubuntu Server 24.04

# Hyper-V上で使えそうな技術の選定
 - RemoteFX → セキュリティ上の理由で廃止された
 - DDA (PCIEレベルのパススルー) → なんかうまくいかなかった
 - GPU-PV → うまくいったので紹介！

# Hyper-Vの仮想マシン(Ubuntu)の設定
 - VMの世代：第二世代
 - セキュアブート：無効
 - 動的メモリ：無効
 - チェックポイント：無効
 - 自動停止アクション：シャットダウン

# Ubuntu Server24.04 導入
特になし

# GPU-PVの有効化
ホスト機(Windows)側のPowershellで作業する
※ゲスト側は停止しておくこと

```powershell
# 既存のGPU-PVの設定を削除
Remove-VMGpuPartitionAdapter -VMName "仮想マシン名"

# デバイスファイルパス名の取得
Get-VMHostPartitionableGpu | Format-Table Name

# GPU-PVの設定
Add-VMGpuPartitionAdapter -VMName "仮想マシン名" -InstancePath "取得したデバイスファイルパス名"
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MinPartitionVRAM 1
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MaxPartitionVRAM 11
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -OptimalPartitionVRAM 10
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MinPartitionEncode 1
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MaxPartitionEncode 11
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -OptimalPartitionEncode 10
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MinPartitionDecode 1
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MaxPartitionDecode 11
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -OptimalPartitionDecode 10
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MinPartitionCompute 1
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MaxPartitionCompute 11
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -OptimalPartitionCompute 10
Set-VM -GuestControlledCacheTypes $true -VMName "仮想マシン名"
Set-VM -LowMemoryMappedIoSpace 1Gb -VMName "仮想マシン名"
Set-VM -HighMemoryMappedIoSpace 32GB -VMName "仮想マシン名"
```

下記は以前紹介した方法、
何時ごろからか-Passthru指定でのGPUリソース全割り当ては動作しなくなった

```powershell
Add-VMGpuPartitionAdapter -VMName "仮想マシン名" -InstancePath "取得したデバイスファイルパス名"
Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -Passthru
Set-VM -GuestControlledCacheTypes $true -VMName "仮想マシン名"
Set-VM -LowMemoryMappedIoSpace 1Gb -VMName "仮想マシン名"
Set-VM -HighMemoryMappedIoSpace 32GB -VMName "仮想マシン名"
```

※nvidia-smiを実行しても、下記のようなエラーになる

```
nvidia-smi

Failed to initialize NVML: GPU access blocked by the operating system
Failed to properly shut down NVML: GPU access blocked by the operating system
```

# ドライバ導入(ホスト機)
スクリプトをgithubに公開されている方がいらっしゃったので利用させてもらう
参考：
https://github.com/staralt/dxgkrnl-dkms

ひきつづき、ホスト機(Windows)側のPowershellで作業する
※ゲスト側は起動しておく

```powershell
$username="user name(ubuntu)"
$ip="ip address(ubuntu)"

# Create a destination folder.
ssh ${username}@${ip} "mkdir -p ~/wsl/drivers; mkdir -p ~/wsl/lib;"

# Copy driver files
# https://github.com/brokeDude2901/dxgkrnl_ubuntu/blob/main/README.md#3-copy-windows-host-gpu-driver-to-ubuntu-vm

(Get-CimInstance -ClassName Win32_VideoController -Property *).InstalledDisplayDrivers | Select-String "C:\\Windows\\System32\\DriverStore\\FileRepository\\[a-zA-Z0-9\\._]+\\" | foreach {
    $l=$_.Matches.Value.Substring(0, $_.Matches.Value.Length-1)
    scp -r $l ${username}@${ip}:~/wsl/drivers/
}

scp -r C:\Windows\System32\lxss\lib ${username}@${ip}:~/wsl/
```

WSL2でUbuntu-24.04を動作させているなら、「/usr/lib/wsl/」配下をそのままコピーしてもいい

# ドライバ導入(ゲスト機)
ゲスト機(Ubuntu)側で作業する

```bash
sudo mv ~/wsl /usr/lib/wsl
sudo chmod -R 555 /usr/lib/wsl
sudo chown -R root:root /usr/lib/wsl

sudo sh -c 'echo "/usr/lib/wsl/lib" > /etc/ld.so.conf.d/ld.wsl.conf'
ldsoconf

curl -fsSL https://content.staralt.dev/dxgkrnl-dkms/main/install.sh | sudo bash -es

# 確認
/usr/lib/wsl/lib/nvidia-smi
# /usr/lib/wsl/lib内のsoファイルのシンボリックリンクを手動作成してやらないとエラーになるかもしれない
```

# VOICEVOXエンジン導入
ひきつづき、ゲスト機(Ubuntu)側で作業する
※version-0.20.0を導入する例
```bash
cd /opt
sudo wget https://github.com/VOICEVOX/voicevox_engine/releases/download/0.20.0/voicevox_engine-linux-nvidia-0.20.0.7z.001
sudo wget https://github.com/VOICEVOX/voicevox_engine/releases/download/0.20.0/voicevox_engine-linux-nvidia-0.20.0.7z.002
sudo apt-get install -y p7zip-full
sudo 7z x voicevox_engine-linux-nvidia-0.20.0.7z.001
sudo mv linux-nvidia/ voicevox
```

# systemctlサービス登録（必要なら）

```bash
sudo vi /etc/systemd/system/voicevox.service
```

```
[Unit]
Description=Voicevox Service
After=network.target

[Service]
User=root
Group=root
WorkingDirectory=/opt/voicevox
ExecStart=/opt/voicevox/run --host=0.0.0.0 --port=50021 --use_gpu --load_all_models
Restart=always

[Install]
WantedBy=multi-user.target

```

```bash
sudo systemctl enable voicevox
sudo systemctl start voicevox
```

# 最後に
ひとまず、Ubuntuの仮想マシンがVOICEVOX（CUDA版）のAPIサーバーとして動作した
ホストであるWindows上のタスクマネージャーからもGPUを使用していることを確認できた
しばらく運用してみて問題が出ないか様子を見ることとする

# 追記(AivisSpeechを動かしてみた)
ほぼ同じ手順でAivisSpeech Engineも動くが、CUDA ToolkitとcuDNNの導入が必要だった
※CUDA ToolkitはWSL-Ubuntuを選択、cuDNNはUbuntu-24.04を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124801/779eacfe-b71a-48f6-b396-20f9b5d8491e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124801/8b8b0c3f-e956-4d1b-941e-379f9124399e.png)

※実際に使ったコマンド
```bash
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin
sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/12.9.1/local_installers/cuda-repo-wsl-ubuntu-12-9-local_12.9.1-1_amd64.deb
sudo dpkg -i cuda-repo-wsl-ubuntu-12-9-local_12.9.1-1_amd64.deb
sudo cp /var/cuda-repo-wsl-ubuntu-12-9-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-9

wget https://developer.download.nvidia.com/compute/cudnn/9.10.2/local_installers/cudnn-local-repo-ubuntu2404-9.10.2_1.0-1_amd64.deb
sudo dpkg -i cudnn-local-repo-ubuntu2404-9.10.2_1.0-1_amd64.deb
sudo cp /var/cudnn-local-repo-ubuntu2404-9.10.2/cudnn-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cudnn-cuda-12
```

# 参考
https://qiita.com/Hyper-W/items/e189790fd4534d9d51ad
https://qiita.com/Hyper-W/items/5ddfc93891f7b620da8a
https://ascii.jp/elem/000/004/019/4019541/
https://devblogs.nvidia.com/announcing-cuda-on-windows-subsystem-for-linux-2/