今までgemma4:31bと26b-a4bメインで遊んでいたけど、何となくメモリをギリギリまで使って大きめのモデルを動かしてみたくなった。
DGX Spark x 2台で動かせるみたいな話はXで見たけど、量子化タイプを選べばうちのEVO-X2でも動くのでは?という気がして試してみた記録。
結論
EVO-X2(Ryzen AI Max+ 395 / 128GB)、OSはデフォルトのWindows11のまま、VRAM割り当ては64GBの環境で、Qwen3.8-Flash-Next(unsloth UD-IQ1_SとUD-IQ3_XXS)を動かそうとした。
llama.cpp b10679 の Windows x64 (Vulkan)版では途中でクラッシュして推論できなかったが、Windows x64 (ROCm 7.14)版では推論までできた。
今回の検証環境
Hardware
- Device: NucBox_EVO-X2
- CPU / APU: AMD Ryzen AI MAX+ 395 with Radeon 8060S
- CPU clock displayed by Windows: 3.00 GHz
- Installed memory: 128 GB
- Windows usable memory: 63.6 GB
- UMA Frame Buffer: 64 GB
- GPU: AMD Radeon 8060S Graphics
- Storage: 1.82 TB total / 732 GB used / 約1.09 TB free
OS
- OS: Windows 11 Pro
- Version: 25H2
- OS Build: 26200.9168
- Installed: 2025-10-24
- Windows Feature Experience Pack: 1000.26100.344.0
llama.cpp
- b10679: Windows x64 Vulkan
- b10679: Windows x64 ROCm 7.14
HIP SDK(これは後述するように、Vulkan版クラッシュの後でインストールしたもの)
- HIP SDK: 7.2.60201-38d754472
- HIP compiler: clang 21.0.0git
- HIP target: gfx1151
使用したモデル
- Repository: unsloth/Qwen3.8-Flash-Next-GGUF
- Quantization: UD-IQ3_XXS(配布ページ上は約82GB、llama.cppログでは76.32GiB)
UD-IQ1_S (配布ページ上は約72.5GB、llama.cppログでは67.55GiB) - URL: https://huggingface.co/unsloth/Qwen3.8-Flash-Next-GGUF
※ 2026-09-02 訂正: 初出時、UD-IQ1_SとUD-IQ3_XXSのログ上のサイズを逆に記載していたため修正しました。
うちの通信環境が貧弱すぎて、モデル2つダウンロードするのに5時間ぐらいかかった。次からは寝る前にダウンロードを仕込んでおこう。
llama.cppの準備
Qwen3.8-Flash-Nextは新しいモデルなので、検証当日(2026/8/29)の朝見たときの最新版 b10679 を選択。
今までEVO-X2ではWindows x64 (Vulkan)版+gemma4を使っていたので、今回も最初は(Vulkan)版を選択。
https://github.com/ggml-org/llama.cpp/releases
ここからzipファイルをダウンロードして、適当な場所に展開するだけ。
なのだけど、後でWindows x64 (ROCm 7.14)版に変更することになる。
Vulkan版で起きたクラッシュ
モデルとllama.cppの準備ができたら早速動かしてみる。最初は UD-IQ1_Sから、モデルが大きいのではコンテキストサイズなど欲張らずに控えめに、llama.cppを展開したフォルダでpowershellを起動して
.\llama-server.exe `
-m (モデルのパス)\Qwen3.8-Flash-Next-UD-IQ1_S-00001-of-00003.gguf `
-c 4096 `
-np 1 `
-ngl 999 `
--host 127.0.0.1 `
--port 8081 `
-lv 4
起動ログを見ていると、1分もしないうちに
llama_server: model loaded
llama_server: listening on http://127.0.0.1:8081
まで出て、意外とすんなり起動できた!?と思ったけど、そんなに甘くはなかった。
$Body = @{
model = "Qwen3.8-Flash-Next"
messages = @(
@{
role = "user"
content = "こんにちは。日本語で一文だけ自己紹介してください。"
}
)
temperature = 0.2
max_tokens = 256
stream = $false
} | ConvertTo-Json -Depth 6
$response = Invoke-RestMethod `
-Method Post `
-Uri "http://127.0.0.1:8081/v1/chat/completions" `
-ContentType "application/json; charset=utf-8" `
-Body $Body
$response | ConvertTo-Json -Depth 10
こんな感じで一言だけのシンプルなプロンプトを投げてみると、
0.34.206.258 I srv llama_server: listening on http://127.0.0.1:8081
0.34.206.697 I srv update_slots: all slots are idle
1.12.785.901 I srv server_strea: conv_id= (empty=1)
1.12.786.979 I srv operator(): chat format: peg-native
1.12.787.382 I slot get_availabl: id 0 | task -1 | - skipping, slot is empty
1.12.787.393 I slot get_availabl: id 0 | task -1 | selected slot by LRU, t_last = -1
1.12.787.394 I srv get_availabl: updating prompt cache
1.12.787.543 I srv load: - looking for better prompt, base f_keep = -1.000, f_sim = 0.000
1.12.787.549 I srv update: - cache state: 0 prompts, 0.000 MiB (limits: 8192.000 MiB, 4096 tokens, 8589934592 e
st)
1.12.787.551 I srv get_availabl: prompt cache update took 0.16 ms
1.12.787.704 I slot launch_slot_: id 0 | task -1 | sampler chain: logits -> ?penalties -> ?dry -> ?top-n-sigma -> top-
k -> ?typical -> top-p -> min-p -> ?xtc -> temp-ext -> dist
1.12.787.720 I slot launch_slot_: id 0 | task -1 | sampler params:
repeat_last_n = 64, repeat_penalty = 1.000, frequency_penalty = 0.000, presence_penalty = 0.000
dry_multiplier = 0.000, dry_base = 1.750, dry_allowed_length = 2, dry_penalty_last_n = 64
top_k = 20, top_p = 0.950, min_p = 0.050, xtc_probability = 0.000, xtc_threshold = 0.100, typical_p = 1.000, top_n_sig
ma = -1.000, temp = 0.200
mirostat = 0, mirostat_lr = 0.100, mirostat_ent = 5.000, adaptive_target = -1.000, adaptive_decay = 0.900
1.12.787.723 I slot launch_slot_: id 0 | task 0 | processing task, is_child = 0
1.12.787.731 I slot operator(): id 0 | task 0 | new prompt, n_ctx_slot = 4096, n_keep = 0, task.n_tokens = 62
1.12.787.803 I slot operator(): id 0 | task 0 | cached n_tokens = 0, memory_seq_rm [0, end)
ここまで来てエラーも吐かずにサーバーが落ちる。
2回ぐらい試したけど結果は同じで、試しに起動オプションに -fa off をつけると、今度は起動の途中で落ちる。手元にあった別バージョンのlllama.cpp b10665のVulkanでも落ちる。
この間メモリの使用量はずっと監視していたけれど、メモリ容量にはまだ余裕があり、メモリ不足で落ちているわけではなさそう。
AIの助けを借りて、色々調べてみると、Windowsのイベントログにアプリケーションエラーが出ている。
- Faulting module: amdvlk64.dll
- Driver version: 32.0.12078.30
- Exception code: 0xc0000409
AIに調べてもらったところ
- amdvlk64.dllはAMDのWindows Vulkanドライバ
- 最初のprompt eval、すなわち62トークンの入力を処理するために、chunked Gated Delta Netを含む最初のGPU compute graphを実行しようとした地点で落ちているように見える
- fused Gated Delta Net、Lightning Indexer、DeepSeek V4 HC などを含むQwen3.8-Flash-NextのGPU compute graphを、Windows版AMD Vulkanドライバが処理するところで落ちている可能性がある
ということで、素人にはたぶん直しようがない。
ROCm版を試してみる
Vulkan版がダメならROCm版を試してみよう、ということで、Vulkan版と同じllama.cpp b10679の Windows x64 (ROCm 7.14) のzipをダウンロードして展開。起動する前にGPUを認識できているか確認したら、ダメっぽい。
.\llama-server.exe --list-devices
Available devices:
(none)
ROCm版は過去に一度も使ったことがなかったので、必要なライブラリが足りていなくて、AMDのHIP SDKをインストールする必要があるらしい。
AMDのHIP SDKのダウンロードページ
https://www.amd.com/en/developer/resources/rocm-hub/hip-sdk.html
を見に行くと、Windows版で新しめのものは7.2と7.1.1がある。けど、llama.cppのzipにはROCm 7.14って書いてなかったっけ??バージョン違ってて大丈夫なのか??
これもAIと相談したら、バージョンが違って動くかは分からないけど、HIP SDK7.2をインストールして、llama.cppのプレビルド版が動かなければ自分でソースからビルドすればよい、ということになり、EVO-X2には開発環境は入れてなかったけど、この機会にVisual Studio Build Toolsでも入れるか~、それよりVRAM割り当て32GBに変えてCPUで動かしてみる方が楽か?とか考えながら、まずはHIP SDK7.2をインストールしてみる。
HIP SDKをインストールしてROCm版が動くようになる
HIP SDK7.2のインストーラをダウンロードして、
https://rocm.docs.amd.com/projects/install-on-windows/en/latest/install/install.html
ここの案内に従ってインストールして環境変数を設定して、確認コマンドの
hipInfo
hipconfig
を打ってみる。hipInfoの方はGPUが認識されていそうな情報が出る。
device# 0
Name: AMD Radeon(TM) 8060S Graphics
pciBusID: 197
...
hipconfigの方は何か所か、
'C:\Program' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
とか変なメッセージが出ているが、スペースを含むパスでおかしくなっているだけかも?
さっき失敗した、llama-serverのGPU認識は成功している
.\llama-server.exe -lv 5 --list-devices
0.00.051.123 I load_backend: loaded ROCm backend from C:\llama-b10679-rocm\ggml-hip.dll
0.00.051.329 I load_backend: loaded RPC backend from C:\llama-b10679-rocm\ggml-rpc.dll
Available devices:
0.00.060.593 I load_backend: loaded CPU backend from C:\llama-b10679-rocm\ggml-cpu-zen4.dll
ROCm0: AMD Radeon(TM) 8060S Graphics (102129 MiB, 101968 MiB free)
ROCm0としてRadeon 8060Sを認識できたので、検証はそのまま続行
一度サーバーを起動してみよう、ということでROCm版のllama.cppを展開したフォルダでVulkan版と同じ起動オプションで起動
.\llama-server.exe `
-m (モデルのパス)\Qwen3.8-Flash-Next-UD-IQ1_S-00001-of-00003.gguf `
-c 4096 `
-np 1 `
-ngl 999 `
--host 127.0.0.1 `
--port 8081 `
-lv 4
起動ログを見ていると、Vulkan版よりちょっと時間がかかって
llama_server: model loaded
llama_server: listening on http://127.0.0.1:8081
が表示された。起動ログには
- ROCm0 : AMD Radeon(TM) 8060S Graphics (102129 MiB, 101968 MiB free)
も表示されているので、GPUも認識されている。
Vulkan版はこの次で失敗したので、恐る恐るさっきと同じ「こんにちは。日本語で一文だけ自己紹介してください。」という一言プロンプトを投げてみると、ちゃんと応答が返って来た!これでとりあえず、「Qwen3.8-Flash-Nextを動かす」という第1目標は達成。
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"role": "assistant",
"content": "こんにちは、私はAIアシスタントとして、あなたの質問に答えたり、文章を書いたり、考えを整理したりするお手伝いをするためにここに来ました。",
"reasoning_content": "The user is asking me to introduce myself in Japanese with just one sentence. They said \"こんにちは\" (hello) and asked me to give a one-sentence self-introduction in Japanese. Let me craft a natural, concise self-introduction in Japanese.\n"
}
}
],
"created": 1787999514,
"model": "(モデルのパス)\\Qwen3.8-Flash-Next-UD-IQ1_S-00001-of-00003.gguf",
"system_fingerprint": "b10679-50f068fff",
"object": "chat.completion",
"usage": {
"completion_tokens": 88,
"prompt_tokens": 62,
"total_tokens": 150,
"prompt_tokens_details": {
"cached_tokens": 0
}
},
"id": "chatcmpl-qX5ixN5KeabYJG0AfohZteQbHYJBGXpG",
"timings": {
"cache_n": 0,
"prompt_n": 62,
"prompt_ms": 740.622,
"prompt_per_token_ms": 11.945516129032258,
"prompt_per_second": 83.71341926110756,
"predicted_n": 88,
"predicted_ms": 2829.484,
"predicted_per_token_ms": 32.522804597701146,
"predicted_per_second": 30.74765575631458
}
}
Qwen3.8-Flash-Nextの出力の特徴
最初に何も設定せずにリクエストを投げると、レスポンスには reasoning_content が付いていて、内部思考っぽいテキストが結構長く出る。起動ログにも
Reasoning effort is set to xhigh.
と出ている。
最初は max_tokens: 256 のまま、Pythonコードを書いてもらったり、文章要約をしてもらったりしたが、finish_reason が length になって、最終回答の content が空のまま reasoning_content だけで256トークンを使い切ることがあった。
"finish_reason": "length",
"content": "",
"completion_tokens": 256
これだと、単純な要約でも「考えている途中」で出力が切れてしまう。たぶんこのモデルを普段使いするなら、max_tokens はreasoningと最終回答を合わせた上限として考えた方がよさそう。
今回は通常の短いチャット、要約、コード生成では、リクエスト側でthinkingを切った。
chat_template_kwargs = @{
enable_thinking = $false
}
これで reasoning_content なしに最終回答が返るようになった。
ただ、コード生成はthinkingを切っても出力自体が長い。max_tokens: 512 では実行例の終わり付近で切れたので、max_tokens: 768 または 1024 にした。今回のPythonコード生成では、IQ1_Sは541 tokens / finish_reason: stop、IQ3_XXSは723 tokens / finish_reason: stop で最後まで出力された。
IQ1_SとIQ3_XXSのメモリ使用量と速度比較
今回は、まず動くかどうかを確認することを優先して、以下の条件を固定した。
- llama.cpp: b10679 / commit
50f068fff - backend: ROCm
- context:
-c 4096 - parallel slots:
-np 1 - GPU layers:
-ngl 999 - Flash Attention: auto(ログ上では enabled)
- CPU threads: 16
- thinking: 基本テストでは off
-ngl 999 を指定した結果、両方とも49/49 layersがGPUへoffloadされた。ただし、モデル全体がGPU専用メモリだけに置かれるわけではなく、CPU model bufferも確保される。
| 項目 | UD-IQ1_S | UD-IQ3_XXS |
|---|---|---|
| llama.cppログ上のGGUFサイズ | 67.55 GiB | 76.32 GiB |
| GGUF file type | IQ1_S - 1.5625 bpw | IQ3_XXS - 3.0625 bpw |
| GPU offload | 49 / 49 layers | 49 / 49 layers |
| CPU model buffer | 27,465.95 MiB | 27,465.95 MiB |
| ROCm model buffer | 41,368.28 MiB | 50,191.17 MiB |
| ROCm Host model buffer | 341.02 MiB | 497.31 MiB |
| ROCm compute buffer | 188.07 MiB | 188.07 MiB |
| 4K context時のKV buffer | 96.00 MiB + 36.00 MiB | 96.00 MiB + 36.00 MiB |
| recurrent state buffer | 112.57 MiB | 112.57 MiB |
IQ3_XXSはIQ1_Sより、ROCm model bufferが約8.6GiB増えた。この差はGGUFサイズの差とほぼ同じ。
PowerShellからWindows Performance Counterを2秒間隔で取って、OS RAM、llama-serverプロセス、GPU Adapter Memoryを監視した。GPU Local Usage と GPU Adapter Total Committed は、IQ1_Sで約42GiB前後、IQ3_XXSで約51GiB前後まで上がった。
EVO-X2はUMAなので、Windows側のRAM、GPU Local / Dedicated / Shared、プロセスのPrivate Memoryは、同じ物理メモリを別のレイヤーで数えている可能性がある。これらを足して128GBと比較することはできない。ここでは、同じ監視スクリプト・同じ条件での量子化間の相対比較として見ている。
生成速度は、thinking offで短い実用プロンプトを数件試した範囲では、IQ1_Sのdecodeが約30.3 tok/s、IQ3_XXSが約27.0 tok/sだった。今回の比較可能な長めのコード生成では、IQ3_XXSはIQ1_Sより約11%遅い。
| タスク | UD-IQ1_S | UD-IQ3_XXS |
|---|---|---|
| Pythonコード生成のdecode | 30.33 tok/s | 27.07 tok/s |
| EVO-X2向け検証手順のdecode | 30.39 tok/s | 27.16 tok/s |
このぐらいならIQ3_XXSの方を常用しても待ち時間はあまり気にならなさそう。今回は簡単な日本語タスクだけだけど、IQ1_SでもIQ3_XXSでも明確な文字化けや意味崩壊は見なかった。
ただし、IQ1_Sはモデル全体を一律に1bit化したものではない。ログには f32、bf16、q8_0、q6_K、q5_K、iq4_nl なども混在している。単純な「1bit量子化モデル」と比較するのは注意が必要そう。
日本語応答・要約・コード生成の確認
ベンチマークというほどではないけど、シンプルなタスクをいくつか試した。
- 日本語で一文だけ自己紹介
- 短い技術文章の箇条書き要約
- EVO-X2の環境を指定した初回検証手順の提案
-
pathlib、例外処理、UTF-8 BOM CSV、型ヒントを指定したPythonコード生成
短い自己紹介や要約はIQ1_SとIQ3_XXSの両方で問題なく終了した。finish_reason は stop だった。
Pythonコード生成では、指定ディレクトリ以下を再帰走査し、拡張子ごとのファイル数と合計バイト数をCSVに出力するスクリプトを生成してもらった。IQ1_Sではmax_tokens: 768、IQ3_XXSではmax_tokens: 1024で最後まで出力できた。
生成したコードは、.py ファイルに保存し、次で構文確認した。
py -m py_compile .\extension_stats_generated.py
その後、コードを保存したフォルダを対象に実行して、CSVが出力されることも確認できた。
Extension,File Count,Total Bytes
.py,1,2045
.pyc,1,4123
.txt,5,4019
少なくとも今回の小さなテストでは、「それっぽいコードを出した」だけでなく、保存・構文確認・実行・CSV出力までできた。
次回以降にやること
- パラメータを調整したり、MTPを入れたりして高速化したい
- reasoningの効果を確認したい
- 日本語の長文をどこまで扱えるのか、推論は正確なのかを確認したい
- Open WebUIからのチャット(主に長文読解支援)とOpenCodeでのコーディングにどこまで使えるか確認したい
- HIP SDK 7.2でllama.cppを自前ビルドして、ROCm 7.14のプレビルド版との差も見たい
付録(詳細データなど)
llama-server起動コマンド
.\llama-server.exe `
-m "(モデルのパス)\Qwen3.8-Flash-Next-UD-IQ3_XXS-00001-of-00003.gguf" `
-c 4096 `
-np 1 `
-ngl 999 `
--host 127.0.0.1 `
--port 8081 `
-lv 4
テストに使ったプロンプト
$prompt = "こんにちは。日本語で一文だけ自己紹介してください。"
$prompt = @"
次の文章を3点の箇条書きで要約してください。
ローカルLLMの性能評価では、生成速度だけでなく、
モデルロード時間、入力処理速度、メモリ使用量、
長文コンテキストでの安定性、および実際の作業品質を
分けて確認する必要がある。
"@
$prompt = @"
128GBのユニファイドメモリを持つPCで、
約68GiBのモデルをローカル実行する場合の
安全な初回検証手順を、5項目で提案してください。
"@
$prompt = @"
Pythonで、指定ディレクトリ以下を再帰的に走査し、
拡張子ごとのファイル数と合計バイト数をCSVへ出力する
実行可能なスクリプトを書いてください。
要件:
- pathlibを使用
- PermissionErrorおよびOSErrorは無視して継続
- CSVはUTF-8 with BOM(utf-8-sig)
- 型ヒントを付ける
- 拡張子なしは "(no extension)" とする
- 実行例を含める
- コードのみを出力する
- 説明文は不要
"@
リクエストコマンド
$Body = @{
model = "Qwen3.8-Flash-Next"
messages = @(
@{
role = "user"
content = ${prompt}
}
)
chat_template_kwargs = @{
enable_thinking = $false
}
temperature = 0.2
top_p = 0.8
max_tokens = 768
stream = $false
} | ConvertTo-Json -Depth 6
$response = Invoke-RestMethod `
-Method Post `
-Uri "http://127.0.0.1:8081/v1/chat/completions" `
-ContentType "application/json; charset=utf-8" `
-Body $Body
# 出力を全部表示
$response | ConvertTo-Json -Depth 10
# 本文のみ表示
$response.choices.message.content
Qwen3.8-Flash-Next IQ3_XXSが書いてくれたpythonコード
#!/usr/bin/env python3
"""指定ディレクトリ以下を再帰的に走査し、拡張子ごとのファイル数と合計バイト数をCSVへ出力する。"""
import csv
import sys
from collections import defaultdict
from pathlib import Path
from typing import Dict, Tuple
def scan_directory(root: Path) -> Dict[str, Tuple[int, int]]:
"""指定ディレクトリ以下を再帰的に走査し、拡張子ごとのファイル数と合計バイト数を返す。
Args:
root: 走査対象のルートディレクトリ
Returns:
拡張子をキー、(ファイル数, 合計バイト数) を値とする辞書
"""
stats: Dict[str, Tuple[int, int]] = defaultdict(lambda: (0, 0))
try:
for path in root.rglob("*"):
try:
if path.is_file():
# 拡張子を取得
suffix = path.suffix
if not suffix:
ext_key = "(no extension)"
else:
ext_key = suffix
# ファイルサイズを取得
try:
size = path.stat().st_size
except (PermissionError, OSError):
continue
# 統計を更新
count, total_size = stats[ext_key]
stats[ext_key] = (count + 1, total_size + size)
except (PermissionError, OSError):
continue
except (PermissionError, OSError):
pass
return dict(stats)
def write_csv(stats: Dict[str, Tuple[int, int]], output_path: Path) -> None:
"""統計情報をCSVファイルに書き込む。
Args:
stats: 拡張子をキー、(ファイル数, 合計バイト数) を値とする辞書
output_path: 出力先CSVファイルパス
"""
with open(output_path, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(["extension", "file_count", "total_bytes"])
for ext, (count, total_size) in sorted(stats.items()):
writer.writerow([ext, count, total_size])
def main() -> None:
"""メイン関数。"""
if len(sys.argv) < 2:
print("Usage: python script.py <directory> [output.csv]")
sys.exit(1)
root_path = Path(sys.argv[1])
if not root_path.exists():
print(f"Error: Directory '{root_path}' does not exist.")
sys.exit(1)
if not root_path.is_dir():
print(f"Error: '{root_path}' is not a directory.")
sys.exit(1)
if len(sys.argv) >= 3:
output_path = Path(sys.argv[2])
else:
output_path = Path("file_stats.csv")
stats = scan_directory(root_path)
write_csv(stats, output_path)
print(f"Scanned directory: {root_path}")
print(f"Output written to: {output_path}")
print(f"Total extensions found: {len(stats)}")
if __name__ == "__main__":
main()
注意
- 本記事の数値は、Windows 11、EVO-X2、llama.cpp b10679、ROCm 7.14 prebuilt、HIP SDK 7.2の組合せでの結果
-
-ngl 999、context 4K、slot 1での初期確認であり、最適設定を探索した結果ではない - ロード時刻はWindowsのファイルキャッシュの影響を分離していない
- 長文context、MTP、speculative decoding、PLE / N-gram embedding配置、KV cache設定は未検証
- Windows UMA環境のメモリcounterは重複計上を含む可能性があるため、絶対値ではなく相対比較として扱った
