Benchmarks
tools/bench/openads_bench genera un DBF sintético de 100 000
filas (ID N(8,0), TAG C(4), AMT N(8,2)) y mide un conjunto
fijo de cargas SQL a través de la ABI pública
(AdsExecuteSQLDirect). Mediana de 5 repeticiones por carga,
builds Release.
Resultados v0.4.x (2026-05-06)
| Carga (mediana ms) | Windows MSVC | Linux clang -O3 | macOS AppleClang |
|---|---|---|---|
| crear DBF 100 k filas | 63.5 | 57.9 | 34.0 |
SELECT COUNT(*) |
297.7 | 42.0 | 103.9 |
WHERE TAG = 'AAAA' |
303.7 | 48.3 | 108.4 |
SUM/AVG/MIN/MAX(AMT) |
374.3 | 120.5 | 136.1 |
GROUP BY TAG |
321.9 | 58.6 | 120.9 |
ORDER BY AMT LIMIT 10 |
668.0 | 165.4 | 260.5 |
DISTINCT TAG |
598.4 | 95.2 | 213.4 |
BETWEEN 100 AND 500 |
314.1 | 63.7 | 114.4 |
Linux clang -O3 gana en todas las cargas SQL — aproximadamente
7× más rápido que MSVC Release en el COUNT de tabla completa,
4× en el ORDER BY más pesado. macOS Intel queda en medio.
Bench v2 — cargas con índices (Windows MSVC, 100 k filas)
| Carga (mediana ms) | ms |
|---|---|
CREATE INDEX ID_IDX |
38.0 |
WHERE ID = 50000 (post-índice) |
308.0 |
WHERE ID BETWEEN 10000 AND 20000 |
308.2 |
UNION ALL de dos selects filtradas |
608.2 |
GROUP BY TAG HAVING COUNT(*) > 100 |
0.2 |
Que indexed_eq ~308 ms ≈ seq_walk_where ~315 ms expone una
oportunidad conocida: el planner SQL actualmente NO empuja los
predicados WHERE a un índice CDX/NTX coincidente. Cerrar esa
brecha es un milestone futuro.
Bench v3 — AOF (Rushmore-style) (rc12, 100 k filas)
AdsSetAOF parsea + evalúa la condición, instala un bitmap por
registro como predicado de filtro que Skip / GoTop honran, y
enruta cada hoja por range-scan de CDX / NTX cuando un índice
abierto tiene ese campo como key expr. AdsGetAOFOptLevel reporta
ADS_OPTIMIZED_FULL / PART / NONE según cobertura.
La navegación con bitmap sparse (M-AOF.5) lleva el walk del
visible-set de O(N) a O(M).
Mismo DBF sintético de 100 000 filas, todas medianas de 5
repeticiones, builds Release:
| Carga AOF | Win MSVC x64 | Linux clang -O3 | macOS AppleClang | OptLevel |
|---|---|---|---|---|
AdsSetAOF("TAG='AAAA'"), sin índice TAG |
593 ms | 93 ms | 210 ms | NONE |
AdsSetAOF("TAG='AAAA'"), con índice TAG |
323 ms | 58 ms | 119 ms | FULL |
AdsSetAOF("TAG BETWEEN 'AAAA' AND 'CCCC'"), idx |
24 ms | 4.5 ms | 9 ms | FULL |
Speedup vs baseline full-scan no-indexada (mismo host):
| Carga AOF | Win MSVC | Linux clang | macOS |
|---|---|---|---|
AdsSetAOF("TAG='AAAA'"), con índice TAG |
1.83× | 1.61× | 1.77× |
AdsSetAOF("TAG BETWEEN 'AAAA' AND 'CCCC'") |
24.4× | 20.7× | 23.4× |
Qué provoca la aceleración:
AdsSetAOFse convierte en range-scan sobre el índice en lugar de decode + AST eval por fila. Ganancia rc11 (M-AOF.4).Skip/GoTopsólo recorren los registros que pasan (navegación sparse, M-AOF.5) en vez de iterar cada recno consultando el predicado. Ganancia rc12 — la ventana “10-100×” tipo Rushmore para filtros selectivos.
El speedup de ~1.83× del eq-walk está limitado por el coste de
load_record_ por registro visible (~80 µs × 3848 matches ≈
310 ms suelo en Windows). Aplicaciones que no tocan el dato
matched — COUNT(*) sobre el AOF, o dbSeek puntual — entran
en la ventana Rushmore completa encima del range-scan gain.
Ejecutar en tu hardware
cmake --build build/default --target openads_bench --config Release
./build/default/tools/bench/openads_bench --rows 100000 --repeats 5 --csv
El flag --csv emite una fila CSV por carga.