Skip to main content

Overview

Off Grid leverages platform-specific hardware accelerators to achieve real-time on-device AI inference:
  • Android: OpenCL (GPU) for LLMs, Qualcomm QNN (NPU) for image generation
  • iOS: Metal (GPU) for LLMs, Apple Neural Engine (ANE) for image generation
All acceleration is optional with automatic CPU fallback. The app detects hardware capabilities at runtime and degrades gracefully.

LLM Inference Acceleration

OpenCL GPU Offloading (Android)

Llama.cpp supports GPU acceleration via OpenCL on Qualcomm Adreno GPUs. Architecture:
Layer Offloading:
llama.cpp splits the model:
  • First nGpuLayers layers → GPU via OpenCL
  • Remaining layers → CPU via ARM NEON
Compatibility Matrix: Known Issues:
Crash during initialization: Some Adreno GPUs crash when offloading >40 layers. Start with 0 GPU layers and incrementally increase by 8-10 layers while testing stability.
Flash Attention Conflict:
llama.cpp’s flash attention implementation conflicts with OpenCL backend. Attempting to use both causes SIGSEGV.

Metal GPU Acceleration (iOS)

Llama.cpp uses Metal Performance Shaders (MPS) for GPU acceleration on Apple Silicon. Architecture:
Layer Offloading:
On iOS, offloading all layers (nGpuLayers: 99) typically provides best performance since Metal backend is stable.
Unified Memory Advantage: Apple Silicon uses unified memory (CPU and GPU share RAM). Benefits:
  • Zero-copy tensor transfers (no CPU↔GPU memcpy)
  • Lower latency for small batches
  • Higher effective memory bandwidth
CLIP GPU Acceleration (Vision Models):
CLIP image encoder runs on Metal GPU (if enabled). Provides 2-3x speedup for vision inference. Known Issues:
M1 iPad Air throttling: Sustained generation (>2 minutes) may trigger thermal throttling, reducing GPU clocks by 20-30%. Performance degrades to ~70% of initial speed.

Image Generation Acceleration

Qualcomm QNN NPU (Android)

Qualcomm AI Engine Direct (QNN) accelerates Stable Diffusion on the Hexagon DSP (Neural Processing Unit). Architecture:
Supported Chipsets:
SoC: SM8450
HTP Version: V68
Performance: ~8-12s for 512×512 @ 20 steps
Models: qnn-min variant (conservative optimizations)
QNN Model Format: Stable Diffusion models pre-compiled for QNN:
  • unet.bin — UNet denoising network (HTP-optimized)
  • vae_decoder.bin — VAE decoder (HTP-optimized)
  • clip.bin or clip.mnn — Text encoder (CPU or MNN)
Models from xororz/sd-qnn-* HuggingFace repos. Runtime Library Selection:
libQnnHtp.so automatically loads chipset-specific variant:
  • Detects HTP version via /sys/devices/soc0/soc_id
  • Loads corresponding libQnnHtpV73.so, libQnnHtpV75.so, etc.
  • Falls back to oldest compatible version if exact match unavailable
Environment Variables:
  • DSP_LIBRARY_PATH — Hexagon DSP firmware location
  • ADSP_LIBRARY_PATH — Audio DSP library path (required for some SoCs)
Performance Comparison: QNN provides 2-3x speedup with 40% lower power consumption. Known Issues:
SELinux blocking DSP access: Some devices (Xiaomi, OPPO with custom Android skins) have SELinux policies that block FastRPC. QNN initialization fails with “Failed to create backend.” Solution: Use MNN (CPU) fallback.
Cache Warmup: First generation after model load takes 60-120s for QNN to:
  1. Analyze compute graph
  2. Compile ops for HTP
  3. Cache compiled binaries to /data/local/tmp/qnn_cache/
Subsequent generations skip warmup and start immediately.

Apple Neural Engine (iOS)

Apple’s dedicated AI accelerator for Core ML inference. Architecture:
Compute Units:
Core ML automatically dispatches ops to ANE or CPU based on compatibility. ANE Specifications:
TOPs: 16 (trillion ops/sec)
Architecture: 3rd-gen ANE
Performance: ~10-12s for SD 1.5 fp16 @ 20 steps
Found in: iPhone 15 Pro, iPhone 15 Pro Max
Op Compatibility: Core ML dispatches to ANE if:
  • Op is convolution, matrix multiply, or supported activation
  • Tensor shapes are ANE-compatible (batch size 1, certain dimension alignments)
  • Precision is fp16 or int8
Fallback to CPU:
  • Custom ops (e.g., some schedulers)
  • Unsupported tensor shapes
  • Dynamic shapes (rare in Stable Diffusion)
Performance by Model Type: Full-precision models run faster because ANE doesn’t need to dequantize weights. Power Efficiency: ANE power consumption: ~1-2W during inference (vs. 5-8W for GPU). iPhone 15 Pro generates 10 images on a full charge with screen off.

RAM-Based Limitations

Off Grid enforces RAM-based safety limits to prevent OS-level kills and crashes.

Pre-Load Memory Checks

Memory Budget:
Warning Thresholds:
User can proceed but should close background apps.

Runtime Context Caps

Prevents users from setting 8K context on 4GB devices (would cause abort() during KV cache allocation). Auto-Scaling Context:
User’s 8192 context setting automatically reduced to 2048 on 4GB devices.

Overload Prevention in ARCHITECTURE.md

From source docs:
RAM-Aware Runtime Safeguards: On low-RAM devices (e.g. iPhone XS with 4GB), llama.cpp and CLIP can call abort() during Metal GPU allocation, which is a POSIX signal that bypasses JavaScript try/catch and kills the app instantly. To prevent this, initWithAutoContext() applies device-RAM-based caps before any native call:
Implementation:
This runs before initContextWithFallback(), so the dangerous Metal allocation is never attempted.

Performance Benchmarks

Text Generation (Tokens/Sec)

Android (Snapdragon 8 Gen 3): iOS (A17 Pro):

Image Generation (Seconds)

Android: iOS:

Vision Inference (Seconds)

Best Practices

For LLM Inference

  1. Start conservative: Begin with 0 GPU layers, incrementally increase by 8-10 layers while monitoring stability
  2. Monitor temperature: Sustained generation can trigger thermal throttling on mobile devices
  3. Reduce context on ≤4GB devices: Use 2048 max context to prevent OOM
  4. Disable flash attention with GPU: Auto-disabled on Android, but verify on custom builds

For Image Generation

  1. Prefer NPU/ANE over CPU: 2-3x faster with lower power consumption
  2. Use fp16 models on iOS: Palettized models are 2x slower due to dequantization overhead
  3. Cache warmup on first run: Warn users first generation will be slow (QNN cache building)
  4. Unload text model before image generation: Prevents RAM contention on 4-6GB devices

For Memory Management

  1. Respect 60% RAM budget: Prevents OS kills
  2. Warn at 50%: Give users option to close background apps
  3. Unload before switching models: Releases GPU/ANE resources
  4. Monitor with DeviceInfoScreen: Real-time RAM usage in settings

Debugging Acceleration Issues

OpenCL (Android)

Common errors:
  • CL_OUT_OF_HOST_MEMORY — Reduce GPU layers
  • CL_DEVICE_NOT_FOUND — GPU not detected, use CPU
  • SIGSEGV during clBuildProgram — OpenCL compiler crash, disable GPU

Metal (iOS)

Common errors:
  • MTLBuffer allocation failed — Reduce context size or GPU layers
  • abort() in mtl_backend_init — ≤4GB device, GPU layers should be 0

QNN (Android)

Common errors:
  • Failed to create backend — SELinux blocking DSP, use MNN fallback
  • libQnnHtp.so not found — Runtime libs not extracted, check prepareRuntimeDir()

ANE (iOS)

Use Xcode Instruments → Core ML:
  1. Profile → Core ML
  2. Run image generation
  3. Check “ANE Utilization” track
  4. Should be over 80% during UNet inference
Low utilization (under 50%) indicates fallback to CPU — model may not be ANE-optimized.

References

  • llama.cpp Backends: https://github.com/ggerganov/llama.cpp/tree/master/docs/backend
  • Qualcomm AI Engine: https://www.qualcomm.com/developer/software/qualcomm-ai-engine-direct
  • Apple Neural Engine: https://github.com/hollance/neural-engine (reverse engineering)
  • Core ML Performance: https://developer.apple.com/documentation/coreml/optimizing_your_model_for_the_neural_engine
  • Metal Performance Shaders: https://developer.apple.com/documentation/metalperformanceshaders