Instructions for validating and running models downloaded by LocalNar using
llama-cli, llama-server, or llama-bench across any infrastructure.
LocalNar stores models in a structured on-disk hierarchy. Its root resolves in
precedence order: library.download_directory from the persisted settings file
at ${XDG_CONFIG_HOME:-$HOME/.config}/localnar/settings.toml, then
$LOCALNAR_MODELS_DIR, then the built-in default ~/.cache/localnar/models:
~/.cache/localnar/models/
└── <owner>/
└── <repository>/
└── <revision>/
├── <model-file>.gguf
├── <model-file>.gguf.sha256
└── <model-file>.gguf.tags
- Inside LocalNar TUI:
- Press
Alt+2(or cycle withTab) to switch to the Library screen. (Tabs useAlt+1for Search,Alt+2for Library,Alt+3for Settings, andAlt+4for Help). - Select the model using
↑/↓. - Press
iorEnterto inspect the model. The exact absolute path to the.gguffile is shown on screen.
- Press
- Via terminal (valid for the environment/default root only):
If you configured a Model Download Path in Settings, search that effective directory instead, or use the absolute path the TUI shows when inspecting the model.
find "${LOCALNAR_MODELS_DIR:-$HOME/.cache/localnar/models}" -name "*.gguf"
Before loading a model into an inference engine, verify that the download is complete and uncorrupted:
- Inside LocalNar: On the Library screen (
Alt+2orTab), select the model and pressv. LocalNar verifies against the digest it recorded locally when one exists, otherwise against the checksum the remote catalog advertises. The state badge confirms verified when a checksum is available in either place; with no checksum anywhere the replica stays unproven. - Via command line (requires an existing
.sha256sidecar / recorded digest):MODEL_PATH="/path/to/model.gguf" echo "$(cat "${MODEL_PATH}.sha256") ${MODEL_PATH}" | sha256sum -c
If the verification passes (OK), the model weights are intact.
Regardless of your system specs (NVIDIA, AMD, Apple Silicon, or pure CPU), calculate your offload parameters before launching:
| Scenario | Rule | Setting |
|---|---|---|
| Model fits fully in VRAM (e.g. 8B on 12 GB VRAM) | Offload all layers to GPU | -ngl 99 |
| Model larger than VRAM (e.g. 27B on 12 GB VRAM) | Offload as many layers as fit safely in free VRAM | -ngl <calculated> |
| No GPU / Pure CPU | Keep all layers in system RAM | -ngl 0 |
Calculating hybrid layers for large models:
Example: A 27B model (15,000 MB, 65 layers) on a 12 GB GPU with ~10,800 MB free:
Always set -t equal to your machine's physical CPU cores (not virtual/hyperthreaded cores). This prevents cache contention and SMT slowdown when layers run on CPU:
# On Linux, inspect physical core count:
lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l-c <tokens>: Sets context window size (e.g.4096,8192,16384).-fa on: Enables Flash Attention (significantly reduces VRAM usage for context).-ctk q8_0 -ctv q8_0: Quantizes key/value cache to 8-bit, cutting KV memory consumption in half with negligible accuracy loss.
Note:
llama-cli,llama-server,llama-bench,llama-perplexity, and the companion utilities below are external tools. LocalNar neither vendors nor invokes them; it only manages the model files they load.
Run llama-cli for a direct terminal conversation to test generation speed and output correctness:
llama-cli \
-m "/path/to/model.gguf" \
-ngl 99 \
-fa on \
-c 8192 \
-p "Explain the concept of entropy in simple terms:"llama-cli \
-m "/path/to/model.gguf" \
-ngl 40 \
-t 8 \
-fa on \
-c 8192 \
-ctk q8_0 \
-ctv q8_0 \
-b 2048 \
-ub 512Tip: For scripted non-interactive execution, pass
-st(or--single-turn) so the program outputs the generation and immediately exits instead of waiting for interactive user input in a conversation loop.
llama-cli \
-m "/path/to/model.gguf" \
-ngl 0 \
-t 8 \
-c 4096Start a local OpenAI-compatible API server:
llama-server \
-m "/path/to/model.gguf" \
-ngl 40 \
-t 8 \
-fa on \
-c 16384 \
-ctk q8_0 \
-ctv q8_0 \
--port 8080 \
--parallel 1Once loaded (model loaded, listening on http://127.0.0.1:8080), validate via curl:
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "Respond with one word: ready"}],
"max_tokens": 10
}'llama-bench strips away tokenizer, chat templating, and disk overhead to measure
pure hardware execution speed.
llama-bench \
-m "/path/to/model.gguf" \
-ngl 99 \
-fa 1 \
-p 512 \
-n 128Sample output:
| model | size | params | backend | ngl | fa | test | t/s |
| -------------- | -------- | ------- | ------- | --- | -- | ----- | --------------- |
| qwen2 14B Q4_0 | 7.93 GiB | 14.77 B | CUDA | 99 | 1 | pp512 | 2906.89 ± 72.39 |
| qwen2 14B Q4_0 | 7.93 GiB | 14.77 B | CUDA | 99 | 1 | tg128 | 60.47 ± 0.12 |
pp512(Prompt Processing / Prefill): Tokens/sec while ingesting a 512-token prompt. Runs dense matrix multiplications (GEMM), pushing GPU compute cores to 90%–100%.tg128(Text Generation / Decode): Tokens/sec while generating output tokens. Memory-bandwidth bound.
Pass comma-separated values to test multiple context lengths in a single run, and
use -o md to output directly in Markdown format:
llama-bench \
-m "/path/to/model.gguf" \
-ngl 99 \
-fa 1 \
-p 512,1024,2048 \
-n 0,128 \
-o mdTo measure the exact throughput degradation caused by leaving layers on the CPU:
llama-bench \
-m "/path/to/model.gguf" \
-ngl 0,20,40,99 \
-fa 1 \
-p 512 \
-n 64Perplexity (PPL) measures how accurately a model predicts text tokens. A lower score indicates higher quality and less degradation from quantization.
The -f flag requires a real text file on disk. You can evaluate perplexity immediately
using any local file (e.g. README.md):
llama-perplexity \
-m "/path/to/model.gguf" \
-f README.md \
-ngl 99 \
-c 512Sample output:
perplexity: calculating perplexity over 2 chunks, n_ctx=512, batch_size=2048
[1]11.3007,[2]10.5921,
Final estimate: PPL = 10.5921 +/- 1.45250
For standardized scores comparable against published model cards:
# 1. Download and extract the standard dataset
wget https://huggingface.co/datasets/ggml-org/ci/resolve/main/wikitext-2-raw-v1.zip
unzip wikitext-2-raw-v1.zip
# 2. Calculate standard perplexity
llama-perplexity \
-m "/path/to/model.gguf" \
-f wikitext-2-raw/wiki.test.raw \
-ngl 99 \
-c 2048When running a 14B or 27B model, you can pair it with a tiny draft model (e.g. 0.5B or 1.5B) to boost generation speed from ~60 t/s up to 90–110+ t/s:
llama-cli \
-m "/path/to/base-model-14B.gguf" \
-md "/path/to/draft-model-0.5B.gguf" \
-ngl 99 \
-ngld 99 \
-fa on| Tool | Purpose | Typical Command |
|---|---|---|
llama-tokenize |
Inspect token decomposition and count prompt tokens | llama-tokenize -m <model.gguf> -p "Sample text" |
llama-embedding |
Generate vector embeddings for RAG or search | llama-embedding -m <model.gguf> -p "Search query" |
llama-simple-chat |
Minimal chat interface with zero TUI dependencies | llama-simple-chat -m <model.gguf> -ngl 99 |
llama-quantize |
Convert unquantized (F16/BF16) weights to Q4_K_M or IQ4_XS |
llama-quantize <input.gguf> <output.gguf> Q4_K_M |
- Cause:
-nglwas set higher than available VRAM, or context size (-c) allocated too large a KV buffer. - Fix:
- Reduce
-nglby 2–4 layers. - Lower
-c(e.g. from16384to8192). - Add
-ctk q8_0 -ctv q8_0to compress the KV cache. - Ensure Flash Attention is enabled (
-fa on).
- Reduce
A common point of confusion during hybrid execution is why both GPU and CPU report low utilization percentages in nvidia-smi and top/htop:
-
The Sequential Ping-Pong (Duty Cycle):
- Generating each token is strictly sequential.
- The GPU computes layers 0–41 in ~40 ms. During this time, the CPU is completely idle (0% CPU).
- The CPU computes layers 42–64 in ~120 ms. During this time, the GPU is completely idle (0% GPU).
- Because both devices take turns waiting on each other, neither device can ever register a continuous 100% active duty cycle during generation.
-
The CPU Memory-Bandwidth Wall:
- Why doesn't the CPU spike to 100% during its turn?
- Single-token generation requires streaming ~5.5 GB of model weights from system DDR5 RAM to CPU cache once per token.
- Dual-channel DDR5 bandwidth (~50–60 GB/s) saturates almost instantaneously.
- Once the memory bus is saturated, the CPU execution units (ALUs/AVX cores) spend most clock cycles stalled waiting on memory cache lines.
- Operating system metrics (
top,htop) measure active instruction retirement across all logical threads (e.g. 16 threads on an 8-core CPU), so memory-stalled threads register as < 10%–15% CPU utilization.
-
How to observe 90%+ GPU compute utilization:
- Prompt Ingestion (Prefill): When feeding a large prompt (e.g. 1,000–2,000 tokens), the entire prompt is evaluated simultaneously via batch matrix-matrix multiplication (GEMM). During prefill, GPU tensor cores run at full parallelism, and GPU utilization will spike to 90%–100%.
# Test prompt prefill saturation with a large input: llama-cli -m "/path/to/model.gguf" -ngl 40 -fa on -c 8192 -p "$(seq -s ' ' 1000)"
- 100% GPU Offload: Use a model whose entire weight footprint fits inside VRAM (
-ngl 99). This eliminates the CPU wait entirely, producing continuous 80%–100% GPU utilization during generation.
- Prompt Ingestion (Prefill): When feeding a large prompt (e.g. 1,000–2,000 tokens), the entire prompt is evaluated simultaneously via batch matrix-matrix multiplication (GEMM). During prefill, GPU tensor cores run at full parallelism, and GPU utilization will spike to 90%–100%.