Status: current · test counts are the last recorded runs (end of WP8) · op table snapshot 2026-10-06
The vendor-free build (-DBATCHLAS_ENABLE_VENDOR_BLAS=OFF) configures, builds, links, loads and runs. Milestone M1 (full ctest suite passing vendor-free) is not reached. Every public dense op and spmm has a native SYCL kernel. The remaining gaps are the host (Backend::NETLIB) path, routing defects and shapes that native can_run refuses.
Performance evidence is in ../perf/. The vendor seam is in vendor-independence.md. Open bugs are in known-defects.md.
| vendor-present | vendor-free | |
|---|---|---|
| configure | default | -DBATCHLAS_ENABLE_VENDOR_BLAS=OFF -DBATCHLAS_ENABLE_CUDA=ON |
| result | BATCHLAS_HAS_CUBLAS / CUSOLVER / CUSPARSE = 1 | BATCHLAS_HAS_CUDA_BACKEND 1, every CUDA math library at 0 |
| ctest -LE slow | 56 / 57 | 35 / 57 |
The one vendor-present failure, lanczos_tests, predates the campaign. It never calls gemv, its only linked rows and no reached rows.
BATCHLAS_ENABLE_VENDOR_BLAS is a master switch over the per-library options in BATCHLAS_VENDOR_LIBRARIES (cmake/BatchLASOptions.cmake).
Do not use the ctest pass count. Level-3 and factorization suites also run against the host backend, which a vendor-free build cannot serve, so a suite can fail on host rows while every CUDA case passes. Vendor-free trsm_tests is 59 passing / 32 failing; all 32 failures are host rows.
The native families' can_run requires d.is_gpu for geqrf, orgqr, ormqr, getrf, getrs, getri, potrf, trsm, syev and gesvd (src/ops/<op>/<op>.cc; geqrf's in can_run.hh). Exceptions: gemv's direct arm and spmm's gather, which run on a native_cpu queue, and gemm, which accepts is_gpu || !has_vendor (src/ops/gemm/gemm.cc).
Every vendor-free failure is a NoRouteError naming an op, scalar type and the switch that would restore it (include/batchlas/no_route.hh). Count them with:
Diff the census and the failing suite names, not the pass count. Recorded after WP8: syev 87, geqrf 44, trsm 32, ormqr 24, trmm 16, herk 16, getri 16, syrk 12, her2k 12, hemm 12, syr2k 10, symm 8, spmm 0 (was 2).
For routes, read the reached rows of the coverage dump (BATCHLAS_COVERAGE_OUT). The static linked table is not evidence: it was stale in both directions (src/select/coverage.cc).
Route = the first runnable native entry of the op's table (tuned/<op>.<device>.txt, selected in src/ops/<op>/<op>.cc). "Transcribed" means the rows reproduce the deleted preferred() window (Tuned selection tables).
| op | vendor-free route | gaps |
|---|---|---|
| gemm | direct, tiled, small, reg, wide. Transcribed: GPU, homogeneous, batch >= 64; double at k >= 2; float NN square max_dim <= 48. | Complex is never native-first: no register kernel, so it falls to direct/tiled, 3.2–7.1× slower than cuBLAS. |
| gemv | cta, direct. Transcribed: complex<double>, transposed, 64 <= red <= 352, out >= 256, batch >= 320. | Vendor first almost everywhere: cuBLAS runs at 94–105% of the DRAM roof on 90 of 92 cells (gemv.md). |
| trsm | cta, sg_left, blocked. Transcribed: native from batch >= 8; float/Side::Right also need batch >= 128 \|\| order <= 32. | Census 32, all host rows. |
| potrf | tiny, cta, lpanel, blocked. Measured per cell. | Uplo::Upper refused by the blocked driver (src/ops/potrf/potrf.cc). Complex (0.311–0.509×) and n <= 256 are vendor-first (potrf.md). |
| posv | tiny, cta, blocked. No vendor family. | None recorded. |
| geqrf | tiny, cta, blocked. Transcribed: above a per-type order floor (float 64, cfloat 48, double 76, cdouble 256); tall panels n >= 32 && aspect >= 4 (8 for 64-bit types). | Census 44, host rows. Below the floor the vendor wins, to 0.02× at double n = 8. Tall clause rows >= 128 is not a bracketed edge; double 128x32 is 0.68× (small-n-baseline.md). |
| orgqr | blocked. Transcribed: rows <= 512 && cols <= 512. | Vendor wins above n = 512. Host rows refused. |
| ormqr | blocked, then vendor on every row. | Census 24, host rows. |
| getrf | tiny, cta, blocked. Transcribed: float order >= 256; cfloat order >= 512 (>= 256 at batch >= 256). | double and complex<double> earn nothing at any order (lu.md). |
| getrs | cta, blocked. Transcribed: cta at nrhs <= 2 (all types), nrhs <= 4 (float); blocked at batch >= 128 with float nrhs >= 64 or double nrhs >= 128. | Clauses A and B hand 84 winning cells to the vendor (largest 3.944×). |
| getri | blocked. Transcribed: float order >= 128, cfloat order >= 256. | Census 16, host rows. Unrouted win at batch <= 32, every type (see levers below). |
| gesv | tiny, blocked. No vendor family. | None recorded. |
| spmm | direct. Transcribed: CSR, transA == NoTrans, minus complex<float> with transB != NoTrans. | Census 0. Transposed scatter is vendor-first: 169 of 458 saturated cells lose, worst 3.011, and no clause recovers a window (spmm.md). |
| syev | cta, cta_fused, jacobi, blocked, two_stage. Transcribed from the old CUDA grid. | Census 87. Four call sites demand the vendor syev_vendor_or_throw and throw vendor-free: src/util/cond.cc:54, src/util/norm.cc:47, src/ops/syevx/kernels/syevx_lobpcg.cc:546 and :1076. Fix: call the public syev (known-defects #2). |
| gesvd | jacobi, cta, blocked. Transcribed: real 33..64 is blocked\|vendor\|jacobi (evidence). | Host rows refused. |
| symm | Hand-rolled if chain. expand (mirrored expansion + public gemm; needs expansion_fits). No tile kernel. | Census 8. At the snapshot, double had no expansion route; the flat-selection spec lists expand for both types. Reconcile before relying on either. |
| syrk | Hand-rolled. gram (n <= 128, float and double), triangular (float). | Census 12. Non-float tiles are reachable only from cublas.cc. |
| syr2k | triangular (float only). A real ConjTrans runs as Trans. | Census 10. No non-float tile route; syr2k_triangular_tiles has one call site, in the float-only dispatcher. |
| trmm | triangular (Side::Left), expand. | Census 16. The double/complex tile branch is reachable only from cublas.cc. |
| hemm, herk, her2k | None: vendor or NoRouteError. | Census 12, 16, 12. No native arm in the facade (src/ops/level3/level3.cc:23-70). |
Rows that stay vendor-first by measurement are the vendor's wins; kernel work will not change them.
Definition. -DBATCHLAS_ENABLE_VENDOR_BLAS=OFF configures, builds and passes the full ctest suite. No performance claim; the vendor stays first in every Auto order.
Blockers, by census:
Definition. For each (op, type, shape class) at saturated, large-batch shapes,
and within the op's accuracy tolerance, native moves ahead of the vendor in Auto. A failing cell stays vendor-first and is published as such.
Reached as measured windows: getrf, getrs, getri, gemv, spmm's gather. Not reached by measurement: potrf, spmm's scatter, complex gemm. Not reached for want of an end-to-end measurement: geqrf, orgqr.
Open question: must a CPU SYCL device be fast, or only correct? The standing answer is correct only, since MKL and OpenBLAS already serve the CPU market. Dropping the is_gpu clause from can_run is enough for a family to serve the host queue.
Traps: Backend::INTEL is hard-wired false and oneMKL cannot be tested on this box. A CUDA-off ctest shows about 30 failures that are artefacts of the CPU-only build.
| lever | measured | where |
|---|---|---|
| geqrf / orgqr default flip | 3.24× / 7.85× geomean | qr.md |
| getri at batch <= 32, every type | 1.7–28× over cuBLAS (per-item loop there) | lu.md |
| potrf float at n >= 1024 | 1.13–1.40× over cuSOLVER | potrf.md |
| getrs clauses A and B | 84 winning cells, largest 3.944× | lu.md |
| gemv out_len >= 768 && batch >= 128; batch floor 320 → ~288 | ~18 cells at 2.26–2.91×; six wins at 1.27–4.45× | gemv.md |
| getrs clause-C batch floor 128 → 32 | float 3.87–5.96×, double 3.56–4.31× at nrhs = 128 | lu.md |
| spmm gather narrowed to nrhs >= 16 | 183 cells, worst 0.968; refused, the axis is the column pattern | spmm.md |
| complex register-tiled GEMM | ~2.7× on vendor-free cdouble potrf; unblocks complex gemm, potrf, level-3 complex | gemm.md |
ortho.cc's transposed gemv view, the syev resolver bypasses above, and lanczos.cc's two-column gemm, whose second column is discarded (known-defects.md). Closed: the BATCHLAS_SYRK_ROUTE=native wrong answer, the symm expansion_fits gap and the trsm heterogeneous-batch gate.
| Alternative | Result | Verdict |
|---|---|---|
| syrk/herk for ortho's Gram matrix | 73–96× slower at ortho's shapes | Dead end; wins only at k >= 512, square-ish. |
| trmm for the WY block factor | Loses at every shape | Dead end; complex still takes GEMM. |
| Complex Gram tiles (herk) | Loses to GEMM plus Hermitian fold | Dead end; compute bound. |
| syr2k for sytrd_blocked trailing update, double | 7.7× slower where it matters | Route stays CUDA + float. |
| Cooperative TRSM solve | 0.39× at order 64 | Dead end; the traffic model missed the serial recurrence. |
| Transcribing level-3 gate thresholds into a table | Sends 129 <= n <= 383 to a route that writes both triangles | Dead end. |
| potrf fold-free trailing update | 11% cheaper, and wrong | Dead end. |
The superseded root-level plan documents are kept at the tag perf-evidence/vendor-independence.