UE5 5.5 複数 PC 環境でクックを速くする Zen Storage Server の Shared DDC をできるだけ簡単にセットアップする

Unreal Engine のクックには時間がかかります。特に初めて新しいバージョンの Editor を起動する場合はシェーダーコンパイルに時間がかかり、スプラッシュ画面のまましばらく待つことになります。

複数台 PC を持っている場合はそれぞれの PC 毎に同じビルドが走るのですが、Shared DDC を使うとこの起動待ち時間を大幅に短縮することができます。

UE5 5.5 からは共有フォルダを使わずに Zen Storage Server を使った Shared DDC が使えるようになりました。直接ファイルシステムを使うよりも効率が良く高速化されています。

普段使ってる PC でも Shared DDC が使えるように、できるだけ簡単な手順で Zen Server をセットアップしてみます。

公式のドキュメントはこちらです

UNREAL ENGINE : Zenserver as Shared DDC

セットアップ手順

OS は Windows 11 Home / Pro どちらでも構いません。以下の手順は Home を元に解説しています。

(1) インストールするフォルダの準備

今回は仮に C:\zen 以下に作成するものとして説明します。パスは必要に応じて置き換えてください。C:\zen 以下に ZenInstall と ZenData フォルダを作成します。

C:\zen\ZenInstall
C:\zen\ZenData

(2) バイナリファイルのコピー

エンジンフォルダ Engine\Binaries\Win64 の中にある zen.exe と zenserver.exe を C:\zen\ZenInstall ににコピーします。

Epic Launcher 版エンジンを使用している場合は以下のようなパスになるかと思います。もちろん GitHub 版(ソースコード版)でも構いません。その場合はコピー元のバスを置き換えてください。

C:\Program Files\Epic Games\UE_5.5\Engine\Binaries\Win64\zenserver.exe
C:\Program Files\Epic Games\UE_5.5\Engine\Binaries\Win64\zen.exe

C:\zen\ZenInstall\zenserver.exe
C:\zen\ZenInstall\zen.exe

にコピーする。

(3) zen_config.lua ファイルを用意します

以下の内容で zen_config.lua ファイルを作成し、C:\zen\ZenInstall\zen_config.lua に置きます。

-- Zen Store Lua config

server = {
	dedicated = true,
	datadir = "C:\\zen\\ZenData",
	abslog = "C:\\zen\\ZenData\\local.log",
	debug = false,
	sentry = {
		disable = true,
		allowpersonalinfo = false,
	}
}

network = {
	httpserverclass = "httpsys", -- httpsys|asio
	port = 18558,
}

gc = {
	intervalseconds = 28800, -- every 8 hour
	lightweightintervalseconds = 3600, -- every hour
	cache = {
		maxdurationseconds =  864000, -- 10 days
	}
}

cache = {
	enable = true,
	accesslog = false,
	upstream = {
		upstreamthreadcount = 4,
		policy = "disabled", -- readwrite|readonly|writeonly|disabled
	},
	memlayer = {
		targetfootprint = 1073741824, -- 1 GB
		triminterval = 120, -- max every 2 minutes
		maxage = 172800, -- 2 days
	}
}

datadir, abslog のパスは必要に応じて書き換えてください。

ポート番号はデフォルトで 8558 ですが、これはローカルの ZenServer でも使用します。同じ PC 上で Unreal Editor も走らせる可能性がある場合はポート番号を変更します。今回の例では 18558 に置き換えました。

(4) netsh コマンドの実行

スタートボタン右クリックから「ターミナル(管理者)」を実行します。その後ターミナル内の PowerShell で以下のコマンドを実行します。

netsh http add urlacl url=http://*:18558/ user=$Env:USERNAME

設定結果の確認は「netsh http show urlacl」でできます。間違えた場合は「netsh http delete urlacl url=http://*:18558/ 」で削除してからやり直すことができます。

(5) firewall の設定

  1. スタートメニューで検索欄に「fire」を入力して「Windows Defender ファイアウォール」を起動します。(または 設定画面の検索から「ファイア」→「Windows Defender ファイアウォール」)
  2. 左側の「詳細設定」をクリックしてファイアウォールのダイアログを開きます
  3. 左上の「受信の規則」を選択して、右側の “操作” の中から「新しい規則…」をクリックします
  4. 規則の種類で「ポート」を選択して「次へ」
  5. 特定のローカルポートに「18558」を入れて「次へ」
  6. 「接続を許可する」が選択されていることを確認して「次へ」
  7. 選択しているネットワークの種類に応じて変更します。もしよくわからない場合はデフォルトの全部選択されてるまま「次へ」
  8. 名前を入力できるので「Zen DDC Server」を入れて「完了」

他のファイアウォールソフトウエアを使用している場合は、そちらの設定も行っておいてください。

(6)バッチファイルによる起動

これで完了です。あとは以下のようなバッチファイルを用意して実行するだけです。インストールフォルダが異なる場合は、パスを正しいものに書き換えてください。

@echo off

C:\zen\ZenInstall\zenserver.exe --config=C:\zen\ZenInstall\zen_config.lua

pause

もしこの状態で自動起動させたい場合はショートカットをスタートアップに入れておきます。

C:\Users\ユーザー名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

個人で使う分にはこれで十分だと思いますが、サービスとして起動することもできます。とりあえず先に動作確認してからサービスで起動する方法を解説します。

動作確認

  1. サーバーを起動した PC のブラウザで以下のページを開くことができるか確認します。
    • http://localhost:18558/dashboard
  2. 次に、他の PC からも開けるか確認します。
    • http://<インストールしたPCのIPアドレス>:18558/dashboard
    • 例えば Zen Server を起動した PC が 192.168.0.4 なら 「 http://192.168.0.4:18558/dashboard 」です。

ここでなにか設定できるわけではないので、それぞれページが表示されたら OK です。

UE5 の設定と確認

(1) Shared DDC の設定

プロジェクト側で設定する場合は Config/DefaultEngine.ini に、エンジン側で設定する場合は Engine/Config/BaseEngine.ini の最後に以下の行を追加してください。

[StorageServers]
Shared=(Host="192.168.0.4:18558", Namespace="ue.ddc", EnvHostOverride=UE-ZenSharedDataCacheHost, CommandLineHostOverride=ZenSharedDataCacheHost, DeactivateAt=60)

Host= の IP アドレスの部分は、Zen Storage Server を起動した PC の IP アドレスに置き換えてください。上の例で “192.168.0.4” となっている部分を置き換えます。

(2) Editor の起動と確認

  1. 何らかのプロジェクトで Editor を起動します。
  2. エディタの右下にある「派生データ」から「キャッシュ統計情報を表示」を選択します
  3. ダイアログが開くので、表示の中に「Zen リモート」が含まれていれば OK です

サービスとして起動する

起動バッチファイルを使わずに、サービスとして自動的に走らせておくことができます。もしサービス化する場合は、先にバッチファイルで起動した Zen Server をクローズし、スタートアップフォルダに作ったショートカットも削除しておいてください。

(1) サービスの登録

スタートボタン右クリックから「ターミナル(管理者)」を開いて、以下のコマンドを実行します。C:\zen\ZenInstall のパス部分は必要に応じて置き換えてください。

sc.exe create "Zen Store" start=auto binpath="C:\zen\ZenInstall\zenserver.exe --config=C:\zen\ZenInstall\zen_config.lua"

続けて以下のコマンドも実行します。

sc.exe failure "Zen Store" reset=60 actions=restart/60000

(2) サービスの設定

  1. スタートボタン右クリックから「コンピューターの管理」を開きます
  2. 「サービスとアプリケーション」の中にある「サービス」を選択します
  3. サービス一覧の中から「Zen Store」を探してダブルクリックします
  4. 「ログオン」のタブを選択し、下側の「アカウント」を選択します
  5. アカウント欄右側の「参照」を開いて、入力欄に自分のユーザー名を入れて「名前の確認」をクリックします
  6. 名前が展開されたら「OK」をクリックします
  7. ログインパスワードを入れて「適用」をクリックします
    • ここで「アカウント ~ にサービスとしてログオンする権利が与えられました。」とダイアログ表示が出れば成功です
  8. 「全般」タブに切り替えて「開始」します

これでサービスとして自動的に起動するようになりました。

もし「サービスとしてログオンする権利が与えられました。」が表示されない場合

Windows 11 Pro の場合は以下のようにします

  1. スタートメニューを表示し、検索欄に「ローカル」を入力して「ローカルセキュリティポリシー」を起動します
  2. 「セキュリティの設定」 → 「ローカルポリシー」 → 「ユーザー権利の割り当て」 → 「サービスとしてログオン」をダブルクリックします
  3. 「ユーザーまたはグループの追加」をクリックします
  4. 自分のユーザー名を入力して「名前の確認」を押します
  5. 名前が展開されたら「OK」→「OK」をクリックして閉じます

Windows 11 Home の場合は上の設定ができません。サービスとして起動したい場合は公式のドキュメントに従い、別のローカルユーザーを作成してみてください。もしくは個人で使う場合はサービス化しなくても、バッチファイルによる起動のままでも十分だと思います。

設定の調整など

週末しか使用しないなど、Unreal Engine の利用頻度が低い場合は zen_config.lua 内の期限設定を長めに変更した方が良いかもしれません。

Pixel 9a Tensor G4 の浮動小数点演算能力と Linux ターミナル

Pixel 9a で vfpbench を走らせてみました。vfpbench は CPU の浮動小数点演算能力の理論的なピーク値に焦点を当てたベンチマークです。

以下の表は Pixel 8/8a との比較のための抜粋です。動作クロックが上がっている分だけシングル性能は高いはずですが、1コア減っているためマルチスレッドでは差がつきません。Pixel 8 比では全体的に低い値になっています。Pixel 8a の Single (Cortex-X3) の値が極端に低いですが、おそらく測定時に発熱で制限(サーマルスロットリング)がかかり、クロックが上がらなかったためと思われます。

fp32 SingleFP32 MULTIFP64 SINGLEFP64 MULTI
Pixel 869.7 GFLOPS281.9 GFLOPS28.0 GFLOPS139.6 GFLOPS
Pixel 8a37.7 GFLOPS245.1 GFLOPS18.8 GFLOPS122.2 GFLOPS
Pixel 9a67.9 GFLOPS256.5 GFLOPS33.8 GFLOPS127.4 GFLOPS
SINGLE = Single Thread、MULTI = Multi Thread

全部のデータはこちらで参照できます。

個々の命令の特性を見ると、クロックあたりのピークの浮動小数点演算能力はほぼ同等で特性もかなり似通ったものになっていることがわかります。

Tensor G4 自体は G3 と比べると CPU コアの世代が上がっています。以下は CPU コアの比較表です。

TENSOR G1TENSOR G2TENSOR G3TENSOR G4
PrimeX1 (x2)X1 (x2)X3 (x1)X4 (x1)
BigA76 (x2)A78 (x2)A715 (x4)A720 (x3)
LittleA55 (x4)A55 (x4)A510 (x4)A520 (x4)

Tensor G3 で唯一 32bit 命令2対応していた A510 が無くなり、Tensor G4 では完全に 64bit 命令のみ対応の CPU コアに置き換わりました。Tensor G4 では ARM の 32bit 命令のプログラムは動きません。ARM の各 CPU コアの 32bit 命令対応についてはこちらの記事を参照してください。

もっとも、OS としては Pixel 7 の Tensor G2 世代から 32bit アプリケーションには非対応となっており、すでに完全に 64bit に置き換わっています。そのため 32bit 対応コアがなくなっても影響はありませんのでご安心ください。

vfpbench の話しに戻ります。Tensor G4 の LITTLE コア A520 は 4個搭載されていますが、シングル時の浮動小数点演算性能に対して、4コアマルチスレッドでも 4倍ではなく 2倍にしかなっていません。 以下は LITTLE コアだけの比較になります。

FP32 SINGLEFP32 MULTIFP64 SINGLEFP64 MULTI
Pixel 7a (A55)14.0 GFLOPS54.7 GFLOPS7.0 GFLOPS27.8 GFLOPS
Pixel 8 (A510)27.1 GFLOPS61.3 GFLOPS13.5 GFLOPS29.1 GFLOPS
Pixel 8a (A510)27.0 GFLOPS56.6 GFLOPS13.5 GFLOPS28.0 GFLOPS
Pixel 9a (A520)30.9 GFLOPS62.7 GFLOPS15.4 GFLOPS31.7 GFLOPS
SINGLE = Single Thread、MULTI = Multi Thread

例えば Pixel 9a (A520) の FP32 SINGLE は 30.9 GFLOPS ですが、FP32 MULLTI でも約 2倍の 62.7 GFLOPS しかありません。これは A520 が A510 と同じように 2コアで浮動小数点演算ユニットを共有しているためだと考えられます。

A55 時代の Pixel 7a (Tensor G2) では Multi 時のスコアに対して Single の値が 1/4 になっています。つまり A510/A520 は A55 と比べて Single 時の性能が 2倍になっているわけです。

また LITTLE コアだけの比較では Pixel 8/8a の G3 よりも Pixel 9a の G4 の方が速度が上がっています。それぞれクロック差の影響がきちんとスコアに反映されています。同様に同じ G3 を搭載した Pixel 8 と 8a のパフォーマンスも同等です。LITTLE コアは電力効率が良いためにサーマルスロットリングの影響を受けなかったのだと思われます。

A510 におけるコアの共有についてはこちらで解説しています。

Linux 開発環境 (ターミナル) が 9a で使えないことについて

Pixel 9a を実際に使ってみて少々予想外だった点は、Linux 開発環境 (ターミナル) 機能が使えなかったことです。

「Linux 開発環境」は Pixel の OS に標準で含まれている Linux のコマンドラインの動作環境です。ChromeOS の同名の機能、もしくは Windows の WSL に相当します。Linux 開発環境 (ターミナル) 機能については以下の記事で解説しています。

おそらく使えないのは発売当初だけで、将来の OS 更新によって Pixel 9a でもサポートするのではないかと思われます。

幸いなことに、発売したばかりの Pixel 9a も Android 16 のベータプログラムに含まれていることがわかりました。Pixel 9a でも Android 16 Beta 4 をインストールすると、開発者向けオプショにから Linux 開発環境を有効にすることができました。

どうしても今すぐ Pixel 9a で Linux を使いたいという場合はベータプログラムを利用するのも一つの手かもしれません。もちろんメインのスマートフォンとして使用している場合はお勧めしません。

MCP Server の仕組みを少し調べてみる

LLM をチャットの応答だけでなく、Agent として使うために直接外部アプリケーションを呼び出せるような拡張が行われています。特に MCP と呼ばれるプロトコルではツールとのインターフェースが標準化されており、SDK を使うと簡単に追加機能を作成することが可能です。

PC 上で動いているクライアントアプリケーションは、人間の入力を LLM が動いているサーバーに送信して返答を受け取ります。Agent ではその一連のやり取りにツールが割り込みます。LLM からの要請に応じてクライアント側がツールのソフトウエアを呼び出し、人間の代わりに LLM に対して返答するわけです。これらの Function Calling と呼ばれる機能拡張の仕組みを標準化したものが MCP に相当します。

MCP に対応したツールはサーバーと呼ばれるため、クラウド上に用意したり、LLM サーバー側が直接通信しているような印象を受けますが実際は異なります。簡単に言えば PC 上で動いている LLM アプリケーションに追加するプラグインのことです。

Claude Desktop や Cline などのソフトウエアは、プラグインのロードと同じように組み込まれている MCP のソフトウエアを別プロセスとして起動します。このときプロセス間通信として、PIPE (標準入出力) もしくは TCP (HTTP/SSE) が使われています。

MCP サーバーはクライアント毎に別プロセスが立ち上がります。例えば Claude Desktop と Cline のどちらも同じ MCP のツールを参照していた場合、それぞれが別プロセスとして MCP サーバーを起動します。サーバーは共有されません。やはり構造的にプラグインの方がイメージしやすいように思います。

Python SDK MCP コマンド

PIPE や TCP で渡されるデータは Json です。Python SDK で使われている mcp コマンドは、この Json と Python Script の関数呼び出しの相互変換を行っています。

例えば以下の例では mcp コマンド (mcp.exe) が標準入力から json フォーマットの文字列を受け取り、関数呼び出しの形に変換して server.py 内部の関数呼び出します。また関数の戻り値を json 化して標準出力に出力します。

{
  "mcpServers": {
    "demo-app": {
      "command": "mcp",
      "args": [
        "run",
        "C:\\mcptest\\server.py"
      ]
    },
  }
}

mcp.exe は Python の実行ファイルで、引数で渡されている server.py を内部で import しています。

実際にやり取りされているデータ

本当に標準入出力が使われているのかどうか、間に別のプロセスを挟み込んで通信内容をキャプチャしてみました。引数で渡した mcp サーバーを起動し、標準入出力をそのまま渡します。同時にログファイルに記録します。このコマンド (command-capture.py) は Calude 3.7 Sonnet が作りました。

{
  "mcpServers": {
    "demo-app": {
      "command": "C:\\mcptest\\venv\\Scripts\\python.exe",
      "args": [
        "C:\\mcptest\\command-capture.py",
            "--quiet",
            "--log-dir",
                "C:/mcptest/logs",
            "C:\\mcptest\\venv\\Scripts\\mcp.exe",
                "run",
                "C:\\mcptest\\server.py"
      ]
    }
  }
}

以下は Claude Desktop による実際の入出力を記録したものです。tool/list など method の呼び出しが行われている様子がわかります。

[2025-04-06 15:09:35.419] IN: {"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0}
[2025-04-06 15:09:36.981] OUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"demo-app","version":"1.5.0"}}}
[2025-04-06 15:09:36.988] IN: {"method":"notifications/initialized","jsonrpc":"2.0"}
[2025-04-06 15:09:36.989] IN: {"method":"resources/list","params":{},"jsonrpc":"2.0","id":1}
[2025-04-06 15:09:36.990] IN: {"method":"tools/list","params":{},"jsonrpc":"2.0","id":2}
[2025-04-06 15:09:36.992] OUT: {"jsonrpc":"2.0","id":1,"result":{"resources":[]}}
[2025-04-06 15:09:36.992] OUT: {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"calc_add","description":"Add two numbers","inputSchema":{"properties":{"a":{"title":"a","type":"string"},"b":{"title":"b","type":"string"}},"required":["a","b"],"title":"calc_addArguments","type":"object"}},{"name":"exec_shell_command","description":"Execute a command in the bash shell","inputSchema":{"properties":{"command":{"title":"command","type":"string"}},"required":["command"],"title":"exec_shell_commandArguments","type":"object"}}]}}

[2025-04-06 15:10:53.116] IN: {"method":"tools/call","params":{"name":"calc_add","arguments":{"a":"2838414","b":"8294241"}},"jsonrpc":"2.0","id":36}
[2025-04-06 15:10:53.118] OUT: {"jsonrpc":"2.0","id":36,"result":{"content":[{"type":"text","text":"11132655"}],"isError":false}}

なおアプリケーション側でも同様のログが記録されているため、デバッグに利用する場合はこのようなツールは不要です。Claude Desktop の Windows 版の場合 AppData\Roaming\Claude\logs にあります。

Json 部分を整形すると以下のようになります。

[2025-04-06 15:09:35.419] IN: {
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {
        "name": "claude-ai",
        "version": "0.1.0"
      }
    },
    "jsonrpc": "2.0",
    "id": 0
  }
[2025-04-06 15:09:36.981] OUT: {
    "jsonrpc": "2.0",
    "id": 0,
    "result": {
      "protocolVersion": "2024-11-05",
      "capabilities": {
        "experimental": {},
        "prompts": {
          "listChanged": false
        },
        "resources": {
          "subscribe": false,
          "listChanged": false
        },
        "tools": {
          "listChanged": false
        }
      },
      "serverInfo": {
        "name": "demo-app",
        "version": "1.5.0"
      }
    }
  }
[2025-04-06 15:09:36.988] IN: {
    "method": "notifications/initialized",
    "jsonrpc": "2.0"
  }
[2025-04-06 15:09:36.989] IN: {
    "method": "resources/list",
    "params": {},
    "jsonrpc": "2.0",
    "id": 1
  }
[2025-04-06 15:09:36.990] IN: {
    "method": "tools/list",
    "params": {},
    "jsonrpc": "2.0",
    "id": 2
  }
[2025-04-06 15:09:36.992] OUT: {
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
      "resources": []
    }
  }
[2025-04-06 15:09:36.992] OUT: {
    "jsonrpc": "2.0",
    "id": 2,
    "result": {
      "tools": [
        {
          "name": "calc_add",
          "description": "Add two numbers",
          "inputSchema": {
            "properties": {
              "a": {
                "title": "a",
                "type": "string"
              },
              "b": {
                "title": "b",
                "type": "string"
              }
            },
            "required": [
              "a",
              "b"
            ],
            "title": "calc_addArguments",
            "type": "object"
          }
        },
        {
          "name": "exec_shell_command",
          "description": "Execute a command in the bash shell",
          "inputSchema": {
            "properties": {
              "command": {
                "title": "command",
                "type": "string"
              }
            },
            "required": [
              "command"
            ],
            "title": "exec_shell_commandArguments",
            "type": "object"
          }
        }
      ]
    }
  }



[2025-04-06 15:10:53.116] IN: {
    "method": "tools/call",
    "params": {
      "name": "calc_add",
      "arguments": {
        "a": "2838414",
        "b": "8294241"
      }
    },
    "jsonrpc": "2.0",
    "id": 36
  }
[2025-04-06 15:10:53.118] OUT: {
    "jsonrpc": "2.0",
    "id": 36,
    "result": {
      "content": [
        {
          "type": "text",
          "text": "11132655"
        }
      ],
      "isError": false
    }
  }

標準入出力ができれば良いだけなので、SDK やライブラリがない他の言語でも比較的容易に作ることができる仕組みです。

Android 上で動く Linux 開発環境 (ターミナル)

スマートフォン Pixel の新しい OS 更新 (Android 15 2025/03/05 BPA1A.250305.019) で Linux 開発環境 (ターミナル) が使えるようになりました。まだ試験運用版 (Experimental) なので制限もありますが、普通のスマートフォン上で Debian の仮想マシンが動いています。Chromebook (ChromeOS) の Linux 開発環境や Windows の WSL2 と同じように Linux 向けのアプリケーションをそのまま走らせることができます。

以下の手順や注意事項は 2025/03 (試験運用) 版のものです。今後更新によっていろいろ変わる可能性があります。

有効化手順

  1. 作業中はスリープしないように、先に自動消灯時間を変更しておくことをお勧めします
    • 設定 → ディスプレイとタップ → 画面自動消灯 → 30分に変更
    • インストールや初期設定が終わったら元に戻してください
  2. もしまだ「開発者向けオプション」が有効になっていない場合以下の手順で有効化します
    • 設定 → デバイス情報 →「ビルド番号」を何度か(7回)タップして「開発者向けオプション」を有効化
  3. Linux 開発環境を有効化します
    • 設定 → 開発者向けオプション → Linux 開発環境 →「Android で Linux ターミナルを実行する」を On
  4. これでアプリ一覧 (アプリドロワー) に「ターミナル」アプリが追加されるので「ターミナル」アプリを起動します
  5. 初回は「インストール」をタップして 565MB のデータをダウンロードします
    • ダウンロードに時間がかかりますが、そのまま画面を切り替えないようにしてください
    • インストールが終わり「ターミナルを準備しています」の画面が始まったらコンソール画面になるまでしばらく待ちます
    • ターミナルが起動して Debian のコンソール画面になります
  6. ストレージサイズの変更 (オプション)
    1. デフォルトでは 5.9GB しか割り当てられていないので、先にストレージサイズを増やしておくことをお勧めします。不要な場合はスキップして構いません。
    2. ターミナルで以下のコマンドを実行
      • sudo halt
    3. 「Press ↲ to Recoonect」と表示されたらターミナルの右上の設定アイコン(歯車)をタップ
    4. 「ディスクサイズを変更」を選択し、5.9 GB から最大の 16GB まで増やして「適用」
    5. 自動的にターミナルが終了します
    6. もう一度ターミナルアプリを起動します
      • もしここで「修復不可能なエラー」と表示されてもリカバリしないでください
      • 一旦ターミナルアプリをタスク管理画面で終了させてから起動し直すと正常に繋がります
  7. 以下のコマンドを入力して OS の更新をします
    • sudo apt update ; sudo apt upgrade -y
    • 時間がかかりますが、終わるまでスリープさせずそのままの画面を維持しておいてください
  8. 更新が終わったら 1. で行った画面の自動消灯時間設定を元に戻します。

あとはそのまま Linux 環境として使えます。Docker も使えますので様々なアプリケーションを走らせることが可能です。Bluetooth キーボードがあると便利かもしれません。

※ 画面の自動消灯時間設定を元に戻すのを忘れないようにしてください。

トラブル対策など

ターミナルアプリと VM は別のプロセスですが連動もしています。おそらくターミナル起動時に VM が立ち上がり、終了すると VM も終了するようになっているようです。ただし VM が先に終了してターミナルだけ起動している状態になると Reconnecting 表示のまま進まなくなります。この場合はターミナルを起動し直してください。

修復不可能エラー画面が表示されても、よほどのケースでない限りリストアは不要なようです。リストアしても、同じ手順を行うと結局同じ状態になってしまうので、まずはターミナルの再起動を優先してください。

初期ストレージサイズが 5.9GB しかないため、大きなアプリケーションをインストールしようとすると途中でストレージがあふれてしまうことがあります。いろいろインストールを考えている場合は先に最大の 16GB まで拡張しておくことをお勧めします。

メモリ (RAM) を多く消費している状態で他のアプリに切り替えたりバックグラウンドに移行すると、VM のプロセス自体が kill されてしまうことがあります。インストール途中で強制切断されると中途半端な状態になってしまうので、初回のインストールや更新中は画面を切り替えないようにしてください。

その他気がついたこと、制限など

本体ストレージへのアクセス

/mnt/shared で本体ストレージの Downloads にアクセスできるようです。Android 側のブラウザでダウンロードしたファイルにアクセスすることができます。

また VM のストレージサイズが 16GB に制限されているので、大きなデータファイルをこちらに置いておくと容量を節約できるかもしれません。

RAM 容量

Pixel 8 (VRAM 8GB) を使用していますが、Linux VM 側で使用可能なメモリは 4GB 固定でした。そのためメモリを大量に使うコマンドは、Termux 上では動作するものの Linux VM (Linux 開発環境) ではメモリ不足で実行できない場合があります。

実際に Termux 上では ollama を使って LLM の gemma3:4b (6.4GB) を起動できますが VM 側 (Linux 開発環境) ではメモリ不足で読み込めませんでした。qwen2.5:3b (2.6GB) は動きます。

もしかしたら RAM を 12GB/16GB 搭載している他のデバイスではメモリ割り当てが異なってるかもしれません。

CPU は Tensor G3 の 9 コア全部有効になっています。

ターミナル

ターミナルはブラウザ上で動いているようです。

ターミナルを複数画面開くことはできませんが、sshd を起動して Android の ssh Terminal アプリや Termux を使えば複数のコンソール画面を使い分けることができます。なお ssh 接続する場合はデフォルトユーザーの droid にパスワードを設定しておく必要があります。

  1. sudo apt install openssh-server
  2. sudo passwd droid

自分の環境では VM に 192.168.0.2 が割り当てられていました。ssh Terminal アプリなどから droid@192.168.0.2 でログインできます。Termux の場合「ssh 192.168.0.2 -l droid」です。

リモート接続

VM にはプライベート IP が割り当てられているためスマートフォンの 外部からはそのままでは繋がりません。PC 等から ssh 接続したい場合は Termux を踏み台にしたり、VM 側から ssh でトンネルを作る必要があります。

VM 側
$ ssh -l <PCUSER> -R 9022:127.0.0.1:22 <PCIPADDR>

PC 側
$ ssh -p 9022 -l droid 127.0.0.1

私は VM 側 (Linux 開発環境) に Linux 版 Tailscale をインストールして接続しています。

対応機種

現時点では Google Pixel (Android 15) のみとなっているようです。

OS 標準の Linux 環境なので、特別なアプリをインストールしたり複雑な設定なしに使えるのは非常に便利です。まだ不安定な部分はありますが、利用できるアプリケーションの幅がだいぶ広がりました。スマートフォンには高性能な端末も多いので、Pixel 以外の対応も待ち遠しいです。

APU 内蔵 GPU でもローカル LLM を走らせてみる

以前 ROCm 非対応の Radeon RX 6400 でも、環境変数の設定だけで ROCm 版の ollama を走らせることができました。同じ方法で APU の内蔵 GPU でもローカル LLM を実行できるので実際に試してみました。

今回使用した CPU 内蔵 GPU は以下の 2つです。いずれも DDR5-5600 を使用しています。

CPU (APU)内蔵 GPUアーキテクチャCUspfp32 FLOPS
Ryzen 7 9700XRadeon 610M RDNA2 gfx10372 CU128 sp486.4 GFLOPS
Ryzen 7 7840HSRadeon 780MRDNA3 gfx110312 CU768 sp8294.4 GFLOPS

インストール方法など

事前に UEFI (BIOS) 設定で、内蔵 GPU のフレームバッファの割当をできるだけ増やしておいてください。今回は Ryzen 7 9700X では 16GB、Ryzen 7 7840HS では 8GB に設定しています。

  1. OS は Linux を直接インストールしています
    • Ubuntu 24.04LTS (Desktop 版最小構成)
  2. ollama をインストールします
    • 公式手順どおりです
    • curl -fsSL https://ollama.com/install.sh | sh
  3. ollama の起動設定ファイルを開きます
    • sudo systemctl edit ollama.service
  4. エディタが起動したら 3行目からの空行に以下の 2行を挿入します
    • Ryzen 7 9700X (RDNA2) の場合
      • [Service]
        Environment="HSA_OVERRIDE_GFX_VERSION=10.3.0"
    • Ryzen 7 7840HS (RDNA3) の場合
      • [Service]
        Environment="HSA_OVERRIDE_GFX_VERSION=11.0.2"
  5. 保存して終了したら以下のコマンドを実行します
    • sudo systemctl daemon-reload
    • sudo systemctl restart ollama

実行方法

  1. コマンドラインから直接モデル名を指定して実行します
    • ollama run gemma2:9b
  2. あとは質問などをコンソールから入力できます

もし詳細 (token/s など) を表示させたい場合は質問の前に「/set verbose [ENTER]」を入力しておきます。終了は Ctrl-D です。

実行中に別のターミナルを開いて、コマンドラインから ollama ps を実行することで GPU への割当状況を確認することができます。100% GPU になっていれば OK です。

oga@ub24llm:~$ ollama ps
NAME         ID              SIZE      PROCESSOR    UNTIL
gemma2:9b    ff02c3702f32    9.4 GB    100% GPU     4 minutes from now

実行結果

実際に試した結果は以下のとおりです。CPU のみの場合と比べています。

9b model (gemma2:9b) の場合

CPUGPUGPU割合使用メモリtoken/s
Ryzen 7 9700Xなし0%7.7 GB8.71 tps
Ryzen 7 9700XRadeon 610M100%7.3 GB5.88 tps
Ryzen 7 7840HSなし0%9.0 GB9.02 tps
Ryzen 7 7840HSRadeon 780M100%7.3 GB12.89 tps

Ryzen 7 9700X では GPU Radeon 610M を使用した方が速度が落ちているのは予想通りです。この GPU は 2CU 128sp しかなく CPU よりも演算性能が劣ります。Ryzen 7 9700X の Zen5 は AVX512 命令が同時に 2命令走るため 8 core でも 256sp 相当、クロック差を考慮すると CPU の方が 4倍以上演算能力が高いことになります。

逆に Ryzen 7 7840HS の場合は 768sp × 2ALU の GPU を搭載しており、GPU の方が何倍も演算能力が高いことになります。ですが LLM の場合は演算能力だけでなくメモリ速度の影響を強く受けます。APU ではメモリ帯域で頭打ちになるため、あまりパフォーマンスが変わらないのではないかと思ってましたが少々予想外でした。この結果だけ見るとGPU の方が 40% ほど速くなっています。なお NPU は未使用です。

以下他のモデルでの結果です

14b model (phi4:14b)

CPUGPUGPU割合使用メモリtoken/s
Ryzen 7 9700Xなし0%10 GB5.73 tps
Ryzen 7 9700XRadeon 610M100%12 GB1.96 tps
Ryzen 7 7840HSなし0%11 GB6.22 tps
Ryzen 7 7840HSRadeon 780M80%10 GB6.59 tps

14b のモデルではフレームバッファ割当 8GB の Radeon 780M では VRAM 領域にすべて載りませんでした。そのため 20% は CPU が用いられており CPU と GPU の差は小さくなっています。

他のモデルや他の GPU 利用時の結果は↓こちらのページにまとめています。Wiki なので逐次更新しています。

CPU の速度差

上の結果を見ると CPU 利用時は Ryzen 7 9700X よりも Ryzen 7 7840HS の方がパフォーマンスが高いように見えます。でも実際は演算性能は Zen5 の 9700X の方が上で、Desktop 向けなので電力&冷却も 9700X の方が有利です。

これまでの表の結果を見ると動作時の使用メモリ容量が 7.7GB と 9.0GB で異なっているので、走っている ollama の runner が異なっている可能性があります。runnner によって使用する CPU 命令に違いがあり、最適化方法が異なっている可能性があります。また動作プラットフォームを変えると逆転することがあるため、計測環境の不一致も考えられます。どちらが速い、等の結論を出すにはまだ早いようです。

以下は異なる OS 下での ollama による違い調べたものです。token/s が大きい順に並べています。WSL2 の結果を見ると 9700X の方が速くなっています。

9b (gemma2:9b) CPUのみ

OSCPU使用メモリtoken/s
Windows11 + WSL2Ryzen 7 9700X7.7 GB9.46 tps
LinuxRyzen 7 7840HS9.0 GB9.02 tps
LinuxRyzen 7 9700X7.7 GB8.71 tps
Windows11Ryzen 7 9700X7.7 GB8.66 tps
Windows 11 + WSL2Ryzen 7 7840HS9.0 GB8.59 tps

演算能力で大きく異なるはずの 9700X と 7840HS でも結果としてはあまり違いが生じない結果となっています。使用している RAM の速度が同一なので、メモリ帯域による限界ではないかと思われます。

まとめなど

さまざまな GPU で試した結果を見ても、やはりメモリ速度が非常に重要であることがわかります。CPU 内蔵 GPU (APU) ではメモリ帯域に制限があるため、いくら演算能力が高くても外付け GPU ほどの大幅な高速化は期待できません。それでも Ryzen 7 7840HS は GPU の方が速度が出ていますので、使えるケースでは活用していきたいところです。

関連ページ