C Guidelines
This page collects the engineering conventions that keep cuVS C APIs stable, predictable, and easy to use from downstream projects and language bindings. Start with the Contributor Guide, then use this page when designing public C APIs, C wrappers, or C-facing documentation.
Local Development
Most C API changes can be developed directly in this repository. Cross-project work may also require a local RAFT build or a downstream project that consumes the installed cuVS C headers and shared libraries.
If source builds are not being used, install the local cuVS C artifacts into the consuming project’s environment before testing the downstream change.
Public Interface
General Guidelines
Public C APIs should be thin, ABI-stable wrappers around cuVS implementation code. Keep C headers free of C++ types, templates, namespaces, exceptions, and RAII-only ownership patterns.
Expose only C-compatible types:
- Opaque handles for resources, indexes, models, and parameter objects.
- Plain C structs for lightweight metadata and simple value groups.
cuvsError_treturn values for API status.- Explicit pointer outputs for objects created by the API.
Prefer explicit create and destroy functions for every opaque object that owns memory or other resources.
API Stability
The C API is the stable boundary used by downstream integrations and cuVS language bindings. Add new functions or fields before removing old ones, avoid changing the meaning of existing parameters, and keep ABI compatibility in mind when changing public structs or exported symbols.
Stateless C APIs
Prefer stateless functions that take all required state explicitly:
Avoid APIs that hide global state, allocate persistent internal state without an owning handle, or require callers to understand C++ object lifetimes.
Functions On State
When a C API creates an index, model, resource, or parameter object, expose matching operations for lifecycle and persistence in the same API family:
Common Design Considerations
- Use
.hfor public C headers and keep them consumable from both C and C++. - Wrap declarations with
extern "C"guards when a header can be included from C++. - Mark exported public functions with
CUVS_EXPORT. - Keep ownership explicit: the API that creates an object should document the matching destroy function.
- Use DLPack tensors for array inputs and outputs where possible so callers can pass data across language and framework boundaries.
Performance
Keep C wrappers thin. Validate inputs and translate handles at the boundary, but leave expensive work in the underlying cuVS implementation.
Avoid hidden host-device copies and hidden synchronization. If a wrapper needs to synchronize, document that behavior clearly.
Threading Model
C APIs should be safe to call from multiple host threads when each thread uses its own cuvsResources_t instance. Treat cuvsResources_t as the boundary for streams, memory resources, communication handles, and library handles.
Avoid mutable process-wide state in C wrappers. If shared state is unavoidable, make ownership and synchronization explicit.
Asynchronous Operations And Stream Ordering
C APIs should preserve the stream-ordering behavior of the underlying cuVS implementation. Do not add hidden synchronization only to simplify wrapper code.
When a C function accepts cuvsResources_t, use the stream and resources associated with that handle. Work queued by the caller before the API should complete before internal work starts, and work queued by the caller after the API returns should wait for internal work that affects the result.
Resource Management
Every successful create call that returns an owning handle should have a matching destroy call in examples and tests:
Destroy functions should tolerate cleanup after partial setup when practical, and examples should release resources in the reverse order they were acquired.
Multi-GPU
Multi-GPU C APIs generally use one of two execution strategies:
- Single-process multi-GPU: one host process owns and coordinates multiple GPUs. Use
cuvsMultiGpuResourcesCreateorcuvsMultiGpuResourcesCreateWithDeviceIdsfor this model. These functions return acuvsResources_tbacked byraft::device_resources_snmg; release it withcuvsMultiGpuResourcesDestroy. - One-process-per-GPU: each process owns one GPU and participates as a communication rank. APIs that wrap this model should accept the process-local
cuvsResources_tfor the calling rank and document how communicator setup, rank ownership, and synchronization are provided by the underlying implementation.
APIs may support either strategy or both, but they should document which resource handle users are expected to create. Use cuvsMultiGpuResourcesSetMemoryPool to configure an RMM memory pool across devices managed by a single-process multi-GPU resource. Continue to use cuvsStreamSet, cuvsStreamGet, and cuvsStreamSync for stream ownership and synchronization through the supplied opaque resource handle.
Single-GPU C APIs should not require communication libraries or multi-GPU setup.
Using Just-in-Time Link-Time Optimization
C APIs may call implementations that use JIT link-time optimization, but the C wrapper should not duplicate JIT LTO policy or expose C++ implementation details. Keep runtime behavior documented at the API level when JIT compilation can affect first-call latency or cache behavior.
For runtime and cache behavior, see JIT Compilation. For implementation guidance, see Link-time Optimization.
Coding Style
Formatting
cuVS uses pre-commit to run formatting, linting, spelling, and copyright checks. Install it with conda:
Run checks before committing:
Run the full suite across the repository when needed:
You can also install the git hook:
Core Hooks
C headers and C wrapper implementation files are checked by the same formatting, spelling, Doxygen, and copyright hooks used by the rest of cuVS.
Run Doxygen checks for public C API documentation:
codespell catches spelling issues. To apply suggested fixes interactively, run:
Include Style
Use #include <cuvs/...> for public cuVS C headers. Keep public C headers minimal and avoid including private C++ implementation headers from the public C interface.
Copyright
RAPIDS pre-commit hooks check copyright headers on modified tracked files. To run that check manually:
Code Quality
Testing
Public C APIs need direct test coverage because downstream projects and language bindings rely on their runtime and ABI behavior. Prefer tests that exercise public entry points, lifecycle functions, error paths, and resource cleanup.
Error Handling
C APIs should return cuvsError_t and should not let C++ exceptions cross the C boundary. Translate implementation failures into C error values and make sure callers can retrieve useful diagnostic information when available.
Destroy functions should avoid throwing or failing in ways that make cleanup unsafe.
Documentation
Public C APIs require user-facing Doxygen documentation. Document the purpose, parameters, return values, ownership rules, matching destroy functions, and any constraints that affect correct use.