古いグラボでもffmpegがしたい(openSUSE_TW,Geforce GTX 670)

FFmpeg on an Old GPU (OpenSUSE Tumbleweed, GeForce GTX 670)

はじめに

わたしにとってPCでの作業といえば、SSHで基本的にアクセスしてターミナル上で行うことがほとんどです。たまに有事の際にモニターに接続させて起動するぐらいしかGPUを使っていません。
そもそもPCでゲームをしないので、基本的にグラボが必要ない人間ではあるのですが、一応刺しておくかという感覚でGeforce GTX 670が入っています。長い間ほぼ刺さっているだけの状態だったので、これを使って何かできたらな~と探していたところ、FFmpegでGPUエンコーディングさせたら効率上がるんじゃないかと思い挑戦してみることにしました。
今回はこの古いGPUを使ってffmpegの処理を行わせるやり方を記事にしていきたいと思います。

使用したもの

Geforce GTX 670 
‣2012年6月に発売されたグラボで、当時は上位モデルのGTX680に劣らずの性能を発揮しコスパ抜群のグラボでした。 現在は中古が大体4000~6000円ぐらいで売られています。

OS:openSUSE TumbleWeed 
‣カメレオンのロゴマークがトレードマークのLinuxです。ローリングリリースモデル

ドライバーインストール

https://www.nvidia.com/ja-jp/drivers/ で製品名を入力
ドライバー バージョン:470.256.02とわかりました。

https://ja.opensuse.org/SDB:NVIDIA_drivers)から直接該当ファイルを選択してダウンロードしたりもできますが、私の場合、以下のURLでどのドライバーが適切かチェックをして大体確認しました。
https://download.nvidia.com/opensuse/tumbleweed/x86_64/

openSUSEの場合、以下の方法でドライバーダウンロードが可能です。

sudo zypper addrepo --refresh https://download.nvidia.com/opensuse/tumbleweed NVIDIA
sudo zypper in x11-video-nvidiaG05 
# インストール後、再起動
sudo reboot

# 再起動後、nvidia-smiコマンドで正常に認識されているか確認
nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 470.256.02   Driver Version: 470.256.02   CUDA Version:          |
|-------------------------------+----------------------+----------------------+
| 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 GeForce ...  Off  | 00000000:01:00.0 N/A |                  N/A |
| 30%   30C    P8    N/A /  N/A |      1MiB /  1998MiB |     N/A      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

nvidia-smiをみてもらうと、いくつかN/Aというものになっている部分が多いです。
これはエラーという意味ではなく、単純に対応していない・古いものの為、表示が出来ないという意味合いも持っています。
(参考:GPU Memory Usage shows “N/A”

色々なものが[N/A]とでていますが、コマンドが実行されて表示ができていればとりあえずOKです。次にcudaとGCCをダウンロードしていきます。

CUDA,GCCのインストール

残念ながらopenSUSE TumbleWeedにはcudaが正式に配布されていませんでした。今回は一番互換性が高いと思われるLeapで配布されているCUDAをインストールしていきます。CUDAのバージョンはドライバーのリリースノートを参考にしました。

zypper ar https://developer.download.nvidia.com/compute/cuda/repos/opensuse15/x86_64/cuda-opensuse15.repo
zypper --gpg-auto-import-keys refresh
# 今回はcuda 11.4をインストール
zypper -n in -y --auto-agree-with-licenses --no-recommends \
     nv-prefer-signed-open-driver cuda-runtime-11-4

このCUDAの適切なGCCのバージョンですが、以下のURLを参考にしました。
https://stackoverflow.com/questions/6622454/cuda-incompatible-with-my-gcc-version
開くのが面倒な人のために、ここに一応表を載せておきます。

CUDA バージョンGCCバージョン
12.4, 12.513.2
12.1, 12.2, 12.312.2
1212.1
11.4.1+, 11.5, 11.6, 11.7, 11.811
11.1, 11.2, 11.3, 11.4.010
119
10.1, 10.28
9.2, 10.07
9.0, 9.16
85.3
74.9
5.5, 64.8
4.2, 54.6
4.14.5
4.04.4

今回、CUDAのバージョンが11.4を使いましたので、GCC10をダウンロードしました。

sudo zypper install gcc10 gcc10-c++

nv-codec-headers,FFmpegをインストール

nv-codec-headersとは、NVIDIA GPUのビデオエンコーディングとデコーディングをサポートするためのヘッダーファイル群です。FFmpegとNVIDIAのハードウェアアクセラレーション機能を統合するために必要になってきます

それでは実際に、nv-codec-headersをインストールしていきます。sdk8.1を選択しました。※2012年のGTX 670に対して、比較的古いバージョンのSDKが適していると判断しました

git clone --branch sdk/8.1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git 
cd nv-codec-headers 
sudo make install

時期的にFFmpeg 4.4.x または FFmpeg 5.0初期リリースが互換性が高いと感じたので、今回は5.1をインストールしました。

git clone https://github.com/FFmpeg/FFmpeg.git
cd FFmpeg
git checkout release/5.1

FFmpegをインストールする前にGCC10に切り替えます。

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/g++-10 100
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100

FFmpegをインストール

CFLAGS="-O2 -march=native" ./configure --enable-nvenc --enable-cuda --enable-libnpp --enable-cuda-nvcc --enable-libvpx --enable-gpl --enable-version3 --extra-cflags="-I/usr/local/cuda/include -I/usr/local/include/ffnvcodec" --extra-ldflags="-L/usr/local/cuda/lib64" --cc=gcc-10 --cxx=g++-10 --nvcc=/usr/local/cuda/bin/nvcc --extra-libs="-lnppc -lnppig -lnpps"

CPU/GPUの速度比較

GPUを使ったものが以下です

# GPU
time ffmpeg -hwaccel cuda -i /mnt/hdd/movie/20241201_015957271.mp4 -c:v h264_nvenc -preset slow output_video.mp4
real 0m13.633s
user 0m8.239s
sys 0m0.555s

CPUを使ったものが以下です

# CPU
time ffmpeg -i /mnt/hdd/movie/20241201_015957271.mp4 -preset slow output_video.mp4
real	0m12.823s
user	0m27.100s
sys	0m0.437s

CPUとGPUの処理速度がほぼ同速という結果になってしまいました・・いくつかエンコーディング形式を試してみましたが、どれも同じような結果でした。ハードウェアアクセラレーションの設定が最適化されていない可能性がありますが、これが限界な気もします。
CPUの場合、エンコーディングが複数のコアを効果的に利用しているものの、GPUエンコーディングと比較してより多くのCPU時間を消費していることがわかります。
GPUの場合、user(8.239秒)がreal(13.633秒)より短いこともあり、これはGPUエンコーディングの方が、CPUリソースの消費を大幅に抑えながらエンコーディングを実行できたことを表しています。

まとめ

CPUと比較してGPUの方が処理速度が遅くなってしまうのは残念でしたが、GPUエンコーディングはCPUリソースの消費を大幅に抑えられることが分かりました。これは、特にマルチタスク環境や長時間のエンコーディング作業において重要な利点となり得ます。
そもそも2012年のグラフィックカードにしては、十分な性能だと思います。

今後は、以下のポイントに注目してさらなる最適化を試みていけたらと思っています。

  • FFmpegの設定パラメータの詳細な調整
  • CUDAとGCCのバージョン組み合わせの最適化
  • ハードウェアアクセラレーション関連の詳細な設定検証

おわり