Skip to content

Commit

Permalink
add c api
Browse files Browse the repository at this point in the history
  • Loading branch information
divyegala committed Jan 24, 2024
1 parent 2bfe42d commit 562385b
Show file tree
Hide file tree
Showing 12 changed files with 1,126 additions and 8 deletions.
73 changes: 68 additions & 5 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ option(BUILD_SHARED_LIBS "Build cuvs shared libraries" ON)
option(BUILD_TESTS "Build cuvs unit-tests" ON)
option(BUILD_MICRO_BENCH "Build cuvs C++ micro benchmarks" OFF)
option(BUILD_ANN_BENCH "Build cuvs ann benchmarks" OFF)
option(BUILD_C_LIBRARY "Build raft C API library" OFF)
option(BUILD_C_TESTS "Build raft C API tests" OFF)
option(CUDA_ENABLE_KERNELINFO "Enable kernel resource usage info" OFF)
option(CUDA_ENABLE_LINEINFO
"Enable the -lineinfo option for nvcc (useful for cuda-memcheck / profiler)" OFF
Expand All @@ -71,6 +73,7 @@ option(CUVS_NVTX "Enable nvtx markers" OFF)
if((BUILD_TESTS
OR BUILD_MICRO_BENCH
OR BUILD_ANN_BENCH
OR BUILD_C_LIBRARY
)
AND NOT BUILD_CPU_ONLY
)
Expand All @@ -80,6 +83,11 @@ endif()
if(BUILD_CPU_ONLY)
set(BUILD_SHARED_LIBS OFF)
set(BUILD_TESTS OFF)
set(BUILD_C_LIBRARY OFF)
endif()

if(NOT BUILD_C_LIBRARY)
set(BUILD_C_TESTS OFF)
endif()

# Needed because GoogleBenchmark changes the state of FindThreads.cmake, causing subsequent runs to
Expand Down Expand Up @@ -175,7 +183,11 @@ if(NOT BUILD_CPU_ONLY)
include(cmake/thirdparty/get_raft.cmake)
endif()

if(BUILD_TESTS)
if(BUILD_C_LIBRARY)
include(cmake/thirdparty/get_dlpack.cmake)
endif()

if(BUILD_TESTS OR BUILD_C_TESTS)
include(cmake/thirdparty/get_gtest.cmake)
endif()

Expand Down Expand Up @@ -286,6 +298,35 @@ target_compile_options(
# ensure CUDA symbols aren't relocated to the middle of the debug build binaries
target_link_options(cuvs PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/fatbin.ld")

# ##################################################################################################
# * cuvs_c -------------------------------------------------------------------------------
if(BUILD_C_LIBRARY)
add_library(cuvs_c SHARED src/core/c_api.cpp src/neighbors/cagra_c.cpp)

add_library(cuvs::c_api ALIAS cuvs_c)

set_target_properties(
cuvs_c
PROPERTIES CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE ON
EXPORT_NAME c_api
)

target_compile_options(cuvs_c PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CUVS_CXX_FLAGS}>")

target_include_directories(
cuvs_c
PUBLIC "$<BUILD_INTERFACE:${DLPACK_INCLUDE_DIR}>"
INTERFACE "$<INSTALL_INTERFACE:include>"
)

target_link_libraries(cuvs_c PUBLIC cuvs::cuvs)

# ensure CUDA symbols aren't relocated to the middle of the debug build binaries
target_link_options(cuvs_c PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/fatbin.ld")
endif()

# ##################################################################################################
# * install targets-----------------------------------------------------------
rapids_cmake_install_lib_dir(lib_dir)
Expand All @@ -305,17 +346,34 @@ install(
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

if(BUILD_C_LIBRARY)
install(
TARGETS cuvs_c
DESTINATION ${lib_dir}
COMPONENT c_api
EXPORT cuvs-c-exports
)
endif()

install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/include/cuvs/version_config.hpp
COMPONENT cuvs
DESTINATION include/cuvs
)

if(TARGET cuvs_c)
list(APPEND cuvs_components c_api)
list(APPEND cuvs_export_sets cuvs-c-exports)
set(CUVS_C_TARGET cuvs_c)
endif()

# Use `rapids_export` for 22.04 as it will have COMPONENT support
rapids_export(
INSTALL cuvs
EXPORT_SET cuvs-exports
GLOBAL_TARGETS cuvs
COMPONENTS ${cuvs_components}
COMPONENTS_EXPORT_SET ${cuvs_export_sets}
GLOBAL_TARGETS cuvs ${CUVS_C_TARGET}
NAMESPACE cuvs::
)

Expand All @@ -324,21 +382,26 @@ rapids_export(
rapids_export(
BUILD cuvs
EXPORT_SET cuvs-exports
GLOBAL_TARGETS cuvs
COMPONENTS ${cuvs_components}
COMPONENTS_EXPORT_SET ${cuvs_export_sets}
GLOBAL_TARGETS cuvs ${CUVS_C_TARGET}
NAMESPACE cuvs::
)

# ##################################################################################################
# * shared test/bench headers ------------------------------------------------

if(BUILD_TESTS OR BUILD_MICRO_BENCH)
if(BUILD_TESTS
OR BUILD_MICRO_BENCH
OR BUILD_C_TESTS
)
include(internal/CMakeLists.txt)
endif()

# ##################################################################################################
# * build test executable ----------------------------------------------------

if(BUILD_TESTS)
if(BUILD_TESTS OR BUILD_C_TESTS)
include(test/CMakeLists.txt)
endif()

Expand Down
41 changes: 41 additions & 0 deletions cpp/cmake/thirdparty/get_dlpack.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# =============================================================================
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
# =============================================================================

# This function finds dlpack and sets any additional necessary environment variables.
function(find_and_configure_dlpack VERSION)

include(${rapids-cmake-dir}/find/generate_module.cmake)
rapids_find_generate_module(DLPACK HEADER_NAMES dlpack.h)

rapids_cpm_find(
dlpack ${VERSION}
GIT_REPOSITORY https://github.com/dmlc/dlpack.git
GIT_TAG v${VERSION}
GIT_SHALLOW TRUE
DOWNLOAD_ONLY TRUE
OPTIONS "BUILD_MOCK OFF"
)

if(DEFINED dlpack_SOURCE_DIR)
# otherwise find_package(DLPACK) will set this variable
set(DLPACK_INCLUDE_DIR
"${dlpack_SOURCE_DIR}/include"
PARENT_SCOPE
)
endif()
endfunction()

set(CUVS_MIN_VERSION_dlpack 0.8)

find_and_configure_dlpack(${CUVS_MIN_VERSION_dlpack})
74 changes: 74 additions & 0 deletions cpp/include/cuvs/core/c_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <stdint.h>

#include <cuda_runtime.h>

/**
* @defgroup c_api C API Core Types and Functions
* @{
*/

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief An opaque C handle for C++ type `raft::resources`
*
*/
typedef uintptr_t cuvsResources_t;

/**
* @brief An enum denoting return values for function calls
*
*/
typedef enum { CUVS_ERROR, CUVS_SUCCESS } cuvsError_t;

/**
* @brief Create an Initialized opaque C handle for C++ type `raft::resources`
*
* @param[in] res cuvsResources_t opaque C handle
* @return cuvsError_t
*/
cuvsError_t cuvsResourcesCreate(cuvsResources_t* res);

/**
* @brief Destroy and de-allocate opaque C handle for C++ type `raft::resources`
*
* @param[in] res cuvsResources_t opaque C handle
* @return cuvsError_t
*/
cuvsError_t cuvsResourcesDestroy(cuvsResources_t res);

/**
* @brief Set cudaStream_t on cuvsResources_t to queue CUDA kernels on APIs
* that accept a cuvsResources_t handle
*
* @param[in] res cuvsResources_t opaque C handle
* @param[in] stream cudaStream_t stream to queue CUDA kernels
* @return cuvsError_t
*/
cuvsError_t cuvsStreamSet(cuvsResources_t res, cudaStream_t stream);

#ifdef __cplusplus
}
#endif

/** @} */
105 changes: 105 additions & 0 deletions cpp/include/cuvs/core/detail/interop.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <raft/core/device_mdspan.hpp>
#include <raft/core/error.hpp>
#include <raft/core/host_mdspan.hpp>
#include <raft/core/mdspan_types.hpp>

#include <dlpack/dlpack.h>
#include <rmm/device_buffer.hpp>
#include <sys/types.h>

namespace cuvs::core::detail {

template <typename AccessorType>
DLDevice accessor_type_to_DLDevice()
{
if constexpr (AccessorType::is_host_accessible and AccessorType::is_device_accessible) {
return DLDevice{kDLCUDAManaged};
} else if constexpr (AccessorType::is_device_accessible) {
return DLDevice{kDLCUDA};
} else if constexpr (AccessorType::is_host_accessible) {
return DLDevice{kDLCPU};
}
}

template <typename T>
DLDataType data_type_to_DLDataType()
{
uint8_t const bits{sizeof(T) * 8};
uint16_t const lanes{1};
if constexpr (std::is_floating_point_v<T>) {
return DLDataType{kDLFloat, bits, lanes};
} else if constexpr (std::is_signed_v<T>) {
return DLDataType{kDLInt, bits, lanes};
} else {
return DLDataType{kDLUInt, bits, lanes};
}
}

bool is_dlpack_device_compatible(DLTensor tensor)
{
return tensor.device.device_type == kDLCUDAManaged || tensor.device.device_type == kDLCUDAHost ||
tensor.device.device_type == kDLCUDA;
}

bool is_dlpack_host_compatible(DLTensor tensor)
{
return tensor.device.device_type == kDLCUDAManaged || tensor.device.device_type == kDLCUDAHost ||
tensor.device.device_type == kDLCPU;
}

template <typename MdspanType, typename = raft::is_mdspan_t<MdspanType>>
MdspanType from_dlpack(DLManagedTensor* managed_tensor)
{
auto tensor = managed_tensor->dl_tensor;

auto to_data_type = data_type_to_DLDataType<typename MdspanType::value_type>();
RAFT_EXPECTS(to_data_type.code == tensor.dtype.code,
"code mismatch between return mdspan and DLTensor");
RAFT_EXPECTS(to_data_type.bits == tensor.dtype.bits,
"bits mismatch between return mdspan and DLTensor");
RAFT_EXPECTS(to_data_type.lanes == tensor.dtype.lanes,
"lanes mismatch between return mdspan and DLTensor");
RAFT_EXPECTS(tensor.dtype.lanes == 1, "More than 1 DLTensor lanes not supported");
RAFT_EXPECTS(tensor.strides == nullptr, "Strided memory layout for DLTensor not supported");

auto to_device = accessor_type_to_DLDevice<typename MdspanType::accessor_type>();
if (to_device.device_type == kDLCUDA) {
RAFT_EXPECTS(is_dlpack_device_compatible(tensor),
"device_type mismatch between return mdspan and DLTensor");
} else if (to_device.device_type == kDLCPU) {
RAFT_EXPECTS(is_dlpack_host_compatible(tensor),
"device_type mismatch between return mdspan and DLTensor");
}

RAFT_EXPECTS(MdspanType::extents_type::rank() == tensor.ndim,
"ndim mismatch between return mdspan and DLTensor");

// auto exts = typename MdspanType::extents_type{tensor.shape};
std::array<int64_t, MdspanType::extents_type::rank()> shape{};
for (int64_t i = 0; i < tensor.ndim; ++i) {
shape[i] = tensor.shape[i];
}
auto exts = typename MdspanType::extents_type{shape};

return MdspanType{reinterpret_cast<typename MdspanType::data_handle_type>(tensor.data), exts};
}

} // namespace cuvs::core::detail
Loading

0 comments on commit 562385b

Please sign in to comment.