概要
開発者が投稿されたUnbelievable! Run Kimi K3–2.8 Trillion Parameters — on a Single 4GB GPUを読んでAirLLMを知った。
小さなVRAMで巨大LLMに推論をさせるフレームワーク。ハイレベルな仕組みはとてもわかりやすく、LLMの全レイヤをVRAMへoffloadせずに使用するレイヤだけを順にoffloadするというもの。従いVRAMの容量は1レイヤ分で足りる。もちろんVRAMはTransformerだけが使用しているわけではないのでイニシャル分も固定費的に必要だが、それも本手法の効果が歴然としている。
強烈なビジネスチャンスを感じるので、ProsであるVRAM削減効果とConsの推論時間延伸を調べてみる。
- 実施期間: 2026年8月
- 環境:Google Colab Pro, Ubuntu20.04
- GPU: T4(Colab), A100(Colab), RTX-3090Ti(Ubuntu)
- LLM: Qwen/Qwen3-14B
Model Overview:
Type: Causal Language Models
Training Stage: Pretraining & Post-training
Number of Parameters: 14.8B
Number of Paramaters (Non-Embedding): 13.2B
Number of Layers: 40
Number of Attention Heads (GQA): 40 for Q and 8 for KV
Context Length: 32,768 natively and 131,072 tokens with YaRN. - LLM: Qwen/Qwen3-32B
Model Overview
Type: Causal Language Models
Training Stage: Pretraining & Post-training
Number of Parameters: 32.8B
Number of Paramaters (Non-Embedding): 31.2B
Number of Layers: 64
Number of Attention Heads (GQA): 64 for Q and 8 for KV
Context Length: 32,768 natively and 131,072 tokens with YaRN.
1. 比較条件
次の変数を変えながらVRAM使用量と推論時間を計測する。on-premise
無圧縮Qwen3-14BはRTX-3090Tiに乗らないため、無圧縮Qwen3-14B on A100をbase lineとする。
- GPU:
A100-SXM4-40GB(VRAM: 39.49GiB)
Tesla T4(VRAM: 14.56GiB)
RTX-3090Ti(VRAM: 24GiB) - AirLLM: なし / あり(v3.1.0)
- LLM: Qwen3-14B / 32B
- 圧縮: No compression / 8bit
2. 測定結果
8tokensの生成にかかった時間(sec)を計測した。これらLLMは無圧縮ではRTX-3090Tiに乗らないので計測していない。
2.1 Qwen3-14B
| GPU | compression | AirLLM | Max VRAM usage(GiB) | 1st elapsed | 2nd elapsed |
|---|---|---|---|---|---|
| A100 | no(16bit) | no AirLLM | 27.53 | 0.4339 | 0.4349 |
| A100 | no(16bit) | with AirLLM | 1.47 | 177.25 | 107.19 |
| T4 | no(16bit) | with AirLLM | 1.47 | 958.80 | 959.08 |
| T4 | 8bit | with AirLLM | 2.92 | 547.03 | 564.73 |
| RTX-3090Ti | no(16bit) | with AirLLM | 1.46 | 84.21 | 78.03 |
| RTX-3090Ti | 8bit | with AirLLM | 2.91 | 66.44 | 66.69 |
評価に使用するelapsed timeはキャッシュが効く2回目の計測値を使用する。
分析①:AirLLM導入によるVRAM削減と推論のトレードオフ
同一GPU(A100)で比較すると、AirLLMの強力なメモリ削減効果と、それに伴うオーバーヘッドが明確に表れている。
- VRAM: 27.53GiB → 1.47GiB (約95%削減)
- Inference time: 0.43sec → 107.19sec (約250倍の増大)
分析②:Quantization (8bit) による推論の高速化
AirLLMでは、8bit化によってGPUの演算自体が高速化するというより、Storage → Memory → GPUへのWeight転送量が減るため、ボトルネックであるI/O待ちが軽減されInference timeが短縮される。
- T4環境: 959.08sec → 564.73sec (約41%短縮)
-
3090Ti環境: 78.03sec → 66.69sec (約15%短縮)
※ただし、実装の都合か8bit化するとMax VRAM使用量が約2倍に増加する点には注意が必要。
分析③:ハードウェア環境(Storage I/O)の影響
AirLLMの仕組み上、Storageの読み込み速度が律速となる。オンプレ環境のRTX-3090Tiは、ColabのA100(107.19sec)と比較しても78.03secと高速であり、高速なローカルストレージ(NVMe SSDなど)の恩恵が大きいと推測される。
分析④:ハードウェア環境による量子化効果の差
T4(Colab)では8bit化で推論時間が約40%短縮したが、RTX-3090Ti(オンプレ)では約15%の短縮にとどまった。これは、AirLLMのボトルネックである「ストレージ→VRAMへの転送時間」が影響している。Colabの遅いストレージ環境では転送量を半分にする効果が劇的だが、オンプレの高速なNVMe SSD環境ではそもそも転送が速いため、データ半減の恩恵が薄れ、AirLLM自体の処理オーバーヘッドが相対的に顕在化したためと推測される。
2.2 Qwen3-32B
LLMのレイヤ数が増えたときの変化をT4で計測する。
| GPU | compression | AirLLM | Max VRAM usage(GiB) | 1st elapsed | 2nd elapsed |
|---|---|---|---|---|---|
| T4 | no(16bit) | with AirLLM | 1.47 | 2152.20 | 2151.79 |
| T4 | 8bit | with AirLLM | 2.93 | 1178.14 | 1183.85 |
分析⑤:モデルサイズ(パラメータ数)の増加による推論時間への影響
Qwen3-14B(レイヤ数40)を32B(レイヤ数64)へ置き換え、パラメータ数をスケールさせた際の推論時間を計測した。結果として、推論時間には以下のような大きな増加が認められた。
- 量子化なしのInference time: 959.08sec → 2151.79sec (約2.24倍へ増大)
- 8Bit量子化のInference time: 564.73sec → 1183.85sec (約2.10倍へ増大)
ここで注目すべきは、推論時間の増加倍率(約2.1〜2.2倍)が、パラメータ数の増加倍率(32B / 14B ≒ 2.28倍)とほぼ完全に一致している点である。
レイヤ数が1.6倍になっただけでなく、各レイヤの次元数も大きくなっているため、モデル全体の重みの総データ量が約2.3倍になっている。AirLLMは推論の過程でモデルの全レイヤを都度ストレージからロードするため、Storage I/Oにかかる時間は「転送回数(レイヤ数)」以上に、「転送する総データ量(総パラメータ数)」に綺麗に比例して足を引っ張ることがこの結果から読み取れる。
3. 考察
VRAMの減量効果は確かに明らかだが、それ以上に推論時間の悪化が目立つ。チャット目的ではなく、時間をかけても良いAgenticなバッチ処理で、完全ローカルで推論する要件が必須であれば検討してもよいのかもしれない。
何よりこのフレームワークの目的はVRAMに乗らなかったLLMが乗るようになることなので、言わずもがな、か。
巨大LLMでもレイヤサイズは大きく違わなかったり、MoEならメモリ効率も良いので前述のレポートのようにKIMI K3でもVRAM 4GiBで動作するのであろう。またKIMI K3のレイヤ数は今回の2.3倍の93枚なので、乱暴な前提だがTransformerの構造が似ていれば推論時間もこの倍率程度で済むのかもしれない。つまり80sec/8tokens = 10sec/tokenあたり?
4. 使用コード
ChatGPTで作成したUbuntu用のコード
4.1 環境構築
conda create -n airllm-bench python=3.12 -y
conda activate airllm-bench
python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu128
python -m pip install -U airllm bitsandbytes
mkdir -p ~/airllm-bench-cache
export HF_HOME=~/airllm-bench-cache/huggingface
export TRANSFORMERS_CACHE=~/airllm-bench-cache/huggingface
4.2 チェック用コード
import torch
print("PyTorch:", torch.__version__)
print("Torch CUDA:", torch.version.cuda)
print("CUDA available:", torch.cuda.is_available())
if torch.cuda.is_available():
print("GPU:", torch.cuda.get_device_name(0))
print(
"VRAM:",
round(torch.cuda.get_device_properties(0).total_memory / 1024**3, 2),
"GiB",
)
> bitsandbytes installed
> Python: /home/ihmon/miniconda3/envs/airllm-bench/bin/python
> PyTorch: 2.11.0+cu128
> Torch CUDA: 12.8
> Transformers: 5.12.1
> bitsandbytes: 0.50.0
> CUDA available: True
> GPU: NVIDIA GeForce RTX 3090 Ti
> AirLLM import: OK
4.3 実行コード
import time
import torch
from airllm import AutoModel
model_name = "Qwen/Qwen3-14B"
print("Loading model...")
t0 = time.perf_counter()
air_model = AutoModel.from_pretrained(
model_name,
compression="8bit", # 量子化しないのならコメントアウト
)
setup_time = time.perf_counter() - t0
print(f"AirLLM setup time : {setup_time:.2f} s")
prompt = """
Explain in simple terms why time-series forecasting becomes difficult
when the behavior of users changes in response to the forecast itself.
"""
messages = [
{"role": "user", "content": prompt}
]
text = air_model.tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=False,
)
input_tokens = air_model.tokenizer(
[text],
return_tensors="pt",
return_attention_mask=False,
truncation=True,
padding=False,
)
input_ids = input_tokens["input_ids"]
print("Input tokens:", input_ids.shape[1])
torch.cuda.empty_cache()
torch.cuda.reset_peak_memory_stats()
torch.cuda.synchronize()
t0 = time.perf_counter()
with torch.inference_mode():
output = air_model.generate(
input_ids.cuda(),
max_new_tokens=8,
use_cache=True,
return_dict_in_generate=True,
do_sample=False,
)
torch.cuda.synchronize()
elapsed = time.perf_counter() - t0
generated_tokens = (
output.sequences.shape[1]
- input_ids.shape[1]
)
tokens_per_sec = generated_tokens / elapsed
seconds_per_token = elapsed / generated_tokens
peak_memory = torch.cuda.max_memory_allocated() / 1024**3
print()
print(f"Elapsed : {elapsed:.2f} s")
print(f"Generated tokens : {generated_tokens}")
print(f"Tokens/sec : {tokens_per_sec:.5f}")
print(f"Seconds/token : {seconds_per_token:.2f} s")
print(f"Peak GPU memory : {peak_memory:.2f} GiB")
generated_ids = output.sequences[0][input_ids.shape[1]:]
print()
print(
air_model.tokenizer.decode(
generated_ids,
skip_special_tokens=True,
)
)
前処理でモデルのレイヤは分解され次のフォルダに保管される。サイズは約240GiBだった。
/home/ihmon/airllm-bench-cache/huggingface/hub/models--Qwen--Qwen3-14B/snapshots
下記に置かれるオリジナルのサイズとほぼ同じである。
/home/ihmon/airllm-bench-cache/huggingface/hub/models--Qwen--Qwen3-14B/blobs
以上