はじめに
この記事では、複数GPU環境でGPUを指定してUE製アプリを動かした時の困ったことを共有します。
自分と同じような初心者の方がやらざるを得なくなり、困ったときにちょっとでも参考になればと思います。
...ほぼはじめての記事ですのでお手柔らかにお願いします。
対象読者
複数GPUを積んだ環境で、指定したGPUを使用してUE製のアプリを起動させたい方。
指定して起動させる方法はわかったけどもなぜかうまく起動できなかった方。
実行環境
- OS: Linux(Debian11)
- Unreal Engine: 5.3.2、5.2.1
- GPU: NVIDIA T4、NVIDIA L4
- 実行サーバー: Google Cloud(Compute Engine)
やりたかったこと
クラウド上のサーバーに複数のGPUを配置し、Pixel Streamingを利用したUE製アプリケーションを同時に複数起動させる試みをしました。
(後述しますが、実はこの方法だと余計にお金がかかってしまったり、望んでいたパフォーマンスを出せなかったことから断念しています...)
当初、GPUの使用率が上がるにつれて勝手に使用するGPUを分散してくれるのかなと思っていました。
...が、もちろんそんなことはなく、1台目のGPUの負荷が最高に高まっていても一向に2台目のGPUを使用してくれませんでした。
そこで、何らかの追加実装か実行時の引数が必要なのかなと思い、今回調べることとなりました。
結論、どうすればよいのか?
こちらのフォーラムにあるように、実行時に-graphicsadapter
を引数として渡し、GPUを指定すれば良いみたいです。
${fileName} -graphicsadapter=${GPUのindex番号(※)} -RenderOffscreen -ResX=854 -ResY=480 -ForceRes
上記のような形で実行することで指定できます。
(※)正確には、0から始まるハードウェアマシン上の番号です。そのため、1台目なら0、2台目なら1になるのかなと思っています。こちらは、nvidia-smiやnvidia-smiの--query-gpuオプションで確認可能です。そのため、どのGPUを使用したいか確認する場合には、こちらから調べて指定すると良いと思います。
...本来は上記で良いのですが、「自分の環境」 では
# 2つ目のGPUを動作させる場合
${fileName} -graphicsadapter=2 -RenderOffscreen -ResX=854 -ResY=480 -ForceRes
というように、GPUのindex番号ではなく、index番号に+1する必要がありました(原因は後述します)。
実際にやってみる
謎の挙動の原因を探る前に実際に動くかどうかを確かめてみましょう。
今回どのような形で確かめているかと言いますと
- Linuxのサーバーのため、Linux用のクロスコンパイルを行い、Linux用のpackageファイルを作成する
- 何らかの方法でサーバーへファイルをデプロイ
- 前述の起動引数を指定して起動
- エラー発生の有無の確認およびnvidia-smiの実行
という形で確かめております。
# 1つ目のGPUを使用した場合
Wed Dec 20 16:40:33 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.105.17 Driver Version: 525.105.17 CUDA Version: 12.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 NVIDIA L4 On | 00000000:00:03.0 Off | 0 |
| N/A 51C P0 66W / 72W | 2153MiB / 23034MiB | 98% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
| 1 NVIDIA L4 On | 00000000:00:04.0 Off | 0 |
| N/A 39C P8 17W / 72W | 2MiB / 23034MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 10461 C+G ************** 2148MiB |
+-----------------------------------------------------------------------------
# 2つ目のGPUを使用した場合
Wed Dec 20 16:41:30 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.105.17 Driver Version: 525.105.17 CUDA Version: 12.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 NVIDIA L4 On | 00000000:00:03.0 Off | 0 |
| N/A 58C P8 31W / 72W | 6MiB / 23034MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
| 1 NVIDIA L4 On | 00000000:00:04.0 Off | 0 |
| N/A 45C P0 63W / 72W | 2153MiB / 23034MiB | 98% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 10703 G ************ 3MiB |
| 1 N/A N/A 10703 C+G ************ 2148MiB |
+-----------------------------------------------------------------------------+
どうやら、上記のログのようにうまく動いているようですね。
仮にgraphicsadapterに1を指定するとどうなるか
ということで、動かせることはわかったため、graphicsadapterに1を渡してみるとどうなるか見てみましょう。
// UE5.2.1の場合
$ ./MultiGPUTest_5_2_1.sh -graphicsadapter=1 -RenderOffscreen -ResX=854 -ResY=480 -ForceRes
5.2.1-0+UE5 1009 0
Disabling core dumps.
sh: 1: xdg-user-dir: not found
WARNING: lavapipe is not a conformant vulkan implementation, testing use only.
Failed to find symbol file, expected location:
"/***/***/***/***/Linux/MultiGPUTest_5_2_1/Binaries/Linux/MultiGPUTest_5_2_1-Linux-Shipping.sym"
LowLevelFatalError [File:.\Runtime/VulkanRHI/Private/VulkanUtil.cpp] [Line: 1017]
VulkanRHI::vkCreateQueryPool(Device->GetInstanceHandle(), &PoolCreateInfo, VULKAN_CPU_ALLOCATOR, &QueryPool) failed, VkResult=-8
at .\Runtime/VulkanRHI/Private/VulkanQuery.cpp:52
with error VK_ERROR_FEATURE_NOT_PRESENT
Signal 11 caught.
Malloc Size=262146 LargeMemoryPoolOffset=262162
CommonUnixCrashHandler: Signal=11
Engine crash handling finished; re-raising signal 11 for the default handler. Good bye.
Segmentation fault
// UE5.3.2の場合
$ ./MultiGPUTest_5_3_2.sh -graphicsadapter=1 -RenderOffscreen -ResX=854 -ResY=480 -ForceRes
5.3.2-0+UE5 1009 0
Disabling core dumps.
sh: 1: xdg-user-dir: not found
WARNING: lavapipe is not a conformant vulkan implementation, testing use only.
Unsupported intrinsic: vec1 32 ssa_173 = intrinsic load_subgroup_invocation () ()
Segmentation fault
実行すると上記のエラーに遭遇してしまい、うまく起動しませんでした。
なぜこのような現象に陥ったのか?
まず、graphicsadapterで指定した数値というのは、エンジンのVulkanRHI.cpp 612行目で使用されています。
その後、CVarExplicitAdapterValue
に格納された数値を利用して、VulkanAPIに対応したデバイスを格納するOriginalOrderedDevices
という配列から指定された数値番目の値を取得し、Device
およびDeviceIndex
を変数として格納しています。
このDevice
というのが、恐らくエラー発生箇所で使用しているデバイスなどに関係している箇所だと思われるのですが、原因はこのDeviceが何者なのか?という点です。
普通に考えれば、GPUなのかなと思いますので、一度OriginalOrderedDevices
に格納されている値を見てみましょう。
UE起動時にOriginalOrderedDevices
に格納されるデバイスについて、VulkanDevice.cpp 273-276行目に記述のあるログが吐き出されるため、これを確認してみましょう。
[2023.12.22-16.24.47:003][ 0]LogVulkanRHI: Display: Found 3 device(s)
[2023.12.22-16.24.47:003][ 0]LogVulkanRHI: Display: Device 0:
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - DeviceName: Tesla T4
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - API=1.3.224 (0x4030e0) Driver=0x835a4440 VendorId=0x10de
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - DeviceID=0x1eb8 Type=VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - Max Descriptor Sets Bound 32, Timestamps 1
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: Device 1:
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - DeviceName: llvmpipe (LLVM 11.0.1, 256 bits)
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - API=1.0.2 (0x400002) Driver=0x1 VendorId=0x10005
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - DeviceID=0x0 Type=VK_PHYSICAL_DEVICE_TYPE_CPU
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - Max Descriptor Sets Bound 8, Timestamps 1
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: Skipping device [llvmpipe (LLVM 11.0.1, 256 bits)] of type VK_PHYSICAL_DEVICE_TYPE_CPU (add -AllowCPUDevices to your command line to include it).
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: Device 2:
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - DeviceName: Tesla T4
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - API=1.3.224 (0x4030e0) Driver=0x835a4440 VendorId=0x10de
[2023.12.22-16.24.47:004][ 0]LogVulkanRHI: Display: - DeviceID=0x1eb8 Type=VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
すると、なんてことでしょう...
Device1を見てほしいのですが、こちらが2番目の情報ですね。
VK_PHYSICAL_DEVICE_TYPE_CPU
とあるように2番目がCPUに関するものだったのです。
VK_PHYSICAL_DEVICE_TYPE_CPU
が意味するのは、2番目のデバイスがCPUを使用してグラフィックス処理を行うことを示しています。
そして、3番目の値がGPUに関するものでした。
つまり、ここの値の影響でindex + 1しなければ、目的のGPUが使えなかったわけですね。
そして、なんらかの影響でCPUのグラフィックス機能では問題があったことからエラーとなっていたのだと考えられます。
まとめ
- GPUの指定は、
-graphicsadapter=${GPUのindex番号}
で行えます。 - ただし、
graphicsadapter
は、あくまでVulkanAPIに対応したデバイスを取得した際のリストの順番を指定するものであった。
そのため、今まで動かなくて困っていたという方は、もしかしたらGPU以外のデバイスが迷い込んでいるかもしれません。
補足事項
ちなみに、今回は特定のGPUを指定して実行、という感じでしたが、マルチGPUで動かす方法がUEでは提供されているみたいです。
たとえば、nDisplayを利用することで複数PCや場合によってはマルチGPUに対応することができたり、NVIDIA SLI Alternate Frame Renderingを利用することで複数GPUでレンダリングすることが可能みたいです。
いずれの方法もやりたいことと違っていたり、環境的な制約でうまくできなかったりしたため見送りました。
そのため、最終的には今回のように起動するUEアプリごとにGPUを指定し、振り分けて実行することとなりました。
余談
そもそも複数のUEアプリを動かしたときに、1つのGPUのみで6つほど安定して動かせていたのに、2つのGPUに振り分けると4つ動かしたら動きにカクツキが出てきたという現象に出会いました。
結局、時間的にも金額的に採用しないという結論に至ったため詳しい調査できずに終わってしまいましたが、なぜカクツキが出てきたのか調査はしてみたいなと思っています!
UEやGPU(ハード)関係への造詣を深めたいと思う今日この頃です...