larryliu0820's picture
Add model card
610be8f verified
|
Raw
History Blame
5.94 kB
metadata
library_name: executorch
license: apache-2.0
base_model: Qwen/Qwen3-0.6B
tags:
  - executorch
  - ggml
  - metal
  - cuda
  - on-device
  - quantized
  - q8_0
language:
  - en
pipeline_tag: text-generation

Qwen3-0.6B Q8_0 — ExecuTorch + GGML Backend

Qwen3-0.6B exported to ExecuTorch .pte format with the ggml backend and Q8_0 quantization. Runs on Metal (Apple Silicon) and CUDA (NVIDIA GPUs) using the same .pte file.

Includes the original .gguf file for llama.cpp baseline comparison.

Performance

Decode throughput (tok/s, tg128)

Platform executorch-ggml (.pte) llama.cpp (.gguf) % of llama.cpp
NVIDIA A100-SXM4-40GB 411 380 108%
Apple M4 Max 300 300 100%

Prefill throughput (tok/s, pp5)

Platform executorch-ggml (.pte) llama.cpp (.gguf)
NVIDIA A100-SXM4-40GB 1012 1518
Apple M4 Max 640 800

Per-step breakdown (decode, steady state)

Phase Metal (M4 Max) CUDA (A100)
build_graph 0.0 ms (cached) 0.0 ms (cached)
sched_alloc 0.0 ms (cached) 0.0 ms (cached)
compute 0.4 ms (async) 0.4 ms (async)
output sync 2.9 ms 2.0 ms
total ~3.3 ms ~2.4 ms

Graph: 602 nodes, 1 split (all ops on GPU).

Files

File Size Description
qwen3_q8_0.pte 762 MB ExecuTorch model (Q8_0 weights, F32 KV cache)
Qwen3-0.6B-Q8_0.gguf 610 MB GGUF model for llama.cpp baseline

Quick Start

1. Build

git clone https://github.com/anthropics/executorch-ggml
cd executorch-ggml
git submodule update --init --recursive

Metal (macOS):

cmake -B build_native \
    -DEXECUTORCH_GGML_BUILD_LLAMA_RUNNER=ON \
    -DCMAKE_BUILD_TYPE=Release
cmake --build build_native --target benchmark_llm --parallel 16

CUDA:

cmake -B build_native \
    -DEXECUTORCH_GGML_BUILD_LLAMA_RUNNER=ON \
    -DGGML_CUDA=ON \
    -DCMAKE_CUDA_ARCHITECTURES=80 \
    -DCMAKE_BUILD_TYPE=Release
cmake --build build_native --target benchmark_llm --parallel 16

2. Download

pip install huggingface_hub
python -c "
from huggingface_hub import snapshot_download
snapshot_download('larryliu0820/Qwen3-0.6B-Q8_0-ExecuTorch-GGML',
                  local_dir='models/qwen3')
"

3. Benchmark

Run executorch-ggml:

./build_native/benchmark/benchmark_llm \
    models/qwen3/qwen3_q8_0.pte \
    --n-decode 128 --prompt-len 5

Run llama.cpp baseline (for comparison):

# Build llama-bench
cd third-party/llama.cpp
cmake -B build -DGGML_METAL=ON -DCMAKE_BUILD_TYPE=Release  # or -DGGML_CUDA=ON
cmake --build build --target llama-bench --parallel 16
cd ../..

# Benchmark
third-party/llama.cpp/build/bin/llama-bench \
    -m models/qwen3/Qwen3-0.6B-Q8_0.gguf \
    -ngl 99 -p 5 -n 128 -r 5

4. Profiling

# Per-call timing breakdown
GGML_PERF_LOG=1 ./build_native/benchmark/benchmark_llm \
    models/qwen3/qwen3_q8_0.pte --n-decode 32

# Per-op timing (adds sync overhead — use for relative comparison only)
GGML_PROFILE=1 ./build_native/benchmark/benchmark_llm \
    models/qwen3/qwen3_q8_0.pte --n-decode 5

Export Pipeline

The .pte was exported with all optimization passes:

from executorch_ggml.modules.rms_norm import swap_rms_norm
from executorch_ggml.passes.fold_rms_norm_weights import fold_rms_norm_weights
from executorch_ggml.passes.fuse_rope_pass import fuse_rope_in_graph
from executorch_ggml.passes.strip_gqa_expand_pass import strip_gqa_expand

# 1. Module swaps (before export)
swap_rms_norm(model)           # 113 RMSNorm modules fused
fold_rms_norm_weights(model)   # 57 weight MULs eliminated

# 2. Export
ep = exportable.export()["model"]

# 3. AOT graph passes
fuse_rope_in_graph(ep.graph_module, head_dim=128, freq_base=1e6)  # 56 RoPE fused
strip_gqa_expand(ep.graph_module)  # GQA REPEATs eliminated

# 4. Edge lowering with Q8_0 quantization
edge_mgr = to_edge_transform_and_lower(
    ep,
    partitioner=[GgmlPartitioner(quant_config=GgmlQuantConfig())],
    compile_config=EdgeCompileConfig(_check_ir_validity=False, _skip_dim_order=True),
    transform_passes=[ReplaceCopyOpsPass(), RemoveGraphAssertsPass()],
    constant_methods=exportable.metadata,
)

See runner/export_qwen3_q8_optimized.py for the full script.

Optimizations Applied

Optimization Effect Nodes
Fused RMSNorm (swap_rms_norm) 8 ops/norm → 1 -24%
Fused RoPE (fuse_rope_in_graph) 9 ops/Q,K → 1 -24%
GQA strip (strip_gqa_expand) Remove expand/repeat -10%
RMSNorm weight fold Absorb weight into downstream linear -4%
Mask conversion cache Deduplicate across 28 layers -8%
RESHAPE collapse RESHAPE(RESHAPE(x))RESHAPE(x) -13%
PERMUTE composition PERMUTE(PERMUTE(x)) → identity included
SiLU-gate fusion x * sigmoid(x) * upswiglu_split -9%
Graph cache (default on) Skip rebuild on same shape 0 ms build
Mutable KV cache ggml_set_rows on GPU, no CPU fallback 1 split

Environment Variables

Variable Values Description
GGML_PERF_LOG 1 Per-call timing breakdown
GGML_PROFILE 1 Per-op timing (adds sync overhead)
GGML_NO_GRAPH_CACHE 1 Disable graph caching (debug)
GGML_DEBUG_DUMP <path> Per-node tensor stats
GGML_SKIP_OUTPUT_COPY 1 Skip logits GPU→CPU copy (CUDA only)

Model Details

  • Base model: Qwen/Qwen3-0.6B
  • Parameters: 596M
  • Architecture: 28 layers, 16 attention heads, 8 KV heads, head_dim=128
  • Quantization: Q8_0 (weights only; KV cache is F32)
  • Max sequence length: 128 (exported)
  • Framework: ExecuTorch with ggml backend