Ryzen APU上のROCmでのメモリ割り当てに関する調査

# ROCmのHIPでのメモリ割り当て
https://qiita.com/asfdrwe/items/7ba1aa2251b3e34c9f8a

こちらでhipMallocをhipHostMallocに動的に置き換える[force-host-alloction-APU](https://github.com/segurac/force-host-alloction-APU)を利用してStable DifussionをRyzen APUで動作させています。

[ROCmの文書](https://rocblas.readthedocs.io/en/rocm-6.0.0/conceptual/gpu-memory.html)よりHIPのメモリ割り当てAPIについて、hipMallocではDevice(GPU)からメモリを取得し、hipHostMallocではHost(CPU)からメモリを取得するとなっています。

CPUもGPUもメインメモリを共有するUnified Memory Architecture(UMA)のRyzen APU上のROCmのHIPで、どの領域からどれくらいメモリ取得可能か不明瞭だったので、[HIP-ExcampleのHelloWorld](https://github.com/ROCm/HIP-Examples)を参考に、次の簡単なプログラムで調査してみました。

## 調査環境
|種類|内容|
|----------|---------------------------------|
|CPU       | Ryzen 5600G                     |
|マザーボード| ASRock B450M-HDV (BIOS 4.70)    |
|メモリ     | CFD W4U3200CS-16G (16GB×2=32GB) |
|OS        | Fedora 40 |

## 調査用プログラム

```
#include <hip/hip_runtime.h>
#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
    char *buffer[256];
    int i, j;

    for (i = 0 ; i < 256; i++) {
        if (hipMalloc((void**)&buffer[i], 128*1024*1024) == hipSuccess)
            cout << "ALLOCATE: " << (i + 1) * 128 << " MB" << endl;
        else {
            cout << "DO NOT ALLOCATE: " << (i + 1) * 128 << " MB" << endl;
            break;
        }
    }
    for (j = 0; j < i - 1; j++) {
        hipFree(buffer[j]);
    }
}
```

Memory.cppとして保存してください。128 MB単位でメモリを確保し、成功した場合(hipSuccess)は『ALLOCATE: これまで確保した容量』を表示し、失敗した場合は『DO NOT ALLOCATE: 確保しようとした容量』と表示します。最大128×256=32768 MB(32 GB)まで確保しようとします。もっと大きく確保したいなら`char *buffer[256];`と`    for (i = 0 ; i < 256; i++) {`の256を増やしてください。

### 調査用プログラムのビルド

`hipcc`等をHIPの開発環境を入れてください。Fedora 40なら

```
$ sudo dnf install clang hipcc lld lld-devel compiler-rt
```

です。

```
$ hipcc -o Memory Memory.cpp
```
でビルドしてください。

### force-host-allocation-APUのビルド

[force-host-alloction-APU](https://github.com/segurac/force-host-alloction-APU)もgitで取得してビルドしてください。

```
$ git clone https://github.com/segurac/force-host-alloction-APU
$ cd force-host-alloction-APU/
$ hipcc forcegttalloc.c -o libforcegttalloc.so  -shared -fPIC
$ cd ..
```

## BIOSでの手動のVRAM割り当てなしの場合
調査環境のメインメモリは32 GBでスワップが 8 GBです。

```
$ free
               total        used        free      shared  buff/cache   available
Mem:        32210192     9760280     4147096      427692    19197216    22449912
Swap:        8388604        6144     8382460
```

`dmesg`コマンドでカーネルログを見ると、VRAMが512 MBで[GTT](https://dri.freedesktop.org/wiki/GART/)が15727 MBとなっています。

```
$ sudo dmesg | grep amdgpu
...
[    4.585655] [drm] amdgpu: 512M of VRAM memory ready
[    4.585656] [drm] amdgpu: 15727M of GTT memory ready.
...
```

### hipMalloc
hipMallocはVRAMからメモリを確保します。調査用プログラムをKDE Plasma環境で実行すると

```
$ ./Memory
ALLOCATE: 128 MB
ALLOCATE: 256 MB
ALLOCATE: 384 MB
DO NOT ALLOCATE: 512 MB
```

384 MBまで確保に成功し512 MBの確保は失敗します。VRAM 512 MBに対してKDEもVRAMを使用しているので、512 MB確保できないのは正しい挙動だと思います。

### hipHostMalloc
force-host-alloction-APUを使ってhipMallocをhipHostMallocに置き換えて実行します。hipHostMallocはメインメモリからメモリを確保します。

```
$ LD_PRELOAD=./force-host-alloction-APU/libforcegttalloc.so ./Memory
ALLOCATE: 128 MB
ALLOCATE: 256 MB
ALLOCATE: 384 MB
ALLOCATE: 512 MB
ALLOCATE: 640 MB
ALLOCATE: 768 MB
ALLOCATE: 896 MB
...
ALLOCATE: 29184 MB
ALLOCATE: 29312 MB
DO NOT ALLOCATE: 29440 MB
```

29440 MBで確保に失敗します。30 GB弱までは確保できています。メインメモリが32 GBでスワップ 8 GBなので、他のプログラムが使用しているメモリを除くと30 GB程度で失敗したようです。

実際に確保したメモリをGPUが正しく使用できるかまではわかりませんが、ROCmのHIPでは16 GB以上確保できるようです。GTTの15727 MBは関係ないようです。

## BIOSで16 GB VRAMに割り当てた場合

```
$ sudo dmesg | grep amdgpu
...
[    4.326371] [drm] amdgpu: 16384M of VRAM memory ready
[    4.326372] [drm] amdgpu: 7927M of GTT memory ready.
...
```

VRAMが16384 MBでGTTが7927 MBです。

`Memory`を実行すると、

```
$ ./Memory
ALLOCATE: 128 MB
ALLOCATE: 256 MB
ALLOCATE: 384 MB
ALLOCATE: 512 MB
ALLOCATE: 640 MB
ALLOCATE: 768 MB
ALLOCATE: 896 MB
...
ALLOCATE: 16000 MB
ALLOCATE: 16128 MB
ALLOCATE: 16256 MB
DO NOT ALLOCATE: 16384 MB
```

16256 MBまで確保できて16384 MBの確保に失敗します。VRAMが16384 MBなので他のプログラムの使用分を考えると正しい挙動です。

force-host-alloction-APUを使ってhipMallocをhipHostMallocに置き換えて実行した場合、

```
$ LD_PRELOAD=./force-host-alloction-APU/libforcegttalloc.so./Memory
ALLOCATE: 128 MB
ALLOCATE: 256 MB
ALLOCATE: 384 MB
ALLOCATE: 512 MB
...
ALLOCATE: 13312 MB
ALLOCATE: 13440 MB
ALLOCATE: 13568 MB
ALLOCATE: 13696 MB
DO NOT ALLOCATE: 13824 MB
```

13824 MBで確保に失敗します。VRAMに16 GB割り当てているのでメインメモリが32 GBあっても16 GBしか使えないので他のプログラムの使用分を考えると正しい挙動だと思います。こちらもGTTの7927 MBとは関係なくメモリを確保しています。

## まとめ
BIOSでVRAMを割り当てた場合最大16 GBまでの制限があるようですが、hipHostMallocでメモリを確保する場合HIPプログラムはメインメモリの空き容量分メモリを確保できます。


外付けGPUの場合GPUのVRAMとメインメモリではアクセス速度に大きな違いがあり、hipMallocをhipHostMallocに置き換えるのは有効でないと思われますが、APUの場合は最初からメインメモリしかないのでhipMallocをhipHostMallocに置き換えてもあくせす速度は変わらないはずです。メインメモリは外付けGPUのVRAMと違って容易に増設可能なので、VRAMを多く必要とする大規模言語モデル等の実行にRyzen APUが役立つ可能性があります。