volvoxai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/bin/volvox.js +72 -0
- package/dist/v0.1.0/volvoxai.js +4664 -0
- package/dist/v0.1.0/volvoxai.min.js +1848 -0
- package/dist/v0.1.0/volvoxai.wasm +0 -0
- package/dist/volvoxai.js +4664 -0
- package/dist/volvoxai.min.js +1848 -0
- package/dist/volvoxai.wasm +0 -0
- package/docs/README.md +22 -0
- package/docs/browser-runtime.md +87 -0
- package/docs/efficientdet_tflite_vs_volvoxai.md +445 -0
- package/docs/microkernel_optimization_guide.md +153 -0
- package/docs/model-format.md +108 -0
- package/docs/models.md +103 -0
- package/docs/native-runtime.md +189 -0
- package/docs/operation_list.md +232 -0
- package/docs/operator_fusion_patterns.md +58 -0
- package/docs/quickstart.md +115 -0
- package/docs/roadmap.md +19 -0
- package/docs/testing.md +97 -0
- package/docs/textbook/01-foundations.md +233 -0
- package/docs/textbook/02-tinystories-language-model.md +300 -0
- package/docs/textbook/03-efficientdet-vision-model.md +281 -0
- package/docs/textbook/04-precision-and-quantization.md +208 -0
- package/docs/textbook/05-inside-the-engine.md +155 -0
- package/docs/textbook/06-native-engine-architecture.md +338 -0
- package/docs/textbook/07-glossary-and-next-steps.md +258 -0
- package/docs/textbook/README.md +85 -0
- package/docs/textbook/ko/01-foundations.md +231 -0
- package/docs/textbook/ko/02-tinystories-language-model.md +300 -0
- package/docs/textbook/ko/03-efficientdet-vision-model.md +277 -0
- package/docs/textbook/ko/04-precision-and-quantization.md +206 -0
- package/docs/textbook/ko/05-inside-the-engine.md +154 -0
- package/docs/textbook/ko/06-native-engine-architecture.md +333 -0
- package/docs/textbook/ko/07-glossary-and-next-steps.md +253 -0
- package/docs/textbook/ko/README.md +83 -0
- package/docs/xnnpack_optimization_guide.md +197 -0
- package/js/CPUEngine.js +241 -0
- package/js/Graph.js +49 -0
- package/js/GraphExecutor.js +1020 -0
- package/js/GraphLoader.js +282 -0
- package/js/ShaderLibrary.js +236 -0
- package/js/Tensor.js +25 -0
- package/js/Tokenizer.js +266 -0
- package/js/VolvoxAI.js +130 -0
- package/js/WasmEngine.js +378 -0
- package/js/WebNNEngine.js +169 -0
- package/js/index.js +11 -0
- package/js/ops/add.js +31 -0
- package/js/ops/argMax.js +33 -0
- package/js/ops/averagePool2D.js +38 -0
- package/js/ops/batchNorm2D.js +28 -0
- package/js/ops/cast.js +19 -0
- package/js/ops/clip.js +15 -0
- package/js/ops/concat2.js +18 -0
- package/js/ops/conv1D.js +35 -0
- package/js/ops/conv2D.js +70 -0
- package/js/ops/convTranspose2D.js +45 -0
- package/js/ops/crossAttention.js +69 -0
- package/js/ops/crossSDPA.js +41 -0
- package/js/ops/dequantizeLinear.js +9 -0
- package/js/ops/div.js +15 -0
- package/js/ops/embedding.js +14 -0
- package/js/ops/expand.js +24 -0
- package/js/ops/gELU.js +9 -0
- package/js/ops/gather.js +51 -0
- package/js/ops/gatherElements.js +33 -0
- package/js/ops/globalAveragePool.js +21 -0
- package/js/ops/hardSigmoid.js +12 -0
- package/js/ops/hardSwish.js +12 -0
- package/js/ops/interp1D.js +25 -0
- package/js/ops/layerNorm.js +25 -0
- package/js/ops/leakyReLU.js +10 -0
- package/js/ops/logSoftmax.js +15 -0
- package/js/ops/matMul.js +35 -0
- package/js/ops/maxPool2D.js +36 -0
- package/js/ops/meanHeight.js +17 -0
- package/js/ops/mul.js +31 -0
- package/js/ops/nonMaxSuppression.js +72 -0
- package/js/ops/pReLU.js +11 -0
- package/js/ops/pad.js +35 -0
- package/js/ops/profileX.js +22 -0
- package/js/ops/profileY.js +22 -0
- package/js/ops/rMSNorm.js +14 -0
- package/js/ops/reLU.js +8 -0
- package/js/ops/reduceMean.js +17 -0
- package/js/ops/reduceSum.js +19 -0
- package/js/ops/reshape.js +6 -0
- package/js/ops/resize.js +44 -0
- package/js/ops/sDPA.js +44 -0
- package/js/ops/siLU.js +8 -0
- package/js/ops/sigmoid.js +6 -0
- package/js/ops/slice.js +36 -0
- package/js/ops/softmax.js +18 -0
- package/js/ops/spatialSoftargmaxY.js +28 -0
- package/js/ops/split.js +24 -0
- package/js/ops/sub.js +11 -0
- package/js/ops/tanh.js +7 -0
- package/js/ops/transpose.js +34 -0
- package/js/ops/upsample2x.js +23 -0
- package/js/ops/where.js +15 -0
- package/package.json +33 -0
- package/shaders/add.wgsl +13 -0
- package/shaders/add3Relu.wgsl +23 -0
- package/shaders/addRelu.wgsl +22 -0
- package/shaders/averagePool2D.wgsl +24 -0
- package/shaders/batchNorm2D.wgsl +21 -0
- package/shaders/binaryBroadcast.wgsl +34 -0
- package/shaders/broadcastBinary.wgsl +26 -0
- package/shaders/clip.wgsl +10 -0
- package/shaders/concat2.wgsl +16 -0
- package/shaders/concatCopy.wgsl +10 -0
- package/shaders/concatSigmoidCopy.wgsl +16 -0
- package/shaders/conv1D.wgsl +37 -0
- package/shaders/conv2D.wgsl +80 -0
- package/shaders/conv2DDepthwise4.wgsl +74 -0
- package/shaders/conv2DDepthwise8.wgsl +66 -0
- package/shaders/conv2DPointwise16.wgsl +67 -0
- package/shaders/conv2DPointwise16Tile.wgsl +86 -0
- package/shaders/conv2DPointwise8.wgsl +85 -0
- package/shaders/conv2DPointwise8Vec2.wgsl +70 -0
- package/shaders/conv2DPointwise8Vec4.wgsl +65 -0
- package/shaders/conv2DRegularC3Out16.wgsl +75 -0
- package/shaders/convTranspose2D.wgsl +33 -0
- package/shaders/copy.wgsl +13 -0
- package/shaders/crossAttention.wgsl +140 -0
- package/shaders/crossAttentionF32.wgsl +98 -0
- package/shaders/crossSDPA.wgsl +74 -0
- package/shaders/dequantizeLinear.wgsl +14 -0
- package/shaders/div.wgsl +34 -0
- package/shaders/elementwise.wgsl +13 -0
- package/shaders/embedding.wgsl +22 -0
- package/shaders/expand.wgsl +18 -0
- package/shaders/gELU.wgsl +13 -0
- package/shaders/gather.wgsl +17 -0
- package/shaders/generalTranspose.wgsl +19 -0
- package/shaders/globalAveragePool.wgsl +19 -0
- package/shaders/hardSigmoid.wgsl +13 -0
- package/shaders/hardSwish.wgsl +13 -0
- package/shaders/interp1D.wgsl +28 -0
- package/shaders/layerNorm.wgsl +33 -0
- package/shaders/leakyReLU.wgsl +11 -0
- package/shaders/linearF32.wgsl +33 -0
- package/shaders/linearF32RowMajor.wgsl +24 -0
- package/shaders/linearInt8.wgsl +42 -0
- package/shaders/logSoftmax.wgsl +22 -0
- package/shaders/maxPool2D.wgsl +37 -0
- package/shaders/meanHeight.wgsl +18 -0
- package/shaders/mul.wgsl +32 -0
- package/shaders/nonMaxSuppression.wgsl +92 -0
- package/shaders/pReLU.wgsl +14 -0
- package/shaders/pad.wgsl +19 -0
- package/shaders/profileX.wgsl +28 -0
- package/shaders/profileY.wgsl +28 -0
- package/shaders/quantizeLinear.wgsl +69 -0
- package/shaders/rMSNorm.wgsl +21 -0
- package/shaders/reLU.wgsl +13 -0
- package/shaders/reduce.wgsl +17 -0
- package/shaders/resize.wgsl +52 -0
- package/shaders/sDPA.wgsl +71 -0
- package/shaders/siLU.wgsl +13 -0
- package/shaders/sigmoid.wgsl +13 -0
- package/shaders/slice.wgsl +26 -0
- package/shaders/softmax.wgsl +23 -0
- package/shaders/spatialSoftargmaxY.wgsl +32 -0
- package/shaders/split.wgsl +15 -0
- package/shaders/sub.wgsl +34 -0
- package/shaders/tanh.wgsl +13 -0
- package/shaders/upsample2x.wgsl +24 -0
- package/shaders/where.wgsl +12 -0
- package/volvoxai.wasm +0 -0
|
Binary file
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# VolvoxAI Documentation
|
|
2
|
+
|
|
3
|
+
This directory holds the detailed reference material for VolvoxAI. Start with
|
|
4
|
+
the root [README](../README.md) for the short overview, then use these pages for
|
|
5
|
+
the implementation details.
|
|
6
|
+
|
|
7
|
+
| Doc | What it covers |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| [quickstart.md](quickstart.md) | Build commands, browser bundle, CLI smoke tests, and model download commands. |
|
|
10
|
+
| [browser-runtime.md](browser-runtime.md) | Browser and Node runtime tiers: WebNN, WebGPU, WASM SIMD, and CPU. |
|
|
11
|
+
| [native-runtime.md](native-runtime.md) | Native C engine, native CLI tasks, GPU/NPU backends, Android cross-build notes. |
|
|
12
|
+
| [model-format.md](model-format.md) | Volvox blueprint format, safetensors loading, tensor layout, precision policy. |
|
|
13
|
+
| [models.md](models.md) | Regenerating EfficientDet, TinyStories, and TinyReceiptKIE model packages. |
|
|
14
|
+
| [operation_list.md](operation_list.md) | Per-op backend support matrix for browser and native runtimes. |
|
|
15
|
+
| [testing.md](testing.md) | WebGPU op tests, native smoke tests, parity checks, and known validation limits. |
|
|
16
|
+
| [roadmap.md](roadmap.md) | Current gaps and planned work. |
|
|
17
|
+
| [textbook/](textbook/README.md) | A from-scratch walkthrough of inference, tensors, models, and engine internals. |
|
|
18
|
+
| [efficientdet_tflite_vs_volvoxai.md](efficientdet_tflite_vs_volvoxai.md) | EfficientDet Lite0 CPU/GPU benchmark methodology and results. |
|
|
19
|
+
| [microkernel_optimization_guide.md](microkernel_optimization_guide.md) | CPU Conv/GEMM microkernel notes. |
|
|
20
|
+
| [operator_fusion_patterns.md](operator_fusion_patterns.md) | Graph fusion patterns applied by the native optimizer. |
|
|
21
|
+
| [xnnpack_optimization_guide.md](xnnpack_optimization_guide.md) | XNNPACK-style packing and indirection reference notes. |
|
|
22
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Browser and Node Runtime
|
|
2
|
+
|
|
3
|
+
VolvoxAI exposes one browser API and chooses the best available execution tier at
|
|
4
|
+
initialization and compilation time.
|
|
5
|
+
|
|
6
|
+
## Backend Selection
|
|
7
|
+
|
|
8
|
+
`VolvoxAI.init(preferredBackend = "auto", wasmUrl = "./volvoxai.wasm")` accepts a
|
|
9
|
+
backend string or a strict ordered backend array.
|
|
10
|
+
|
|
11
|
+
| Call | Behavior |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `VolvoxAI.init()` or `VolvoxAI.init("auto")` | Try WebNN, then WebGPU, then WASM, then CPU. |
|
|
14
|
+
| `VolvoxAI.init("webnn")` | Prefer WebNN; fall back to WASM then CPU. |
|
|
15
|
+
| `VolvoxAI.init("webgpu")` | Prefer WebGPU; fall back to WASM then CPU. |
|
|
16
|
+
| `VolvoxAI.init("wasm")` | Skip WebNN/WebGPU and use WASM with CPU fallback. |
|
|
17
|
+
| `VolvoxAI.init("cpu")` | CPU only. |
|
|
18
|
+
| `VolvoxAI.init(["wasm", "webgpu"])` | Strict allow-list. No CPU fallback unless `"cpu"` is included. |
|
|
19
|
+
|
|
20
|
+
Array mode is strict. `init()` throws if none of the listed backends initializes,
|
|
21
|
+
and `compile()` throws if the initialized backends cannot compile the graph.
|
|
22
|
+
|
|
23
|
+
## Tier 1: WebNN
|
|
24
|
+
|
|
25
|
+
`WebNNEngine` builds an `MLGraphBuilder` graph and dispatches through
|
|
26
|
+
`navigator.ml`. The browser may route work to NPU, GPU, or CPU depending on the
|
|
27
|
+
platform, browser flags, drivers, and supported ops.
|
|
28
|
+
|
|
29
|
+
Current WebNN coverage:
|
|
30
|
+
|
|
31
|
+
`MatMul`/`Linear`/`Gemm`, `Add`, `Mul`, `ReLU`, `GELU`, `SiLU`/`Swish`,
|
|
32
|
+
`Sigmoid`, `Softmax`, `Reshape`/`Flatten`, `LayerNorm`, `Conv2D`,
|
|
33
|
+
`Embedding`, and `SDPA` decomposed into lower-level WebNN ops.
|
|
34
|
+
|
|
35
|
+
Missing low-cost mappings include `Sub`, `Div`, `Tanh`, `Clip`, `LeakyReLU`,
|
|
36
|
+
`PReLU`, `HardSwish`, `HardSigmoid`, `Transpose`, `Concat`, `Split`, `Slice`,
|
|
37
|
+
`Pad`, `Where`, `Gather`, `Cast`, `Expand`, reductions, pooling, batch norm,
|
|
38
|
+
resampling, and `ConvTranspose2D`.
|
|
39
|
+
|
|
40
|
+
Missing composite formulas include `RMSNorm`, `CrossSDPA`, `CrossAttention`,
|
|
41
|
+
`LogSoftmax`, `DequantizeLinear`, and `Conv1D` as a reshape plus `conv2d`.
|
|
42
|
+
|
|
43
|
+
WebNN requires a secure context such as HTTPS or localhost. Chromium WebNN support
|
|
44
|
+
is evolving and often needs `--enable-features=WebMachineLearningNeuralNetwork`.
|
|
45
|
+
`deviceType: "npu"` is not proof that an NPU executed the work; unsupported
|
|
46
|
+
accelerators can fall back to CPU.
|
|
47
|
+
|
|
48
|
+
Useful references:
|
|
49
|
+
|
|
50
|
+
- W3C WebNN: <https://www.w3.org/TR/webnn/>
|
|
51
|
+
- WebNN compatibility: <https://webnn.io/en/api-reference/browser-compatibility/api>
|
|
52
|
+
- Chromium flags: <https://webnn.io/en/api-reference/browser-compatibility/chrome-flags>
|
|
53
|
+
|
|
54
|
+
## Tier 2: WebGPU
|
|
55
|
+
|
|
56
|
+
`GraphExecutor` allocates one GPU buffer per tensor, uploads weights once, and
|
|
57
|
+
builds one compute pipeline per node. `execute()` replays the prebuilt pipelines in
|
|
58
|
+
one command encoder, flushing every 20 dispatches.
|
|
59
|
+
|
|
60
|
+
The WebGPU path keeps the model resident on the GPU and returns a `GPUBuffer`.
|
|
61
|
+
Current limitation: it returns the final node's first output rather than a map of
|
|
62
|
+
all `graph.outputNames`. Multi-output models should use WASM/CPU until WebGPU
|
|
63
|
+
multi-output readback is implemented.
|
|
64
|
+
|
|
65
|
+
Unsupported WebGPU shader nodes currently warn and skip, leaving the output buffer
|
|
66
|
+
unwritten. Use [operation_list.md](operation_list.md) to check whether a model's
|
|
67
|
+
ops are safe on WebGPU.
|
|
68
|
+
|
|
69
|
+
## Tier 3: WASM SIMD
|
|
70
|
+
|
|
71
|
+
`WasmEngine` loads the freestanding `volvoxai.wasm` module built from
|
|
72
|
+
`native/kernels.c`. It uses a bump allocator over `__heap_base` and places graph
|
|
73
|
+
tensors in WASM linear memory. Some pointwise, broadcast, and gather operations are
|
|
74
|
+
handled in JS over typed-array views into the same heap.
|
|
75
|
+
|
|
76
|
+
## Tier 4: CPU
|
|
77
|
+
|
|
78
|
+
`CPUEngine` is the pure-JS reference backend. It is slower, but dependency-free and
|
|
79
|
+
useful for debugging, fallback behavior, and cross-tier correctness checks.
|
|
80
|
+
|
|
81
|
+
## Node Import Behavior
|
|
82
|
+
|
|
83
|
+
`ShaderLibrary.js` statically imports every `shaders/*.wgsl` file as text, which
|
|
84
|
+
only works in the bundled browser build. It is not re-exported from `js/index.js`.
|
|
85
|
+
`GraphExecutor` lazy-loads it only when the WebGPU path compiles, so plain Node
|
|
86
|
+
imports can use WASM/CPU without a bundler.
|
|
87
|
+
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# EfficientDet Lite0: TFLite vs VolvoxAI Native
|
|
2
|
+
|
|
3
|
+
This document compares MediaPipe EfficientDet Lite0 int8, float16-source, and
|
|
4
|
+
float32-source packages on the Linux CPU path, and records the Android
|
|
5
|
+
OpenGL GPU result for the float32 package.
|
|
6
|
+
|
|
7
|
+
Machine/runtime:
|
|
8
|
+
|
|
9
|
+
- CPU: AMD Ryzen 5 5600U with Radeon Graphics, single-thread runs
|
|
10
|
+
- TFLite: `/tmp/benchmark_model`, XNNPACK enabled, `--num_threads=1`,
|
|
11
|
+
`--warmup_runs=2`, `--num_runs=20`
|
|
12
|
+
- VolvoxAI: `./native/volvoxai detect ... --num_threads 1 --warmup_runs 2
|
|
13
|
+
--num_runs 20`
|
|
14
|
+
- Test image for VolvoxAI: `/tmp/volvox_dog.jpg`
|
|
15
|
+
|
|
16
|
+
Android GPU runtime:
|
|
17
|
+
|
|
18
|
+
- Device: Samsung SM-A528N, Qualcomm `lahaina`, Adreno 642L, Android SDK 34
|
|
19
|
+
- TFLite: `/data/local/tmp/benchmark_model`, GPU delegate, `--gpu_backend=gl`,
|
|
20
|
+
`--warmup_runs=2`, `--num_runs=20`
|
|
21
|
+
- VolvoxAI: Android arm64 build, `--opengl`, OpenGL ES 3.2,
|
|
22
|
+
`--warmup_runs 2`, `--num_runs 20`
|
|
23
|
+
- Test image for VolvoxAI: `/data/local/tmp/volvoxai_gltest/volvoxai_object_test.jpg`
|
|
24
|
+
|
|
25
|
+
Important caveats:
|
|
26
|
+
|
|
27
|
+
- TFLite `benchmark_model` measures generated tensor input. VolvoxAI `detect forward`
|
|
28
|
+
is the graph forward after the image input is loaded.
|
|
29
|
+
- VolvoxAI prints raw detection tensors. This is a speed comparison, not a validation of
|
|
30
|
+
MediaPipe postprocessing/NMS correctness.
|
|
31
|
+
- TFLite GPU defaults to allowing lower precision. The Android section includes both
|
|
32
|
+
the default GPU delegate result and a `--gpu_precision_loss_allowed=false` rerun.
|
|
33
|
+
- The TFLite float16 model uses float32 input/output and XNNPACK reports F32 kernels on
|
|
34
|
+
CPU. It is not an FP16 CPU-kernel speed test.
|
|
35
|
+
- Direct TFLite export keeps VolvoxAI tensors in NHWC and stores Conv weights in
|
|
36
|
+
TFLite-native layouts: regular Conv2D as `OHWI`, depthwise Conv2D as `1HWO`. The
|
|
37
|
+
native CPU path prepares a per-node HWIO/HWCM compute cache at engine init, so the
|
|
38
|
+
artifact stays TFLite-shaped without making the hot Conv loops stride through OHWI.
|
|
39
|
+
- The float16-source package stores floating weights as safetensors `F16`. Native CPU
|
|
40
|
+
keeps the artifact half-sized and prepares a widened Conv weight cache at engine init
|
|
41
|
+
for the default `cpu-f16w-pack` path. On this AVX2 CPU this is not native FP16
|
|
42
|
+
arithmetic; it is half-size storage plus one-time FP32 widening.
|
|
43
|
+
|
|
44
|
+
## Artifacts
|
|
45
|
+
|
|
46
|
+
| Package | Source TFLite | Optional ONNX | Volvox weights | Volvox config | Volvox graph contract |
|
|
47
|
+
| --- | ---: | ---: | ---: | ---: | --- |
|
|
48
|
+
| `models/efficientdet_lite0_int8` | `4.4M` | `4.0M` | `3.4M` | `148K` | direct TFLite NHWC, quantized `QConv2D`, `I8` OHWI/1HWO weights |
|
|
49
|
+
| `models/efficientdet_lite0_fp16` | `7.0M` | `13M` | `6.4M` | `108K` | direct TFLite NHWC, `F16` OHWI/1HWO weights |
|
|
50
|
+
| `models/efficientdet_lite0_fp32` | `14M` | `13M` | `13M` | `108K` | direct TFLite NHWC, FP32 OHWI/1HWO weights |
|
|
51
|
+
|
|
52
|
+
The fp16-source package preserves half-precision storage in safetensors:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
models/efficientdet_lite0_fp16/model.safetensors F16 tensors 253
|
|
56
|
+
Conv2D backend=cpu-f16w-pack
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The int8 package preserves quantized Conv nodes and quantization metadata:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
models/efficientdet_lite0_int8/config.json QConv2D 182
|
|
63
|
+
models/efficientdet_lite0_fp16/config.json Conv2D 182
|
|
64
|
+
models/efficientdet_lite0_fp32/config.json Conv2D 182
|
|
65
|
+
models/efficientdet_lite0_fp16/config.json Transpose 0
|
|
66
|
+
models/efficientdet_lite0_fp32/config.json Transpose 0
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The direct TFLite configs report:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
internal_layout=NHWC
|
|
73
|
+
conv_weight_layout=OHWI
|
|
74
|
+
depthwise_weight_layout=1HWO
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The first Conv/QConv weights match TFLite shapes:
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
w0 regular Conv2D (32, 3, 3, 3) # OHWI
|
|
81
|
+
w2 depthwise Conv2D (1, 3, 3, 32) # 1HWO
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The int8 VolvoxAI debug log confirms the quantized path is active:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
QConv2D backend=cpu-qconv
|
|
88
|
+
QConv2D backend=cpu-qconv-f32
|
|
89
|
+
QConv2D backend=cpu-qconv-f32
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Most Conv nodes keep quantized NHWC activations and int8 weights with int32
|
|
93
|
+
accumulation/requantization. Some output-head nodes produce FP32 output
|
|
94
|
+
where the graph leaves the quantized island.
|
|
95
|
+
|
|
96
|
+
## Export Commands
|
|
97
|
+
|
|
98
|
+
Download the float models:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
mkdir -p models/efficientdet_lite0_fp16 models/efficientdet_lite0_fp32
|
|
102
|
+
|
|
103
|
+
curl -L -o models/efficientdet_lite0_fp16/efficientdet_lite0_float16.tflite \
|
|
104
|
+
https://storage.googleapis.com/mediapipe-models/object_detector/efficientdet_lite0/float16/latest/efficientdet_lite0.tflite
|
|
105
|
+
|
|
106
|
+
curl -L -o models/efficientdet_lite0_fp32/efficientdet_lite0_float32.tflite \
|
|
107
|
+
https://storage.googleapis.com/mediapipe-models/object_detector/efficientdet_lite0/float32/latest/efficientdet_lite0.tflite
|
|
108
|
+
|
|
109
|
+
cp models/efficientdet_lite0_int8/labels.txt models/efficientdet_lite0_fp16/labels.txt
|
|
110
|
+
cp models/efficientdet_lite0_int8/labels.txt models/efficientdet_lite0_fp32/labels.txt
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Export TFLite directly to VolvoxAI:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
python3 tools/export_safetensors.py \
|
|
117
|
+
--model models/efficientdet_lite0_fp16/efficientdet_lite0_float16.tflite \
|
|
118
|
+
--out models/efficientdet_lite0_fp16/model.safetensors
|
|
119
|
+
|
|
120
|
+
python3 tools/export_safetensors.py \
|
|
121
|
+
--model models/efficientdet_lite0_fp32/efficientdet_lite0_float32.tflite \
|
|
122
|
+
--out models/efficientdet_lite0_fp32/model.safetensors
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The exporter auto-selects `F16` storage for the float16 TFLite model because its constant
|
|
126
|
+
weights are stored as half precision. Use `--weight-dtype float32` to force FP32 artifact
|
|
127
|
+
storage, or `--weight-dtype float16` to force half-precision storage.
|
|
128
|
+
|
|
129
|
+
The direct float exports reported:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
float16: TFLite ops=516 folded_dequantize=253 lowered_nodes=263 optimized_nodes=262 weights=253
|
|
133
|
+
float32: TFLite ops=263 folded_dequantize=0 lowered_nodes=263 optimized_nodes=262 weights=253
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Benchmark Commands
|
|
137
|
+
|
|
138
|
+
TFLite:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
/tmp/benchmark_model \
|
|
142
|
+
--graph=models/efficientdet_lite0_int8/efficientdet_lite0.tflite \
|
|
143
|
+
--num_threads=1 \
|
|
144
|
+
--use_xnnpack=true \
|
|
145
|
+
--warmup_runs=2 \
|
|
146
|
+
--num_runs=20 \
|
|
147
|
+
--min_secs=0 \
|
|
148
|
+
--max_secs=30
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Add `--enable_op_profiling=true --op_profiling_output_mode=stdout` to reproduce
|
|
152
|
+
the TFLite op-sum tables below.
|
|
153
|
+
|
|
154
|
+
VolvoxAI:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
clang -O3 -mavx2 -mfma -pthread -Inative \
|
|
158
|
+
native/cJSON.c native/safetensors.c native/kernels.c native/quant_cpu_opt.c \
|
|
159
|
+
native/conv_f32_opt.c native/tensor_f32_opt.c native/engine_runtime.c \
|
|
160
|
+
native/engine.c native/image_io.c native/kie_runtime.c native/vulkan_engine.c \
|
|
161
|
+
native/opengl_engine.c native/tokenizer.c native/nnapi_engine.c native/main.c \
|
|
162
|
+
-o native/volvoxai -lm -ldl
|
|
163
|
+
|
|
164
|
+
./native/volvoxai detect models/efficientdet_lite0_int8 \
|
|
165
|
+
--image input0=/tmp/volvox_dog.jpg \
|
|
166
|
+
--image-normalize raw-255 \
|
|
167
|
+
--boxes boxes \
|
|
168
|
+
--scores scores \
|
|
169
|
+
--max-det 5 \
|
|
170
|
+
--num_threads 1 \
|
|
171
|
+
--warmup_runs 2 \
|
|
172
|
+
--num_runs 20
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Add `--debug` to reproduce the VolvoxAI op-sum and load/init tables below.
|
|
176
|
+
|
|
177
|
+
For the float-source packages, replace the model directory with
|
|
178
|
+
`models/efficientdet_lite0_fp16` or `models/efficientdet_lite0_fp32`.
|
|
179
|
+
|
|
180
|
+
Android TFLite OpenGL GPU:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
adb shell 'cd /data/local/tmp && ./benchmark_model \
|
|
184
|
+
--graph=/data/local/tmp/tflite_gltest/efficientdet_lite0_float32.tflite \
|
|
185
|
+
--use_gpu=true \
|
|
186
|
+
--gpu_backend=gl \
|
|
187
|
+
--warmup_runs=2 \
|
|
188
|
+
--warmup_min_secs=0 \
|
|
189
|
+
--num_runs=20 \
|
|
190
|
+
--min_secs=0 \
|
|
191
|
+
--max_secs=60 \
|
|
192
|
+
--num_threads=1'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Add `--gpu_precision_loss_allowed=false` for the strict fp32 GPU rerun.
|
|
196
|
+
|
|
197
|
+
Android VolvoxAI OpenGL ES:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
adb shell 'cd /data/local/tmp/volvoxai_gltest && ./volvoxai detect \
|
|
201
|
+
models/efficientdet_lite0_fp32 \
|
|
202
|
+
--image input0=volvoxai_object_test.jpg \
|
|
203
|
+
--image-normalize raw-255 \
|
|
204
|
+
--boxes boxes \
|
|
205
|
+
--scores scores \
|
|
206
|
+
--max-det 3 \
|
|
207
|
+
--opengl \
|
|
208
|
+
--warmup_runs 2 \
|
|
209
|
+
--num_runs 20'
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## High-Level Result
|
|
213
|
+
|
|
214
|
+
Headline timings are timed first/avg/min/max after two warmup runs. They omit
|
|
215
|
+
TFLite op profiling and VolvoxAI `--debug` logging.
|
|
216
|
+
|
|
217
|
+
| Source model | TFLite first | TFLite avg | TFLite min | TFLite max | VolvoxAI first | VolvoxAI avg | VolvoxAI min | VolvoxAI max | Gap by avg |
|
|
218
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
219
|
+
| int8 | `26.758 ms` | `25.943 ms` | `25.234 ms` | `28.078 ms` | `37.589 ms` | `38.299 ms` | `37.589 ms` | `39.431 ms` | `1.48x` |
|
|
220
|
+
| float16 | `25.816 ms` | `25.857 ms` | `25.115 ms` | `27.432 ms` | `29.486 ms` | `30.895 ms` | `29.486 ms` | `32.473 ms` | `1.19x` |
|
|
221
|
+
| float32 | `25.355 ms` | `26.024 ms` | `25.004 ms` | `26.950 ms` | `29.361 ms` | `30.805 ms` | `29.361 ms` | `33.251 ms` | `1.18x` |
|
|
222
|
+
|
|
223
|
+
TFLite op profiling and VolvoxAI `--debug` summaries from matching 20-run jobs.
|
|
224
|
+
For VolvoxAI, the op row averages the last 20 debug summary blocks and excludes
|
|
225
|
+
the two warmup forwards.
|
|
226
|
+
|
|
227
|
+
| Source model | TFLite profiled op avg | TFLite nodes | VolvoxAI debug op avg | VolvoxAI nodes |
|
|
228
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
229
|
+
| int8 | `25.579 ms` | `196` | `37.078 ms` | `265` |
|
|
230
|
+
| float16 | `26.326 ms` | `193` | `30.770 ms` | `262` |
|
|
231
|
+
| float32 | `26.255 ms` | `193` | `30.987 ms` | `262` |
|
|
232
|
+
|
|
233
|
+
Read:
|
|
234
|
+
|
|
235
|
+
- TFLite/XNNPACK is faster on one CPU thread. The int8 gap is `1.48x`.
|
|
236
|
+
- VolvoxAI int8 uses quantized `QConv2D` execution, but it is slower than the
|
|
237
|
+
float-source native path in this run.
|
|
238
|
+
- TFLite float16 and float32 are almost the same on CPU because XNNPACK reports F32 kernels
|
|
239
|
+
for both.
|
|
240
|
+
- VolvoxAI float16-source stores `F16` weights and defaults to a prepared
|
|
241
|
+
`cpu-f16w-pack` path. Direct half-weight consumption is not used for this
|
|
242
|
+
comparison because this AVX2 CPU lacks native FP16 arithmetic.
|
|
243
|
+
- The direct TFLite paths have no ONNX-derived Transpose nodes. The float
|
|
244
|
+
graph has 262 nodes, and the int8 graph is direct NHWC TFLite with 265 nodes.
|
|
245
|
+
- No-debug single-thread runs are `38.299 ms` for int8 and about `31 ms` for the
|
|
246
|
+
float-source packages.
|
|
247
|
+
|
|
248
|
+
## TFLite Op Sums
|
|
249
|
+
|
|
250
|
+
These come from TFLite operator-profiling runs with the same thread/warmup/run counts.
|
|
251
|
+
The total row is the profiler's 20-run average, not the sum of rounded table rows.
|
|
252
|
+
|
|
253
|
+
| Op group | int8 | float16-source | float32-source |
|
|
254
|
+
| --- | ---: | ---: | ---: |
|
|
255
|
+
| Pointwise 1x1 / GEMM, 101 calls | `17.912 ms` | `18.009 ms` | `18.052 ms` |
|
|
256
|
+
| Depthwise Conv, 80 calls | `4.418 ms` | `5.197 ms` | `5.124 ms` |
|
|
257
|
+
| Stem Conv, 1 call | `1.758 ms` | `1.019 ms` | `0.951 ms` |
|
|
258
|
+
| Unary / activation | `0.871 ms` | `1.019 ms` | `1.043 ms` |
|
|
259
|
+
| Copy / tensor movement | `0.153 ms` | `0.711 ms` | `0.708 ms` |
|
|
260
|
+
| Binary Add | `0.103 ms` | `0.150 ms` | `0.159 ms` |
|
|
261
|
+
| ResizeNearest | `0.072 ms` | `0.085 ms` | `0.086 ms` |
|
|
262
|
+
| MaxPool | `0.017 ms` | `0.059 ms` | `0.056 ms` |
|
|
263
|
+
| Quantize | `0.208 ms` | n/a | n/a |
|
|
264
|
+
| Total profiled op avg | `25.579 ms` | `26.326 ms` | `26.255 ms` |
|
|
265
|
+
|
|
266
|
+
TFLite int8 uses XNNPACK quantized kernels:
|
|
267
|
+
|
|
268
|
+
```text
|
|
269
|
+
Fully Connected (NC, QS8, QC8W) GEMM
|
|
270
|
+
Convolution (NHWC, QC8) DWConv
|
|
271
|
+
Convolution (NHWC, QC8) IGEMM
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The float16-source and float32-source TFLite runs both report:
|
|
275
|
+
|
|
276
|
+
```text
|
|
277
|
+
Fully Connected (NC, F32) GEMM
|
|
278
|
+
Convolution (NHWC, F32) DWConv
|
|
279
|
+
Convolution (NHWC, F32) IGEMM
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## VolvoxAI Op Sums
|
|
283
|
+
|
|
284
|
+
These average the 20 timed `--debug` forwards; the two warmup forwards are excluded.
|
|
285
|
+
|
|
286
|
+
| Op group | int8 package | float16-source package | float32-source package |
|
|
287
|
+
| --- | ---: | ---: | ---: |
|
|
288
|
+
| Conv aggregate | `QConv2D 34.80 ms` | `Conv2D cpu-f16w-pack 28.85 ms` | `Conv2D cpu-pack 28.93 ms` |
|
|
289
|
+
| Concat | `1.25 ms` | `1.37 ms` | `1.49 ms` |
|
|
290
|
+
| Add | `0.42 ms` | `0.33 ms` | `0.34 ms` |
|
|
291
|
+
| MaxPool2D | `0.40 ms` | `0.14 ms` | `0.14 ms` |
|
|
292
|
+
| ResizeNearest2D | `0.04 ms` | `0.08 ms` | `0.08 ms` |
|
|
293
|
+
| QuantizeLinear | `0.18 ms` | n/a | n/a |
|
|
294
|
+
| DequantizeLinear | aliased after Concat | n/a | n/a |
|
|
295
|
+
| Total profiled op avg | `37.08 ms` | `30.77 ms` | `30.99 ms` |
|
|
296
|
+
|
|
297
|
+
VolvoxAI load/init timings from representative debug runs:
|
|
298
|
+
|
|
299
|
+
| Package | load weights | build graph | engine init |
|
|
300
|
+
| --- | ---: | ---: | ---: |
|
|
301
|
+
| int8 | `3.523 ms` | `8.906 ms` | `14.800 ms` |
|
|
302
|
+
| float16-source | `7.216 ms` | `7.503 ms` | `36.135 ms` |
|
|
303
|
+
| float32-source | `12.595 ms` | `6.758 ms` | `40.580 ms` |
|
|
304
|
+
|
|
305
|
+
The float-source engine init includes layout/precision preparation:
|
|
306
|
+
|
|
307
|
+
```text
|
|
308
|
+
fp16: prepack_conv weights=182 biases=182 pointwise_packs=101 igemm_indirs=1 19.720 ms
|
|
309
|
+
fp32: prepack_conv weights=182 biases=0 pointwise_packs=101 igemm_indirs=1 19.535 ms
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## CPU Gap
|
|
313
|
+
|
|
314
|
+
The int8 VolvoxAI path is a real quantized graph island for most Conv nodes. It uses a
|
|
315
|
+
wide AVX2 pointwise tile plus an exact AVX2 byte-dot path for `input_zero_point = -128`
|
|
316
|
+
pointwise layers, vectorized same-quant MaxPool2D, vectorized contiguous QuantizeLinear,
|
|
317
|
+
and aliasing for final no-op DequantizeLinear nodes after Concat. It is not an
|
|
318
|
+
XNNPACK-class CPU implementation.
|
|
319
|
+
|
|
320
|
+
One-thread blockers:
|
|
321
|
+
|
|
322
|
+
1. Pointwise and depthwise microkernels are much simpler than XNNPACK. They need
|
|
323
|
+
architecture-specific dot-product paths, better unrolling, and better cache blocking.
|
|
324
|
+
2. Depthwise Conv needs XNNPACK-style indirection/bounds handling to remove per-MAC
|
|
325
|
+
padding math.
|
|
326
|
+
3. Weight packing exists, but it is not microkernel-specific enough for VNNI/AVX512 or
|
|
327
|
+
ARM `sdot`/`udot`.
|
|
328
|
+
4. Some graph tails leave the quantized island and produce FP32 output tensors.
|
|
329
|
+
5. The fp16-source export preserves `F16` weight storage and prepares those weights once
|
|
330
|
+
for CPU Conv2D. This follows the guide's weight-preparation principle on AVX2, but
|
|
331
|
+
true hardware FP16 arithmetic requires ARM FP16, AVX512-FP16, or a GPU path.
|
|
332
|
+
|
|
333
|
+
## Native GPU Status
|
|
334
|
+
|
|
335
|
+
OpenGL waits at the end of `engine_forward`, so reported forward time includes real
|
|
336
|
+
queued GPU work rather than only CPU-side dispatch/enqueue time.
|
|
337
|
+
|
|
338
|
+
Linux fp32 EfficientDet Lite0 timings on the Ryzen 5 5600U machine:
|
|
339
|
+
|
|
340
|
+
| Engine | Backend | Avg, warmup 2 / runs 20 | Notes |
|
|
341
|
+
| --- | --- | ---: | --- |
|
|
342
|
+
| VolvoxAI | CPU fp32 | `29.990 ms` | `--num_threads 1` |
|
|
343
|
+
| VolvoxAI | Vulkan graph | `45.523 ms` | AMD RADV Renoir, vector/tiled Conv2D shaders |
|
|
344
|
+
| VolvoxAI | OpenGL graph | `40.256 ms` | AMD Renoir Mesa, vector/tiled Conv2D shaders |
|
|
345
|
+
|
|
346
|
+
Representative corrected debug summaries show that most elapsed time is in the final GPU
|
|
347
|
+
wait, not in CPU enqueue:
|
|
348
|
+
|
|
349
|
+
```text
|
|
350
|
+
OpenGL: engine_forward 46.823 ms, GPUWait 40.46 ms, Conv2D enqueue 3.42 ms
|
|
351
|
+
Vulkan: engine_forward 43.100 ms, GPUWait 39.41 ms, Conv2D enqueue 1.67 ms
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Android OpenGL GPU Result
|
|
355
|
+
|
|
356
|
+
Run on the attached SM-A528N / Adreno 642L device with the float32 EfficientDet Lite0
|
|
357
|
+
package.
|
|
358
|
+
|
|
359
|
+
| Engine | Backend | First | Avg | Min | Max | Gap vs TFLite default |
|
|
360
|
+
| --- | --- | ---: | ---: | ---: | ---: | ---: |
|
|
361
|
+
| TFLite | GPU delegate, OpenGL, default precision | `138.365 ms` | `127.211 ms` | `90.464 ms` | `139.163 ms` | `1.00x` |
|
|
362
|
+
| TFLite | GPU delegate, OpenGL, fp32 strict | `120.499 ms` | `130.962 ms` | `120.499 ms` | `139.626 ms` | `1.03x` |
|
|
363
|
+
| VolvoxAI | OpenGL ES 3.2, vector/tiled Conv2D | `118.918 ms` | `144.332 ms` | `118.918 ms` | `153.026 ms` | `1.13x` |
|
|
364
|
+
|
|
365
|
+
The OpenGL run uses Naga-generated GLES shaders for depthwise, pointwise, and the
|
|
366
|
+
EfficientDet stem Conv2D, and is `1.13x` slower than the TFLite OpenGL default-precision
|
|
367
|
+
baseline.
|
|
368
|
+
|
|
369
|
+
TFLite log confirmation:
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
Replacing 263 out of 263 node(s) with delegate (TfLiteGpuDelegateV2)
|
|
373
|
+
yielding 1 partitions for subgraph 0
|
|
374
|
+
Initialized OpenGL-based API
|
|
375
|
+
Created 1 GPU delegate kernels
|
|
376
|
+
Inference (avg): 127211 us
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
VolvoxAI log confirmation:
|
|
380
|
+
|
|
381
|
+
```text
|
|
382
|
+
[VolvoxAI GPU] OpenGL Compute initialized:
|
|
383
|
+
Qualcomm / Adreno (TM) 642L / OpenGL ES 3.2
|
|
384
|
+
[bench] detect: first=118.918 avg=144.332 min=118.918 max=153.026 ms
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
The `--debug` run is not the benchmark average, but it identifies where the time goes:
|
|
388
|
+
|
|
389
|
+
```text
|
|
390
|
+
[debug] engine_forward nodes=262 159.649 ms
|
|
391
|
+
[debug] --- op time summary (by total) ---
|
|
392
|
+
[debug] GPUWait 114.84 ms (n=1)
|
|
393
|
+
[debug] Conv2D 11.90 ms (n=182)
|
|
394
|
+
[debug] Add 2.51 ms (n=42)
|
|
395
|
+
[debug] MaxPool2D 0.76 ms (n=14)
|
|
396
|
+
[debug] ResizeNearest2D 0.62 ms (n=12)
|
|
397
|
+
[debug] Concat 0.18 ms (n=2)
|
|
398
|
+
[debug] Reshape 0.13 ms (n=10)
|
|
399
|
+
[debug] TOTAL 130.94 ms
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
## Android OpenGL Gap Analysis
|
|
403
|
+
|
|
404
|
+
The OpenGL path selects vectorized and tiled Naga-generated GLES shaders for
|
|
405
|
+
EfficientDet's dominant Conv shapes:
|
|
406
|
+
|
|
407
|
+
```text
|
|
408
|
+
stem Conv2D: conv2DRegularC3Out16
|
|
409
|
+
depthwise Conv2D: conv2DDepthwise8
|
|
410
|
+
pointwise Conv2D: conv2DPointwise16Tile
|
|
411
|
+
head pointwise: conv2DPointwise8Vec4 / conv2DPointwise8Vec2
|
|
412
|
+
fallback Conv2D: conv2D / conv2DPointwise8
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
The `1.13x` gap is small but not a measurement artifact. The VolvoxAI OpenGL path is a
|
|
416
|
+
per-node compute backend, while TFLite's GPU delegate compiles the whole graph into one
|
|
417
|
+
delegated partition.
|
|
418
|
+
|
|
419
|
+
Reasons for the gap:
|
|
420
|
+
|
|
421
|
+
1. TFLite delegates the whole EfficientDet graph into one GPU partition and creates one
|
|
422
|
+
GPU delegate kernel. VolvoxAI executes the exported graph as 262 native nodes.
|
|
423
|
+
2. VolvoxAI dispatches each graph op separately. Each dispatch binds buffers, launches
|
|
424
|
+
compute, and inserts a shader-storage memory barrier. That is correct but expensive for
|
|
425
|
+
hundreds of small mobile-GPU kernels.
|
|
426
|
+
3. Each op creates a small uniform parameter buffer for that dispatch. This is minor
|
|
427
|
+
compared with `GPUWait`, but it is unnecessary per-node overhead.
|
|
428
|
+
4. The OpenGL backend has only kernel-level specialization. It does not fuse larger
|
|
429
|
+
EfficientDet blocks across Add/Resize/Conv boundaries the way a graph delegate can.
|
|
430
|
+
5. The Android OpenGL path runs fp32 GLES compute shaders. TFLite default precision can use
|
|
431
|
+
lower precision, but the strict fp32 TFLite rerun is `130.962 ms`, so precision
|
|
432
|
+
alone is not the reason.
|
|
433
|
+
|
|
434
|
+
The debug profile's `Conv2D 11.90 ms` row is CPU-side enqueue/profile time, not real GPU
|
|
435
|
+
compute time. The real queued GPU execution appears in `GPUWait 114.84 ms`; enqueue-only
|
|
436
|
+
OpenGL timing omits it.
|
|
437
|
+
|
|
438
|
+
Work to consistently match or beat the TFLite GPU delegate:
|
|
439
|
+
|
|
440
|
+
1. Fuse EfficientDet blocks at graph compile time instead of dispatching every op as a
|
|
441
|
+
separate shader.
|
|
442
|
+
2. Remove per-dispatch memory barriers when a graph-level dependency schedule can prove
|
|
443
|
+
ordering.
|
|
444
|
+
3. Reuse persistent parameter buffers or push constants/equivalent small-uniform storage.
|
|
445
|
+
4. Add a fp16/mobile GPU path and benchmark it separately from fp32.
|