RFD 1148 details: what the wheel ships, and what refuses to build

The accelerator is a binary, and the build settles it

litert/runtime/accelerators/gpu/BUILD declares macos_dylib(name = "build_ml_drift_webgpu_accelerator_dylib", …) with minimum_os_version = "14.0", no target_compatible_with and no nobuilder tag, unlike the Windows target beside it which carries both. Read alone, that says macOS support withheld from the release.

Reading a BUILD file shows a target is declared. Whether its dependencies can be fetched is a separate question, and bazel 7.7.1 against a fresh clone, --nobuild, --config=macos, answers it:

target                              failure
ml_drift_webgpu_accelerator_dylib   @ml_drift: "At least one of url and urls
                                    must be provided"
ml_drift_metal_accelerator_dylib    no such package 'tools/build_defs/swift'

WORKSPACE:390 declares http_archive(name = "ml_drift", strip_prefix = "ml-drift-main", …) carrying no url field, and no workspace.bzl supplies one. google-ai-edge/ml-drift, google/ml-drift and google-ai-edge/ml_drift all return 404. ml_drift_delegate/ sits in the tree, and that is the delegate glue rather than the compute core it binds to.

The Metal target fails earlier and for its own reason, on a Google-internal //tools/build_defs/swift referenced from ml_drift_delegate/delegate/BUILD:892.

What the three wheels ship

Read out of ai-edge-litert 2.2.0, listing the native libraries in each and the backend strings inside them:

platform        accelerator                            reaches the GPU via
macOS arm64     libLiteRtMetalAccelerator.dylib 11 MB  Metal
Windows amd64   libLiteRtWebGpuAccelerator.dll  23 MB  Tint -> HLSL -> D3D12
Linux x86_64    libLiteRtWebGpuAccelerator.so   18 MB  Tint -> SPIR-V -> Vulkan

“WebGPU” here means Dawn rather than a browser: 1087 dawn and 2026 tint strings in the Windows DLL, 718 and 1262 in the Linux .so, beside 134 D3D12 and 137 hlsl on Windows, 250 Vulkan and 164 Spirv on Linux.

Grepping the Linux accelerator for cuda|cudnn|nvrtc returns 0. The source tree nevertheless carries litert/vendors/nvidia/, a TensorRT compiler plugin with .cu kernels, so the shipped wheel and the tree disagree about NVIDIA and only the wheel was measured.

The macOS wheel contains no file matching webgpu, dawn or tint, and libLiteRt.dylib holds 16 WebGpu strings against 0 Dawn — the core knows the enum while the plugin stays absent.

The vendor ABI, for the Hailo work

header                                      entry points   lines
litert/vendors/c/litert_compiler_plugin.h   18             156
litert/vendors/c/litert_dispatch.h          49             390

reference                                          lines
litert/vendors/examples/example_plugin.cc          416
litert/vendors/examples/example_dispatch.cc        609
litert/vendors/nvidia/compiler/compiler_plugin.cc  892

A vendor plugin is written once per device rather than once per model, which is the axis ggml failed on: trellis2.cpp is 3420 lines of hand-written graph, 79 distinct ggml_* ops across 7 ggml_build_forward_expand sites, for one model.

StableHLO sits upstream of the flatbuffer

litert_torch/backend/lowerings/_basic.py imports stablehlo from litert_converter.mlir.dialects and emits stablehlo.add and stablehlo.multiply; _convolution.py is headed “lowering for coreaten to stablehlo”; backend/export.py returns an MlirLowered carrying .module. litert-torch 0.9.4 requires jax outright and ships backend/jax_bridge, and backend/tf_integration.py:116 rewrites arith.constant to stablehlo.constant “to run the module with TFXLA”.

So the Cloud TPU route is a tap one stage above the flatbuffer rather than a conversion. It stays deferred: no TPU is attached, and RunPod rents NVIDIA.

jax-metal does not serve as a Metal alternative — 0.1.1, released 2024-10-08, pinning jaxlib>=0.4.34 against a current 0.11.1, and it remains the only PJRT Metal plugin in existence.

What Metal reaches, measured

scripts/litert_bench.py, ConvStack(width=64, depth=4, size=64), 460.1 M MACs, on an Apple M2 Pro. The oracle is torch on CPU, which replaces the ONNX diff that retired with the format. Rate counts a fused multiply-add as two operations, gpu_tops.py’s convention, so these compose with RFD 1142’s rows.

backend      accel   best ms  spread   TFLOP/s   max|diff|
cpu          True      12.24      0%      0.08   2.384e-07
metal fp16   True       0.59      3%      1.56   1.687e-03
metal f32    True       0.39     90%      2.33   2.682e-07
coreml       -             -       -      6.97           -    RFD 1142, fp16

Metal takes the whole graphis_fully_accelerated() answers true on every row, so none of this is a partitioned graph with a fast fragment. It reaches 0.33x the figure Core ML’s Metal path gave on the same part, and roughly thirty times CPU.

The accelerator computes in fp16 unless told otherwise, and the evidence is numeric rather than a timing. The default carries max|diff| 1.687e-03 against torch; the same model under GpuOptions(enforce_f32=True) carries 2.682e-07. Four orders of magnitude is not a rounding difference, and it means the earlier reading of the default row as f32 was wrong: the comparison against Core ML’s fp16 figure is like for like.

This harness declines to rank fp16 against f32, and the reason is an artefact worth recording. With a fixed measurement order, whichever GPU configuration ran first won:

order        fp16 best   f32 best   winner
fp16 first      0.38        0.40     fp16
f32 first       0.48        0.45     f32

The ranking followed the order rather than the arithmetic, so the gap between the two is smaller than the artefact. A first version of this harness took one median of fifty repetitions and reported 0.21x; the median moved 74% between rounds on identical input. The harness now alternates the order every round, takes the fastest round rather than the average because contention only subtracts, and prints the spread beside the rate.

Hailo takes TFLite by its own direction

The Dataflow Compiler 5.3.0 guide names its inputs as “a Tensorflow checkpoint, a Tensorflow frozen graph file, a TFLite file, or an ONNX file”, mentions TFLite 57 times, and deprecates parsing TensorFlow 1.x and 2.x .ckpt/.pb “using all parsing APIs” with guidelines for moving to TensorFlow Lite. Choosing LiteRT lands on the input the vendor consolidates on.

Precision is integer: fp16 occurs 0 times in the 83-page datasheet and 0 times in the 187-page compiler guide. The modes are a8_w4, a16_w8 and a16_w16, set per layer through quantization_param(layer, precision_mode=…).