1# Hexagon backend developer details
  2
  3## Backend libraries
  4
  5The Hexagon backend consist of two parts:
  6
  7  - `libggml-hexagon`
  8    This is the regular CPU-side GGML backend library, either shared or statically linked
  9
 10  - `libggml-htp-vNN`
 11    This is the NPU-side (HTP stands for Hexagon Tensor Processor) shared library that contains the Op dispatcher and kernels.
 12    The correct library is selected automatically at runtime based on the HW version.
 13
 14Here is an example of the build artifacts
 15
 16```
 17~/src/llama.cpp$ ls -l pkg-adb/llama.cpp/lib/libggml*
 18pkg-adb/llama.cpp/lib/libggml-base.so
 19pkg-adb/llama.cpp/lib/libggml-cpu.so
 20pkg-adb/llama.cpp/lib/libggml-hexagon.so      <<< CPU library
 21pkg-adb/llama.cpp/lib/libggml-htp-v73.so      <<< HTP op/kernels for Hexagon v73
 22pkg-adb/llama.cpp/lib/libggml-htp-v75.so
 23pkg-adb/llama.cpp/lib/libggml-htp-v79.so
 24pkg-adb/llama.cpp/lib/libggml-htp-v81.so
 25```
 26
 27## Memory buffers
 28
 29Hexagon NPU backend takes advantage of the Snapdragon's unified memory model where all buffers are fully accessible by the CPU and GPU.
 30The NPU does have a dedicated tightly-coupled memory called VTCM but that memory is used only for intermediate data (e.g. dynamically
 31quantized tensors) or temporary data (chunks of the weight tensors fetched via DMA).
 32
 33Please note that currently the Hexagon backend does not implement SET/GET_ROWS Ops because there is no advantage in offloading those
 34to the NPU at this point.
 35
 36The backend does allocates non-host buffers for the tensors with datatypes that require repacking: Q4_0, Q8_0, MXFP4.
 37From the MMU perspective these buffers are still regular buffers (normal access by the CPU) they are marked as non-host simply to force
 38the repacking.
 39
 40## Large model handling
 41
 42Hexagon NPU session (aka Process Domain (PD) in the Hexagon docs) is limited to a memory mapping of around 3.5GB.
 43In llama.cpp/GGML the Hexagon session is mapped to a single GGML backend device (HTP0, HTP1, etc).
 44
 45In order to map models larger than 3.5GB we need to allocate multiple devices and split the model.
 46For this we're taking advantage of the llama.cpp/GGML multi-GPU layer-splitting support.
 47Each Hexagon device behaves like a GPU from the offload and model splitting perspective.
 48
 49Here is an example of running GPT-OSS-20B model on a newer Snapdragon device with 16GB of DDR.
 50
 51```
 52M=gpt-oss-20b-Q4_0.gguf NDEV=4 D=HTP0,HTP1,HTP2,HTP3 P=surfing.txt scripts/snapdragon/adb/run-completion.sh -f surfing.txt -n 32
 53...
 54LD_LIBRARY_PATH=/data/local/tmp/llama.cpp/lib
 55ADSP_LIBRARY_PATH=/data/local/tmp/llama.cpp/lib
 56GGML_HEXAGON_NDEV=4 ./bin/llama-cli --no-mmap -m /data/local/tmp/llama.cpp/../gguf/gpt-oss-20b-Q4_0.gguf
 57      -t 4 --ctx-size 8192 --batch-size 128 -ctk q8_0 -ctv q8_0 -fa on -ngl 99 --device HTP0,HTP1,HTP2,HTP3 -no-cnv -f surfing.txt
 58...
 59llama_model_loader: - type  f32:  289 tensors
 60llama_model_loader: - type q4_0:   96 tensors
 61llama_model_loader: - type q8_0:    2 tensors
 62llama_model_loader: - type mxfp4:  72 tensors
 63...
 64load_tensors: offloaded 25/25 layers to GPU
 65load_tensors:          CPU model buffer size =  1182.09 MiB
 66load_tensors:         HTP1 model buffer size =     6.64 MiB
 67load_tensors:  HTP1-REPACK model buffer size =  2505.94 MiB
 68load_tensors:         HTP3 model buffer size =     5.55 MiB
 69load_tensors:  HTP3-REPACK model buffer size =  2088.28 MiB
 70load_tensors:         HTP0 model buffer size =     7.75 MiB
 71load_tensors:  HTP0-REPACK model buffer size =  2923.59 MiB
 72load_tensors:         HTP2 model buffer size =     6.64 MiB
 73load_tensors:  HTP2-REPACK model buffer size =  2505.94 MiB
 74...
 75llama_context: n_ctx_per_seq (8192) < n_ctx_train (131072) -- the full capacity of the model will not be utilized
 76llama_context:        CPU  output buffer size =     0.77 MiB
 77llama_kv_cache_iswa: creating non-SWA KV cache, size = 8192 cells
 78llama_kv_cache:       HTP1 KV buffer size =    25.50 MiB
 79llama_kv_cache:       HTP3 KV buffer size =    25.50 MiB
 80llama_kv_cache:       HTP0 KV buffer size =    25.50 MiB
 81llama_kv_cache:       HTP2 KV buffer size =    25.50 MiB
 82llama_kv_cache: size =  102.00 MiB (  8192 cells,  12 layers,  1/1 seqs), K (q8_0):   51.00 MiB, V (q8_0):   51.00 MiB
 83llama_kv_cache_iswa: creating     SWA KV cache, size = 256 cells
 84llama_kv_cache:       HTP1 KV buffer size =     0.80 MiB
 85llama_kv_cache:       HTP3 KV buffer size =     0.53 MiB
 86llama_kv_cache:       HTP0 KV buffer size =     1.06 MiB
 87llama_kv_cache:       HTP2 KV buffer size =     0.80 MiB
 88llama_kv_cache: size =    3.19 MiB (   256 cells,  12 layers,  1/1 seqs), K (q8_0):    1.59 MiB, V (q8_0):    1.59 MiB
 89llama_context:       HTP0 compute buffer size =    16.06 MiB
 90llama_context:       HTP1 compute buffer size =    16.06 MiB
 91llama_context:       HTP2 compute buffer size =    16.06 MiB
 92llama_context:       HTP3 compute buffer size =    16.06 MiB
 93llama_context:        CPU compute buffer size =    98.19 MiB
 94...
 95llama_perf_context_print: prompt eval time =    3843.67 ms /   197 tokens ( 19.51 ms per token, 51.25 tokens per second)
 96llama_perf_context_print:        eval time =    1686.13 ms /    31 runs   ( 54.39 ms per token, 18.39 tokens per second)
 97llama_perf_context_print:       total time =    6266.30 ms /   228 tokens
 98llama_perf_context_print:    graphs reused =         30
 99llama_memory_breakdown_print: | memory breakdown [MiB] | total   free    self   model   context   compute    unaccounted |
100llama_memory_breakdown_print: |   - HTP0 (Hexagon)     |  2048 = 2048 + (   0 =     0 +       0 +       0) +           0 |
101llama_memory_breakdown_print: |   - HTP1 (Hexagon)     |  2048 = 2048 + (   0 =     0 +       0 +       0) +           0 |
102llama_memory_breakdown_print: |   - HTP2 (Hexagon)     |  2048 = 2048 + (   0 =     0 +       0 +       0) +           0 |
103llama_memory_breakdown_print: |   - HTP3 (Hexagon)     |  2048 = 2048 + (   0 =     0 +       0 +       0) +           0 |
104llama_memory_breakdown_print: |   - Host               |                 1476 =  1208 +     105 +     162                |
105llama_memory_breakdown_print: |   - HTP1-REPACK        |                 2505 =  2505 +       0 +       0                |
106llama_memory_breakdown_print: |   - HTP3-REPACK        |                 2088 =  2088 +       0 +       0                |
107llama_memory_breakdown_print: |   - HTP0-REPACK        |                 2923 =  2923 +       0 +       0                |
108llama_memory_breakdown_print: |   - HTP2-REPACK        |                 2505 =  2505 +       0 +       0                |
109```