Skip to content

Commit

Permalink
Host implementation of to_arrow using nanoarrow (#16297)
Browse files Browse the repository at this point in the history
Adds the corresponding `to_arrow_host` functions for interop using `ArrowDeviceArray`. This includes updating the version of nanoarrow in use to pick up some bug fixes and features.

Authors:
  - Matt Topol (https://github.com/zeroshade)
  - Muhammad Haseeb (https://github.com/mhaseeb123)
  - Vyas Ramasubramani (https://github.com/vyasr)

Approvers:
  - Muhammad Haseeb (https://github.com/mhaseeb123)
  - Vyas Ramasubramani (https://github.com/vyasr)

URL: #16297
  • Loading branch information
zeroshade authored Jul 24, 2024
1 parent 8c1749b commit 62625f1
Show file tree
Hide file tree
Showing 16 changed files with 1,760 additions and 161 deletions.
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ add_library(
src/interop/arrow_utilities.cpp
src/interop/to_arrow.cu
src/interop/to_arrow_device.cu
src/interop/to_arrow_host.cu
src/interop/from_arrow_device.cu
src/interop/from_arrow_host.cu
src/interop/from_arrow_stream.cu
Expand Down
4 changes: 2 additions & 2 deletions cpp/cmake/thirdparty/get_nanoarrow.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ function(find_and_configure_nanoarrow)
# Currently we need to always build nanoarrow so we don't pickup a previous installed version
set(CPM_DOWNLOAD_nanoarrow ON)
rapids_cpm_find(
nanoarrow 0.5.0
nanoarrow 0.6.0.dev
GLOBAL_TARGETS nanoarrow
CPM_ARGS
GIT_REPOSITORY https://github.com/apache/arrow-nanoarrow.git
GIT_TAG 11e73a8c85b45e3d49c8c541b4e1497a649fe03c
GIT_TAG 1e2664a70ec14907409cadcceb14d79b9670bcdb
GIT_SHALLOW FALSE
OPTIONS "BUILD_SHARED_LIBS OFF" "NANOARROW_NAMESPACE cudf"
)
Expand Down
80 changes: 70 additions & 10 deletions cpp/include/cudf/interop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ struct column_metadata {
* Converts the `cudf::table_view` to `arrow::Table` with the provided
* metadata `column_names`.
*
* @deprecated Since 24.08. Use cudf::to_arrow_host instead.
*
* @throws cudf::logic_error if `column_names` size doesn't match with number of columns.
*
* @param input table_view that needs to be converted to arrow Table
Expand All @@ -150,16 +152,19 @@ struct column_metadata {
* 9 which is the maximum precision for 32-bit types. Similarly, numeric::decimal128 will be
* converted to Arrow decimal128 of the precision 38.
*/
std::shared_ptr<arrow::Table> to_arrow(table_view input,
std::vector<column_metadata> const& metadata = {},
rmm::cuda_stream_view stream = cudf::get_default_stream(),
arrow::MemoryPool* ar_mr = arrow::default_memory_pool());
[[deprecated]] std::shared_ptr<arrow::Table> to_arrow(
table_view input,
std::vector<column_metadata> const& metadata = {},
rmm::cuda_stream_view stream = cudf::get_default_stream(),
arrow::MemoryPool* ar_mr = arrow::default_memory_pool());

/**
* @brief Create `arrow::Scalar` from cudf scalar `input`
*
* Converts the `cudf::scalar` to `arrow::Scalar`.
*
* @deprecated Since 24.08.
*
* @param input scalar that needs to be converted to arrow Scalar
* @param metadata Contains hierarchy of names of columns and children
* @param stream CUDA stream used for device memory operations and kernel launches
Expand All @@ -172,10 +177,11 @@ std::shared_ptr<arrow::Table> to_arrow(table_view input,
* 9 which is the maximum precision for 32-bit types. Similarly, numeric::decimal128 will be
* converted to Arrow decimal128 of the precision 38.
*/
std::shared_ptr<arrow::Scalar> to_arrow(cudf::scalar const& input,
column_metadata const& metadata = {},
rmm::cuda_stream_view stream = cudf::get_default_stream(),
arrow::MemoryPool* ar_mr = arrow::default_memory_pool());
[[deprecated]] std::shared_ptr<arrow::Scalar> to_arrow(
cudf::scalar const& input,
column_metadata const& metadata = {},
rmm::cuda_stream_view stream = cudf::get_default_stream(),
arrow::MemoryPool* ar_mr = arrow::default_memory_pool());

/**
* @brief typedef for a unique_ptr to an ArrowSchema with custom deleter
Expand Down Expand Up @@ -329,28 +335,82 @@ unique_device_array_t to_arrow_device(
rmm::cuda_stream_view stream = cudf::get_default_stream(),
rmm::device_async_resource_ref mr = rmm::mr::get_current_device_resource());

/**
* @brief Copy table view data to host and create `ArrowDeviceArray` for it
*
* Populates the C struct ArrowDeviceArray, copying the cudf data to the host. The
* returned ArrowDeviceArray will have a device_type of CPU and will have no ties
* to the memory referenced by the table view passed in. The deleter for the
* returned unique_ptr will call the release callback on the ArrowDeviceArray
* automatically.
*
* @note For decimals, since the precision is not stored for them in libcudf, it will
* be converted to an Arrow decimal128 that has the widest-precision the cudf decimal type
* supports. For example, numeric::decimal32 will be converted to Arrow decimal128 of the precision
* 9 which is the maximum precision for 32-bit types. Similarly, numeric::decimal128 will be
* converted to Arrow decimal128 of precision 38.
*
* @param table Input table
* @param stream CUDA stream used for the device memory operations and kernel launches
* @param mr Device memory resource used for any allocations during conversion
* @return ArrowDeviceArray generated from input table
*/
unique_device_array_t to_arrow_host(
cudf::table_view const& table,
rmm::cuda_stream_view stream = cudf::get_default_stream(),
rmm::device_async_resource_ref mr = rmm::mr::get_current_device_resource());

/**
* @brief Copy column view data to host and create `ArrowDeviceArray` for it
*
* Populates the C struct ArrowDeviceArray, copying the cudf data to the host. The
* returned ArrowDeviceArray will have a device_type of CPU and will have no ties
* to the memory referenced by the column view passed in. The deleter for the
* returned unique_ptr will call the release callback on the ArrowDeviceArray
* automatically.
*
* @note For decimals, since the precision is not stored for them in libcudf, it will
* be converted to an Arrow decimal128 that has the widest-precision the cudf decimal type
* supports. For example, numeric::decimal32 will be converted to Arrow decimal128 of the precision
* 9 which is the maximum precision for 32-bit types. Similarly, numeric::decimal128 will be
* converted to Arrow decimal128 of precision 38.
*
* @param col Input column
* @param stream CUDA stream used for the device memory operations and kernel launches
* @param mr Device memory resource used for any allocations during conversion
* @return ArrowDeviceArray generated from input column
*/
unique_device_array_t to_arrow_host(
cudf::column_view const& col,
rmm::cuda_stream_view stream = cudf::get_default_stream(),
rmm::device_async_resource_ref mr = rmm::mr::get_current_device_resource());

/**
* @brief Create `cudf::table` from given arrow Table input
*
* @deprecated Since 24.08. Use cudf::from_arrow_host instead.
*
* @param input arrow:Table that needs to be converted to `cudf::table`
* @param stream CUDA stream used for device memory operations and kernel launches
* @param mr Device memory resource used to allocate `cudf::table`
* @return cudf table generated from given arrow Table
*/
std::unique_ptr<table> from_arrow(
[[deprecated]] std::unique_ptr<table> from_arrow(
arrow::Table const& input,
rmm::cuda_stream_view stream = cudf::get_default_stream(),
rmm::device_async_resource_ref mr = rmm::mr::get_current_device_resource());

/**
* @brief Create `cudf::scalar` from given arrow Scalar input
*
* @deprecated Since 24.08.
*
* @param input `arrow::Scalar` that needs to be converted to `cudf::scalar`
* @param stream CUDA stream used for device memory operations and kernel launches
* @param mr Device memory resource used to allocate `cudf::scalar`
* @return cudf scalar generated from given arrow Scalar
*/
std::unique_ptr<cudf::scalar> from_arrow(
[[deprecated]] std::unique_ptr<cudf::scalar> from_arrow(
arrow::Scalar const& input,
rmm::cuda_stream_view stream = cudf::get_default_stream(),
rmm::device_async_resource_ref mr = rmm::mr::get_current_device_resource());
Expand Down
53 changes: 0 additions & 53 deletions cpp/include/cudf/interop/detail/arrow.hpp

This file was deleted.

31 changes: 31 additions & 0 deletions cpp/src/interop/arrow_utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@

#include "arrow_utilities.hpp"

#include <cudf/column/column_view.hpp>
#include <cudf/types.hpp>
#include <cudf/utilities/error.hpp>

#include <rmm/exec_policy.hpp>
#include <rmm/resource_ref.hpp>

#include <thrust/for_each.h>
#include <thrust/iterator/counting_iterator.h>

#include <nanoarrow/nanoarrow.h>

namespace cudf {
Expand Down Expand Up @@ -83,9 +90,33 @@ ArrowType id_to_arrow_type(cudf::type_id id)
case cudf::type_id::FLOAT32: return NANOARROW_TYPE_FLOAT;
case cudf::type_id::FLOAT64: return NANOARROW_TYPE_DOUBLE;
case cudf::type_id::TIMESTAMP_DAYS: return NANOARROW_TYPE_DATE32;
case cudf::type_id::DECIMAL128: return NANOARROW_TYPE_DECIMAL128;
default: CUDF_FAIL("Unsupported type_id conversion to arrow type", cudf::data_type_error);
}
}

ArrowType id_to_arrow_storage_type(cudf::type_id id)
{
switch (id) {
case cudf::type_id::TIMESTAMP_SECONDS:
case cudf::type_id::TIMESTAMP_MILLISECONDS:
case cudf::type_id::TIMESTAMP_MICROSECONDS:
case cudf::type_id::TIMESTAMP_NANOSECONDS: return NANOARROW_TYPE_INT64;
case cudf::type_id::DURATION_SECONDS:
case cudf::type_id::DURATION_MILLISECONDS:
case cudf::type_id::DURATION_MICROSECONDS:
case cudf::type_id::DURATION_NANOSECONDS: return NANOARROW_TYPE_INT64;
default: return id_to_arrow_type(id);
}
}

int initialize_array(ArrowArray* arr, ArrowType storage_type, cudf::column_view column)
{
NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromType(arr, storage_type));
arr->length = column.size();
arr->null_count = column.null_count();
return NANOARROW_OK;
}

} // namespace detail
} // namespace cudf
43 changes: 42 additions & 1 deletion cpp/src/interop/arrow_utilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@

#include <cudf/types.hpp>

#include <rmm/cuda_stream_view.hpp>
#include <rmm/device_buffer.hpp>
#include <rmm/mr/device/per_device_resource.hpp>
#include <rmm/resource_ref.hpp>

#include <nanoarrow/nanoarrow.h>
#include <nanoarrow/nanoarrow_types.h>

namespace cudf {
namespace detail {
Expand Down Expand Up @@ -47,5 +51,42 @@ data_type arrow_to_cudf_type(ArrowSchemaView const* arrow_view);
*/
ArrowType id_to_arrow_type(cudf::type_id id);

/**
* @brief Map cudf column type id to the storage type for Arrow
*
* Specifically this is for handling the underlying storage type of
* timestamps and durations.
*
* @param id column type id
* @return ArrowType storage type
*/
ArrowType id_to_arrow_storage_type(cudf::type_id id);

/**
* @brief Helper to initialize ArrowArray struct
*
* @param arr Pointer to ArrowArray to initialize
* @param storage_type The type to initialize with
* @param column view for column to get the length and null count from
* @return nanoarrow status code, should be NANOARROW_OK if there are no errors
*/
int initialize_array(ArrowArray* arr, ArrowType storage_type, cudf::column_view column);

/**
* @brief Helper to convert decimal values to 128-bit versions for Arrow compatibility
*
* The template parameter should be the underlying type of the data (e.g. int32_t for
* 32-bit decimal and int64_t for 64-bit decimal).
*
* @param input column_view of the data
* @param stream cuda stream to perform the operations on
* @param mr memory resource to allocate the returned device_uvector with
* @return unique_ptr to a device_buffer containing the upcasted data
*/
template <typename DeviceType>
std::unique_ptr<rmm::device_buffer> decimals_to_arrow(cudf::column_view input,
rmm::cuda_stream_view stream,
rmm::device_async_resource_ref mr);

} // namespace detail
} // namespace cudf
10 changes: 5 additions & 5 deletions cpp/src/interop/from_arrow_device.cu
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include <cudf/detail/transform.hpp>
#include <cudf/detail/unary.hpp>
#include <cudf/interop.hpp>
#include <cudf/interop/detail/arrow.hpp>
#include <cudf/table/table_view.hpp>
#include <cudf/types.hpp>
#include <cudf/utilities/default_stream.hpp>
Expand All @@ -39,6 +38,7 @@

#include <nanoarrow/nanoarrow.h>
#include <nanoarrow/nanoarrow.hpp>
#include <nanoarrow/nanoarrow_device.h>

namespace cudf {

Expand Down Expand Up @@ -144,9 +144,6 @@ dispatch_tuple_t dispatch_from_arrow_device::operator()<cudf::string_view>(
rmm::cuda_stream_view stream,
rmm::device_async_resource_ref mr)
{
CUDF_EXPECTS(schema->type != NANOARROW_TYPE_LARGE_STRING,
"Large strings are not yet supported in from_arrow_device",
cudf::data_type_error);
if (input->length == 0) {
return std::make_tuple<column_view, owned_columns_t>(
{type,
Expand All @@ -158,12 +155,15 @@ dispatch_tuple_t dispatch_from_arrow_device::operator()<cudf::string_view>(
{});
}

auto offsets_view = column_view{data_type(type_id::INT32),
data_type offsets_type(type_id::INT32);
if (schema->type == NANOARROW_TYPE_LARGE_STRING) { offsets_type = data_type(type_id::INT64); }
auto offsets_view = column_view{offsets_type,
static_cast<size_type>(input->offset + input->length) + 1,
input->buffers[fixed_width_data_buffer_idx],
nullptr,
0,
0};

return std::make_tuple<column_view, owned_columns_t>(
{type,
static_cast<size_type>(input->length),
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/interop/from_arrow_host.cu
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#include <cudf/detail/unary.hpp>
#include <cudf/dictionary/dictionary_factories.hpp>
#include <cudf/interop.hpp>
#include <cudf/interop/detail/arrow.hpp>
#include <cudf/table/table_view.hpp>
#include <cudf/types.hpp>
#include <cudf/utilities/default_stream.hpp>
Expand All @@ -42,6 +41,7 @@

#include <nanoarrow/nanoarrow.h>
#include <nanoarrow/nanoarrow.hpp>
#include <nanoarrow/nanoarrow_device.h>

namespace cudf {
namespace detail {
Expand Down
Loading

0 comments on commit 62625f1

Please sign in to comment.