christopherkarani/MetalANNS
GPU-native approximate nearest neighbor search for Apple Silicon. Pure Swift + Metal — CAGRA-style graph index with full mutability, filtered search, streaming ingest, and multiple persistence modes.
🚀 Performance that Dominates
MetalANNS is built for the Unified Memory Architecture of M-series and A-series chips. While traditional libraries like HNSW are inherently sequential, MetalANNS uses CAGRA (CUDA-Accelerated Graph-based Approximate) principles, adapted for Metal, to perform massively parallel searches.
Index Build Speed (100k Vectors, 128D)
<p align="center"> <img src="docs/assets/chart-build-time.svg" alt="Build Time Comparison" width="600"> <br> <i>Benchmark: M3 Max (30-core GPU). MetalANNS constructs the graph in parallel using compute shaders.</i> </p>
Search Efficiency: Recall vs. Latency
<p align="center"> <img src="docs/assets/chart-recall-latency.svg" alt="Recall vs Latency Curve" width="600"> <br> <i>MetalANNS maintains perfect recall at 10x the throughput of competitive CPU libraries.</i> </p>
✨ Elegant API
Designed for the modern Swift developer. Zero boilerplate, fully async/await native, and statically safe.
1. Configure & Initialize
Initialize with a specific state. The compiler will prevent you from calling .search() on an unbuilt index.
import MetalANNS
let config = IndexConfiguration(degree: 32, metric: .cosine)
let index = VectorIndex<String, VectorIndexState.Unbuilt>(configuration: config)2. Parallel Build
Leverage the GPU to build the search graph from your embeddings in seconds.
let readyIndex = try await index.build(
vectors: myEmbeddings, // [[Float]]
ids: myDocumentIDs // [String]
)3. Hybrid Search
Combine vector similarity with SQL-like metadata filtering in a single pass.
// Use the elegant Query DSL
let results = try await readyIndex.search(query: queryVector, topK: 10) {
QueryFilter.equals(Field<String>("category"), "research")
QueryFilter.greaterThan(Field<Float>("relevance"), 0.85)
}
for hit in results {
print("Found \(hit.id) with score: \(hit.score)")
}4. Zero-Copy Persistence
Save your index to disk and load it instantly using memory-mapping — ideal for memory-constrained iOS devices.
try await readyIndex.save(to: fileURL)
// Instant load with zero memory overhead
let loadedIndex = try await VectorIndex<String, VectorIndexState.Ready>
.loadReadOnly(from: fileURL, mode: .mmap)🐊 Technical Superiority
Why choose MetalANNS over HNSW or FAISS?
| Feature | MetalANNS | HNSWLib (CPU) | | :--- | :--- | :--- | | Architecture | CAGRA (GPU Parallel) | HNSW (CPU Sequential) | | Memory copies | Zero (UMA) | High (PCIe/Bus) | | Concurrency | Swift 6 Actors | Mutex/Locks | | Persistence | Zero-copy mmap | Full memory load | | API Safety | Type-State Machine | Runtime checks |
[!IMPORTANT] CAGRA vs. HNSW: HNSW builds a hierarchical graph that is difficult to parallelize during construction. MetalANNS uses a fixed-degree directed graph (CAGRA) which allows thousands of GPU threads to explore the search space simultaneously.
🐊 The Mascot
The MetalANNS Crocodile represents our core philosophy:
- Low Latency: Attacks the search problem with predatory speed.
- Apple Ecosystem: Perfectly adapted to its habitat (Metal/Swift).
- Powerful Grip: High recall that never lets go of accuracy.
📦 Installation
Add MetalANNS to your Package.swift:
dependencies: [
.package(url: "https://github.com/christopherkarani/MetalANNS.git", from: "0.1.2")
]📄 License
MetalANNS is available under the MIT license. See LICENSE for more info.
Package Metadata
Repository: christopherkarani/MetalANNS
Stars: 11
Forks: 2
Open issues: 1
Default branch: main
Primary language: swift
License: MIT
Topics: ai, anns, annsearch, anthropic, langchain, langgraph, metal, openai, rag, swift, swift6
README: README.md