239 lines
4.7 KiB
Markdown
239 lines
4.7 KiB
Markdown
# Note tecniche e cronologia del kNN OpenCL
|
|
|
|
Questo documento raccoglie il percorso sperimentale che ha portato al motore kNN OpenCL. Per installazione, compilazione e uso dei binari consultare `README.md`.
|
|
|
|
## Obiettivo
|
|
|
|
Input:
|
|
|
|
```text
|
|
float32[N][128]
|
|
```
|
|
|
|
Gli embedding sono normalizzati L2, quindi cosine similarity e prodotto scalare coincidono. Output:
|
|
|
|
```text
|
|
indices[query_count][TOPK]
|
|
scores[query_count][TOPK]
|
|
```
|
|
|
|
## Baseline
|
|
|
|
```text
|
|
DIM=128
|
|
TOPK=32
|
|
QUERIES=256
|
|
LANES_PER_QUERY=8
|
|
CHUNK_SIZE=50000
|
|
```
|
|
|
|
## Risultati sperimentali principali
|
|
|
|
### Numero di lane
|
|
|
|
```text
|
|
LANES=4: 9.60 GOP/s
|
|
LANES=8: 10.57 GOP/s
|
|
LANES=16: 4.04 GOP/s
|
|
LANES=32: 1.49 GOP/s
|
|
```
|
|
|
|
Otto lane sono risultate la configurazione più equilibrata.
|
|
|
|
### Stage benchmark
|
|
|
|
```text
|
|
DOT: 72.79 M vector/s
|
|
DOT+threshold: 83.47 M vector/s
|
|
TOPK score: 77.94 M vector/s
|
|
TOPK+indici: 76.28 M vector/s
|
|
kNN completo: 75.22 M vector/s
|
|
```
|
|
|
|
### Vettorizzazione
|
|
|
|
```text
|
|
scalar: 74.37 M vector/s
|
|
float4: 84.14 M vector/s
|
|
float8: 83.49 M vector/s
|
|
```
|
|
|
|
Database e query entrambi float4 hanno raggiunto 84.13 M vector/s.
|
|
|
|
### Accumulatori
|
|
|
|
```text
|
|
F4 singolo accumulatore: 74.53 M vector/s
|
|
F4 quattro accumulatori: 83.47 M vector/s
|
|
F4 quattro acc + unroll2: 70.53 M vector/s
|
|
F8 quattro accumulatori: 82.76 M vector/s
|
|
```
|
|
|
|
L'unrolling esplicito ha peggiorato il code generation del compilatore Mali.
|
|
|
|
### Pressione sui registri
|
|
|
|
Una query mantenuta in `private float4 query[DIM4]` ha ridotto le prestazioni a 17.85 M vector/s. Rimuovendo l'array privato si è risaliti a circa 65 M vector/s.
|
|
|
|
### Strategie TOPK
|
|
|
|
```text
|
|
sorted insertion: 78.37 M vector/s
|
|
min-replace: 81.99 M vector/s
|
|
threshold + min-replace: 82.02 M vector/s
|
|
```
|
|
|
|
### Dimensione TOPK
|
|
|
|
```text
|
|
TOPK=8: 73.87 M vector/s
|
|
TOPK=16: 84.60 M vector/s
|
|
TOPK=32: 82.53 M vector/s
|
|
TOPK=64: 78.81 M vector/s
|
|
```
|
|
|
|
## Benchmark da 350000 embedding
|
|
|
|
```text
|
|
Database: 350000
|
|
Query: 256
|
|
Dimensione: 128
|
|
TOPK: 32
|
|
Chunk: 50000
|
|
Confronti: 89600000
|
|
```
|
|
|
|
Variante min-replace:
|
|
|
|
```text
|
|
KNN kernel: 1244.576 ms
|
|
Merge GPU: 8.022 ms
|
|
Merge CPU globale: 14.039 ms
|
|
Tempo complessivo: 1266.638 ms
|
|
Vector rate: 71.992 M vector/s
|
|
Dot rate: 18.430 GFLOP/s
|
|
Traffico: 36.860 GB/s
|
|
Indici non validi: 0
|
|
```
|
|
|
|
Benchmark parametrico:
|
|
|
|
```text
|
|
CPU: 9663.226 ms
|
|
OpenCL: 1373.797 ms
|
|
Speedup: 7.034x
|
|
Posizioni TOPK identiche: 8192 / 8192
|
|
Exact ratio: 100%
|
|
```
|
|
|
|
## Trasformazione in motore dinamico
|
|
|
|
Il kernel produttivo usa definizioni configurabili:
|
|
|
|
```c
|
|
#ifndef DIM
|
|
#define DIM 128
|
|
#endif
|
|
|
|
#ifndef TOPK
|
|
#define TOPK 32
|
|
#endif
|
|
|
|
#ifndef QUERY_COUNT
|
|
#define QUERY_COUNT 1
|
|
#endif
|
|
|
|
#ifndef LANES_PER_QUERY
|
|
#define LANES_PER_QUERY 8
|
|
#endif
|
|
```
|
|
|
|
Il limite fisso `q >= 256` è diventato `q >= QUERY_COUNT`. Le definizioni vengono passate a `clBuildProgram` insieme a `-cl-fast-relaxed-math`.
|
|
|
|
## Backend CPU
|
|
|
|
È stato generalizzato per conteggi e dimensione dinamici, TOPK sicuro, ordinamento deterministico, rifiuto di valori non finiti e layout identico a OpenCL.
|
|
|
|
## Backend OpenCL
|
|
|
|
Il backend:
|
|
|
|
1. enumera piattaforme e GPU;
|
|
2. seleziona preferibilmente Mali-G610;
|
|
3. carica il kernel da file;
|
|
4. compila con parametri runtime;
|
|
5. alloca buffer riutilizzabili;
|
|
6. copia il database a chunk;
|
|
7. esegue il kernel;
|
|
8. legge indici e score;
|
|
9. converte indici locali in globali;
|
|
10. effettua il merge TOPK;
|
|
11. rilascia le risorse tramite RAII.
|
|
|
|
## Diagnostica Mali
|
|
|
|
Il runtime Mali emette `arm_release_ver` direttamente su stdout. La CLI devia temporaneamente stdout verso stderr durante OpenCL e lo ripristina prima della scrittura JSON.
|
|
|
|
## Validazione reale
|
|
|
|
Prototype:
|
|
|
|
```text
|
|
0 IMG_0145.JPG cluster_medoid
|
|
1 IMG_0141.JPG cluster_medoid
|
|
2 IMG_0193.JPG cluster_medoid
|
|
3 IMG_0143.JPG greedy_leave_one_out_gain
|
|
4 IMG_0146.JPG greedy_leave_one_out_gain
|
|
```
|
|
|
|
Risultato:
|
|
|
|
```text
|
|
Query: 21
|
|
Prototype: 5
|
|
Riconosciuti: 20
|
|
Sotto soglia: 1
|
|
Copertura: 95.2%
|
|
Caso sotto soglia: IMG_0183.JPG
|
|
Score: 0.466428
|
|
Miglior prototype: IMG_0143.JPG
|
|
```
|
|
|
|
Python contro C++ CPU:
|
|
|
|
```text
|
|
Query confrontate: 21
|
|
Mismatch top-1: 0
|
|
Mismatch decisione: 0
|
|
Errore massimo: 2.643e-7
|
|
```
|
|
|
|
C++ CPU contro OpenCL:
|
|
|
|
```text
|
|
Dopo leave-one-out: 100 / 100 posizioni identiche
|
|
TOPK grezzo: 105 / 105 posizioni identiche
|
|
Errore massimo: 3.58e-7
|
|
```
|
|
|
|
## File di riferimento
|
|
|
|
```text
|
|
modules/knn-opencl/reference/knn_benchmark.cpp
|
|
modules/knn-opencl/reference/knn_float4_minreplace_validate_benchmark.cpp
|
|
modules/knn-opencl/reference/validation-opencl-350k.txt
|
|
modules/knn-opencl/reference/validation-minreplace-350k.txt
|
|
modules/knn-opencl/reference/VALIDATION.md
|
|
VALIDATION_JESSICA.md
|
|
```
|
|
|
|
## Sviluppi futuri
|
|
|
|
```text
|
|
backend auto
|
|
cache del programma OpenCL
|
|
indice persistente dei prototype
|
|
margine fra prima e seconda identità
|
|
modalità shadow applicativa
|
|
metriche operative
|
|
```
|