Tutorial
XFaiss works as a drop-in replacement for Faiss — only three changes are needed:
- Initialize MU resources —
makeMuResources() - Use the MU index class — e.g.,
MuIndexIvfFlatinstead ofIndexIVFFlat - Sync to device before search —
syncToMuDevice()
Source files are in the xfaiss_tutorial/ directory of the XFaiss repository. All examples share a common utility header (TutorialUtils.h) for data generation, timing, and output formatting.
IVF-Flat
xfaiss_tutorial/1-MuIVFFlat.cpp
#include "TutorialUtils.h"
#include <cassert>
#include <faiss/IndexFlat.h>
#include <faiss/impl/FaissException.h>
#include <faiss/mu/MuIndexIvfFlat.h>
#include <faiss/mu/MuResources.h>
int main() {
try {
size_t d = 128;
size_t nb = 1000000;
size_t nq = 100;
size_t nlist = 100;
size_t nprobe = 10;
size_t k = 5;
auto data = prepareData(d, nb, nq);
faiss::IndexFlatL2 quantizer(data.d);
auto config = faiss::MuResourcesConfig::fromConfigManager();
config.taskCount = 192;
auto resources = faiss::makeMuResources(config);
faiss::MuIndexIvfFlat index(resources, &quantizer, data.d, nlist);
assert(!index.is_trained);
double train_ms =
measureMs([&] { index.train(data.nb, data.xb.get()); });
assert(index.is_trained);
double add_ms = measureMs([&] { index.add(data.nb, data.xb.get()); });
double sync_ms = measureMs([&] { index.syncToMuDevice(); });
index.nprobe = nprobe;
auto I = std::make_unique<idx_t[]>(k * data.nq);
auto D = std::make_unique<float[]>(k * data.nq);
double search_ms = measureMs([&] {
index.search(data.nq, data.xq.get(), k, D.get(), I.get());
});
printHeader("FAISS IVFFlat Search Result (MU)", config.deviceId);
printTimings(search_ms, add_ms, sync_ms, train_ms);
printTopK(I.get(), D.get(), data.nq, k);
return 0;
} catch (const faiss::FaissException& e) {
std::cerr << "Faiss error: " << e.what() << std::endl;
return 1;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}
IVF-RaBitQ
xfaiss_tutorial/2-MuIVFRaBitQ.cpp
The same three-step pattern applies. The key differences from IVF-Flat are the index class, a higher taskCount, and the qb (query bits) parameter:
| IVF-Flat | IVF-RaBitQ | |
|---|---|---|
| Index class | MuIndexIvfFlat | MuIndexIvfRabitq |
taskCount | 192 | 3072 |
qb | N/A | Query quantization bits (e.g., 4) |
#include "TutorialUtils.h"
#include <cassert>
#include <faiss/IndexFlat.h>
#include <faiss/impl/FaissException.h>
#include <faiss/mu/MuIndexIvfRabitq.h>
#include <faiss/mu/MuResources.h>
int main() {
try {
size_t d = 128;
size_t nb = 1000000;
size_t nq = 100;
size_t nlist = 100;
size_t nprobe = 10;
uint8_t query_bits = 4;
size_t k = 5;
auto data = prepareData(d, nb, nq);
faiss::IndexFlatL2 quantizer(data.d);
auto config = faiss::MuResourcesConfig::fromConfigManager();
config.taskCount = 3072;
auto resources = faiss::makeMuResources(config);
faiss::MuIndexIvfRabitq index(resources, &quantizer, data.d, nlist);
assert(!index.is_trained);
double train_ms =
measureMs([&] { index.train(data.nb, data.xb.get()); });
assert(index.is_trained);
double add_ms = measureMs([&] { index.add(data.nb, data.xb.get()); });
double sync_ms = measureMs([&] { index.syncToMuDevice(); });
index.nprobe = nprobe;
index.qb = query_bits;
auto I = std::make_unique<idx_t[]>(k * data.nq);
auto D = std::make_unique<float[]>(k * data.nq);
double search_ms = measureMs([&] {
index.search(data.nq, data.xq.get(), k, D.get(), I.get());
});
printHeader("FAISS IVFRaBitQ Search Result (MU)", config.deviceId);
printTimings(search_ms, add_ms, sync_ms, train_ms);
printTopK(I.get(), D.get(), data.nq, k);
return 0;
} catch (const faiss::FaissException& e) {
std::cerr << "Faiss error: " << e.what() << std::endl;
return 1;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}
Flat (KNN Exact)
xfaiss_tutorial/3-MuFlat.cpp
Brute-force exact nearest neighbor search on the MU device. No training or quantizer is needed — just add vectors, sync, and search.
#include "TutorialUtils.h"
#include <faiss/impl/FaissException.h>
#include <faiss/mu/MuIndexFlat.h>
#include <faiss/mu/MuResources.h>
int main() {
try {
size_t d = 128;
size_t nb = 1000000;
size_t nq = 100;
size_t k = 5;
auto data = prepareData(d, nb, nq);
auto config = faiss::MuResourcesConfig::fromConfigManager();
config.taskCount = 192;
auto resources = faiss::makeMuResources(config);
faiss::MuIndexFlat index(resources, data.d);
double add_ms = measureMs([&] { index.add(data.nb, data.xb.get()); });
double sync_ms = measureMs([&] { index.syncToMuDevice(); });
auto I = std::make_unique<idx_t[]>(k * data.nq);
auto D = std::make_unique<float[]>(k * data.nq);
double search_ms = measureMs([&] {
index.search(data.nq, data.xq.get(), k, D.get(), I.get());
});
printHeader("FAISS IndexFlat Search Result (MU)", config.deviceId);
printTimings(search_ms, add_ms, sync_ms);
printTopK(I.get(), D.get(), data.nq, k);
return 0;
} catch (const faiss::FaissException& e) {
std::cerr << "Faiss error: " << e.what() << std::endl;
return 1;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}
Next Steps
- Benchmark Reference — Preset and option reference for
bench