XVector C API Examples

Code examples demonstrating XVector library usage via the public C API.

XVector provides two ways to run vector search on MX1:

Approach Description When to Use
XVector C API (this page) Native C API for exact KNN, IVF-Flat, IVF-RaBitQ, and HNSW-Flat (experimental) search Custom applications, fine-grained control
XFaiss Drop-in Faiss replacement with MU acceleration Existing Faiss workflows and .faiss index files

This page walks through the KNN exact example; the same C API pattern (context → buffers → load index → search) applies to the IVF-Flat, IVF-RaBitQ, and HNSW-Flat (experimental) indexes.

The xvector-examples package includes a complete KNN example that demonstrates exact nearest neighbor search using the XVector C API.

Compared to the raw XArith/PXL approach, XVector handles device kernel loading, data partitioning, task scheduling, and result merging internally:

Aspect XArith/PXL (raw) XVector C API
Device kernel Write and compile manually Built-in
Memory management Manual PXL alloc/free xvecCreateBuffer / xvecDestroyBuffer
Data partitioning Manual NDArray setup Automatic
Result merging Manual per-task merge Automatic
API style C++ (PXL + device headers) Pure C (xvec_*.h)

Want to see how this search is implemented? The XArith KNN example is the device-kernel version of this same KNN — manual data partitioning, per-task kernel dispatch, and top-k merge that the C API does for you here.

Build

Extract and build the example:

tar -xzf xvector-examples-0.2.2.tar.gz
cd xvector-examples-0.2.2/knn
mkdir build && cd build
cmake ..
make

Run

./knn_xvector                         # run with defaults
./knn_xvector --dim 128 --vectors 1000000 --k 10   # custom parameters

The mubin kernel binary is auto-detected from <exe_dir>/bin/, <exe_dir>/../bin/, or /opt/xvector/bin/.

Options:

Option Default Description
--dim N 1024 Vector dimension
--vectors N 100000 Number of dataset vectors
--queries N 10 Number of query vectors
--k N 5 Number of nearest neighbors
--dist l2\|ip\|all all Distance metric

Environment Variables:

Variable Default Description
XVECTOR_MUBIN_PATH (auto) Path to xvector_kernel.mubin
DEVICEID 0 Device ID
NUMSUB (device) Number of subsystems. Unset asks the device, which differs per part
TASKCOUNT 192 Task count for parallelism

Sample Output

XVector KNN Example

Config: dim=1024 vectors=100000 queries=10 k=5 numSub=auto taskCount=192

Generating random test data... done

KNN L2 Test (vectors=100000, queries=10, dim=1024, k=5)
[xvector] Index loaded: ...
[xvector] Search: ...

KNN DOT_PRODUCT Test (vectors=100000, queries=10, dim=1024, k=5)
[xvector] Index loaded: ...
[xvector] Search: ...

Code Walkthrough

The full example (knn/main.cpp) follows a straightforward flow: create context, load data, search, read results.

Headers

#include <xvector/xvec_buffer.h>
#include <xvector/xvec_context.h>
#include <xvector/xvec_knn_exact.h>
#include <xvector/xvec_types.h>

1. Create Execution Context

The execution context connects to the MX1 device and loads the built-in kernel binary:

xvecExecutionContextTag* ctx = nullptr;
xvecStatus status = xvecCreateContext(&ctx, deviceId, mubinPath);
if (status != XVEC_SUCCESS) {
    // handle error
}

// Configure parallelism. Leave "numSub" unset to use every Sub the device has —
// the count differs per part, and asking for more than it has fails the search.
xvecSetContextProperty(ctx, "taskCount", "192");

For RAII-style resource management, wrap handles in std::unique_ptr with custom deleters. See the full example for ContextDeleter, BufferDeleter, and KnnExactIndexDeleter patterns.

2. Load Index from Raw Vectors

Load a dataset of float32 vectors into a KNN exact index:

xvecKnnExactIndexTag* index = nullptr;
xvecKnnExactLoadIndexFromRawVectors(
    ctx,
    dataset,       // const float* — contiguous float32 array
    vectorCount,   // number of vectors
    dim,           // vector dimension
    XVEC_DISTANCE_L2,  // or XVEC_DISTANCE_DOT_PRODUCT
    &index);

3. Prepare Query Vectors

Create a buffer and copy query vectors into it:

size_t queryBufSize = sizeof(float) * queryCount * dim;

xvecBufferTag* queryBuf = nullptr;
xvecCreateBuffer(ctx, queryBufSize, &queryBuf);

void* queryData = nullptr;
xvecGetBufferData(queryBuf, &queryData);
memcpy(queryData, queryVectors, queryBufSize);

Build the search query and execute:

xvecKnnExactSearchQueryFloat32 searchQuery{};
searchQuery.dimension       = dim;
searchQuery.queryVectorCount = queryCount;
searchQuery.queryVectorsBufferHandle = queryBuf;
searchQuery.topK            = k;

// Allocate result buffer
size_t resultBufSize = sizeof(xvecKnnExactSearchResultFloat32) * queryCount * k;
xvecBufferTag* resultBuf = nullptr;
xvecCreateBuffer(ctx, resultBufSize, &resultBuf);

// Run search
xvecKnnExactSearch(ctx, index, &searchQuery, resultBuf, nullptr);

5. Cleanup

Destroy resources in reverse order:

xvecDestroyBuffer(ctx, resultBuf);
xvecDestroyBuffer(ctx, queryBuf);
xvecKnnExactDestroyIndex(ctx, index);
xvecDestroyContext(ctx);

Next Steps