軽量LLM推論エンジン「llama.cpp」において、OpenCLバックエンドを使用する際に特定の量子化モデルでデータが破損する問題が修正された。今回の修正は、モデルの重み次元が128の倍数でない場合に発生する計算ミスとメモリ破壊に対処するもの。標準的なモデル形状では影響がなかったが、「granite-3.1-3b-a800m-instruct」のような語彙数が奇数の非典型モデルで出力が乱れる不具合が確認されていた。この修正により、Arm系やQualcomm Adreno GPUを搭載したモバイル・エッジデバイス上での小規模モデル動作の信頼性が一段と高まる。
Adreno GPUで顕在化したQ6_K重みの2重不具合
発見された問題は2層構造を持つ。第1に、重みの第1次元(ne01)が128で割り切れない場合、Q6_K量子化の密行列乗算(dense mul_mat)が不正な計算経路に入り、出力が文字化けする不具合があった。第2に、量子化重みをコンポーネント単位でGPUバッファに切り出す「set_tensor」処理において、各コンポーネントの先頭アドレスはデバイスアラインメントに切り上げられるが、確保領域がコンポーネント末尾のオーバーランを考慮しておらず、隣接テンソルを破壊していた。今回のケースでは、49155という奇数語彙数を持つモデルで、最終行のブロックスケールがサイレント破損していた。
修正手法:非典型形状をCPUパスへ迂回、バッファ割当に余裕を確保
修正は大きく2つ。1つ目は、ne01が128の倍数でないQ6_K重みの処理において、デコード(ne1==1)時は正しいフラットGEMVパスを維持し、バッチ処理(ne1>1)時は高速な「noshuffle」カーネルが未対応のためCPUフォールバックへ迂回するよう経路を変更した。2つ目は、OpenCLバッファの割当サイズ計算に「get_alloc_size」を導入し、最大5コンポーネントの量子化テンソルで発生しうる最悪のアライメント不足分(観測された128バイト境界で最大512バイト)を予約する設計とした。標準的な2のべき乗次元は従来通りの高速パスを維持する。
標準モデルでは見えなかったエッジ形状の落とし穴
今回の不具合が長らく潜在化していた背景には、LLMの設計慣行がある。隠れ次元やFFN中間層はほぼ常に128や256といった2のべき乗またはその倍数で設計される。しかし、語彙埋め込み行列はボキャブラリサイズをそのまま次元とするため、ボキャブラリ数が半端なモデルでは今回のような非典型形状が出現する。Qualcomm Adreno GPUを搭載したWindows on Arm環境で、幅広いモデルの互換性を追求するテスト過程において初めて顕在化した。エッジAIの普及で多様なカスタムモデルが流通するほど、こうした境界条件対応の重要性は増す。
オンデバイスAI基盤としてのllama.cppの立ち位置
今回のパッチはQualcommの開発者との共同作業で作成され、Windows arm64(OpenCL Adreno)を含む広範なCIマトリックスで検証が進められている。llama.cppは、Apple Silicon、Qualcomm Adreno、Vulkan、CUDAなど多様なバックエンドを単一コードベースでサポートし、コンシューマデバイス上でのLLM推論の共通インフラとしての地位を固めつつある。修正が数バイトのアライメント不足にまで及ぶ事実は、多様なアクセラレータと多様なモデルを組み合わせるオンデバイスAI環境において、フレームワークレベルでの厳密なメモリ管理と計算カーネルの形状網羅性が競争力の源泉となることを示している。