Skip to content

Speed and quality

Import and cold-start cost are where fastumap differs; per-call latency is the trade-off. Every table on this page is regenerated by make bench-report (see Benchmark reports).

fastumap stays within 0.01 neighbour overlap of umap-learn. Measured on MNIST (784-dimensional, seed 42). These numbers are generated rather than transcribed: make bench-report regenerates them into a dated report under docs/bench/, which records the machine, the load average and every package version they were measured on.

n method wall time overlap@15 global corr
5000 fastumap 22 s 0.341 0.320
5000 umap-learn 79 s 0.344 0.329
5000 pca 87 s 0.058 0.523
10000 fastumap 46 s 0.271 0.361
10000 umap-learn 50 s 0.268 0.387
10000 pca 120 s 0.038 0.487
20000 fastumap 57 s 0.207 0.358
20000 umap-learn 69 s 0.204 0.368
20000 pca 237 s 0.024 0.504

The quality columns are the comparable ones. overlap@15 and global corr are deterministic; the wall times came from a loaded machine (recorded in the report) and are inflated for every method. PCA is included as a control: it preserves global structure best and local structure worst, which is the trade-off UMAP addresses.

umap-learn's times reuse the numba compilation from its first fit in the same process. A fresh process pays roughly 20 s of compilation on every invocation, which fastumap does not pay. Above about 50,000 points fastumap's exact O(n²) neighbour search becomes the bottleneck; pip install fastumap[ann] addresses that, described below. Raising chunk_count (default 1) recovers most of the remaining local-overlap gap at proportional cost, and remains deterministic.

overlap@15 is the fraction of each point's 15 input-space neighbours retained after projection, read against a random baseline. global corr is the Spearman correlation of all pairwise distances, before against after.

Import and cold-start cost are where fastumap differs. Per-call latency is close to umap-learn rather than better than it.

  • The native kernel ships by default, so per-call speed on large batches is close to umap-learn. It runs umap-learn's in-place SGD, in Rust.
  • The numpy fallback, which only an unbuilt source checkout uses, is roughly 2× slower at 1024 dimensions and n=5000 (about 40 s against 20 s). A vectorised numpy SGD cannot match numba's compiled loop.

fastumap is the appropriate choice when import and cold-start cost dominate. On per-call latency for large, high-dimensional batches it is roughly even with umap-learn on the kernel path.

BLAS thread cap under a CPU quota

Some containers cap CPU (docker --cpus, Kubernetes and EKS limits, EC2 cgroups) while still reporting the host's full core count. BLAS then starts more threads than the quota supports and thrashes under load.

fastumap caps the BLAS pool to the quota automatically, with no configuration. Measured under docker --cpus=0.5 on a 16-core host with 4 workers, per-call time drops from about 70 s to about 43 s (roughly 1.6×). Where no quota is visible, nothing is capped.

This is a no-op on AWS Fargate, which meters CPU outside the cgroup, leaving the quota invisible. Fargate also reports a low core count, so BLAS does not oversubscribe there in the first place.

Next