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.
KNN Exact Search
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_ptrwith custom deleters. See the full example forContextDeleter,BufferDeleter, andKnnExactIndexDeleterpatterns.
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);
4. Execute Search
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
- Writing XArith Kernels — Lower-level VPE vector primitives
- XVector API Reference — Full C API reference