Tutorial

XFaiss works as a drop-in replacement for Faiss — only three changes are needed:

  1. Initialize MU resourcesmakeMuResources()
  2. Use the MU index class — e.g., MuIndexIvfFlat instead of IndexIVFFlat
  3. Sync to device before searchsyncToMuDevice()

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