Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
24b1f68
First pass of using the new APIs
vyasr Mar 20, 2025
a09a31d
Fix some C++ API docs
vyasr Mar 20, 2025
5d2884c
Remove now unused APIs
vyasr Mar 20, 2025
b4760ac
Don't call release manually anymore
vyasr Mar 21, 2025
72ef25d
Move table to arrow interop functions to Table class via capsule
vyasr Mar 21, 2025
4bafc1f
Move from_arrow table interop to Table constructor overload
vyasr Mar 21, 2025
17ce423
Move column from_arrow logic to constructor
vyasr Mar 22, 2025
2c65d72
Add missing support for empty type
vyasr Mar 22, 2025
94a2c02
Properly handle length 0 list columns when importing
vyasr Mar 22, 2025
c5cd26b
Support modifying struct field names when exporting via to_arrow
vyasr Mar 22, 2025
219a506
One more fix for empty type
vyasr Mar 22, 2025
6edcf13
Change null_count API to accept a gpumemoryview instead of a pointer …
vyasr Mar 22, 2025
c89c86b
Remove invalid access to gpumemoryview.obj
vyasr Mar 24, 2025
bbbc422
Fix a few more places that need to handle EMPTY
vyasr Mar 24, 2025
83b6b92
Update one more use of null_count to match the updated API signature
vyasr Mar 24, 2025
e2df6b3
One more handling of EMPTY types
vyasr Mar 24, 2025
a820a6c
Fix various typos in null type handling
vyasr Mar 25, 2025
8ba32e3
Revert "Remove invalid access to gpumemoryview.obj"
vyasr Mar 26, 2025
69fd71f
Wrap column views in objects that expose the CUDA array interface and…
vyasr Mar 26, 2025
08ae13a
Update pytest-benchmark bound
vyasr Apr 1, 2025
2197a21
Add a benchmark using pyarrow for construction
vyasr Apr 1, 2025
3eb72ee
Fix comments
vyasr Apr 1, 2025
f07f8cb
More comments
vyasr Apr 1, 2025
b226766
Merge remote-tracking branch 'upstream/branch-25.06' into feat/arrow_…
vyasr Apr 9, 2025
281ece3
Expose the protocol for columns as well and make column implementatio…
vyasr Apr 9, 2025
b3f29eb
Centralize common interop functions
vyasr Apr 9, 2025
db92c88
Rename to _interop_helpers
vyasr Apr 9, 2025
7beecf6
Some cleanup and commenting
vyasr Apr 9, 2025
7c917a2
Fix cudf Python chunk expectations
vyasr Apr 9, 2025
04e37b4
Merge remote-tracking branch 'upstream/branch-25.06' into feat/arrow_…
vyasr Apr 9, 2025
1e4e37e
Add one more include of dlpack
vyasr Apr 10, 2025
e26bd3c
Typo
vyasr Apr 10, 2025
a6b8284
More targets
vyasr Apr 10, 2025
83acdcc
Fix prefetching for EMPTY
vyasr Apr 10, 2025
05a0806
Merge remote-tracking branch 'upstream/branch-25.06' into feat/arrow_…
vyasr Apr 10, 2025
a141d21
New envs need updates
vyasr Apr 10, 2025
5e7ff46
Merge remote-tracking branch 'upstream/branch-25.06' into feat/arrow_…
vyasr Apr 15, 2025
1710d4e
Add helper function for empty columns
vyasr Apr 15, 2025
b7ee1bf
Add missing throw
vyasr Apr 15, 2025
932b0ad
Switch to uintptr_t
vyasr Apr 15, 2025
fd5ab31
Rename cudf_object to plc_object
vyasr Apr 15, 2025
3176f39
Add comments on inline C++
vyasr Apr 15, 2025
457f677
Remove redundant is_fixed_point check
vyasr Apr 15, 2025
bb8a2f0
Minor cleanup
vyasr Apr 15, 2025
33b3cc8
Merge remote-tracking branch 'upstream/branch-25.06' into feat/arrow_…
vyasr Apr 15, 2025
1a3197c
Fix incomplete replacement
vyasr Apr 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion conda/environments/all_cuda-118_arch-aarch64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ dependencies:
- pyarrow>=14.0.0,<20.0.0a0
- pydata-sphinx-theme>=0.15.4
- pynvml>=12.0.0,<13.0.0a0
- pytest-benchmark
- pytest-benchmark<5.1.0
- pytest-cases>=3.8.2
- pytest-cov
- pytest-rerunfailures
Expand Down
2 changes: 1 addition & 1 deletion conda/environments/all_cuda-118_arch-x86_64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ dependencies:
- pyarrow>=14.0.0,<20.0.0a0
- pydata-sphinx-theme>=0.15.4
- pynvml>=12.0.0,<13.0.0a0
- pytest-benchmark
- pytest-benchmark<5.1.0
- pytest-cases>=3.8.2
- pytest-cov
- pytest-rerunfailures
Expand Down
2 changes: 1 addition & 1 deletion conda/environments/all_cuda-128_arch-aarch64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ dependencies:
- pydata-sphinx-theme>=0.15.4
- pynvjitlink>=0.0.0a0
- pynvml>=12.0.0,<13.0.0a0
- pytest-benchmark
- pytest-benchmark<5.1.0
- pytest-cases>=3.8.2
- pytest-cov
- pytest-rerunfailures
Expand Down
2 changes: 1 addition & 1 deletion conda/environments/all_cuda-128_arch-x86_64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ dependencies:
- pydata-sphinx-theme>=0.15.4
- pynvjitlink>=0.0.0a0
- pynvml>=12.0.0,<13.0.0a0
- pytest-benchmark
- pytest-benchmark<5.1.0
- pytest-cases>=3.8.2
- pytest-cov
- pytest-rerunfailures
Expand Down
4 changes: 2 additions & 2 deletions cpp/include/cudf/interop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ class arrow_column {
*
* @param stream CUDA stream used for device memory operations and kernel launches
* @param mr Device memory resource used for any allocations during conversion
* @return unique_column_view_t containing a view of the column data
* @return A view of the column data
*/
[[nodiscard]] column_view view() const;

Expand Down Expand Up @@ -434,7 +434,7 @@ class arrow_table {
*
* @param stream CUDA stream used for device memory operations and kernel launches
* @param mr Device memory resource used for any allocations during conversion
* @return unique_table_view_t containing a view of the table data
* @return A view of the table data
*/
[[nodiscard]] table_view view() const;

Expand Down
5 changes: 4 additions & 1 deletion cpp/src/column/column_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ template <typename ColumnView>
void prefetch_col_data(ColumnView& col, void const* data_ptr, std::string_view key) noexcept
{
if (cudf::experimental::prefetch::detail::prefetch_config::instance().get(key)) {
if (cudf::is_fixed_width(col.type())) {
if (col.type().id() == cudf::type_id::EMPTY) {
// Skip prefetching for empty columns
return;
} else if (cudf::is_fixed_width(col.type())) {
cudf::experimental::prefetch::detail::prefetch_noexcept(
key, data_ptr, col.size() * size_of(col.type()), cudf::get_default_stream());
} else if (col.type().id() == type_id::STRING) {
Expand Down
7 changes: 4 additions & 3 deletions cpp/src/interop/from_arrow_device.cu
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ dispatch_tuple_t dispatch_from_arrow_device::operator()<cudf::list_view>(
size_type const offset = input->offset;
size_type const null_count = input->null_count;
auto offsets_view = column_view{data_type(type_id::INT32),
offset + num_rows + 1,
(num_rows == 0) ? 0 : (offset + num_rows + 1),
input->buffers[fixed_width_data_buffer_idx],
nullptr,
0,
Expand All @@ -300,8 +300,9 @@ dispatch_tuple_t dispatch_from_arrow_device::operator()<cudf::list_view>(
// in the scenario where we were sliced and there are more elements in the child_view
// than can be referenced by the sliced offsets, we need to slice the child_view
// so that when `get_sliced_child` is called, we still produce the right result
auto max_child_offset = cudf::detail::get_value<int32_t>(offsets_view, offset + num_rows, stream);
child_view = cudf::slice(child_view, {0, max_child_offset}, stream).front();
auto max_child_offset =
num_rows == 0 ? 0 : cudf::detail::get_value<int32_t>(offsets_view, offset + num_rows, stream);
child_view = cudf::slice(child_view, {0, max_child_offset}, stream).front();

return std::make_tuple<column_view, owned_columns_t>(
{type,
Expand Down
70 changes: 47 additions & 23 deletions cpp/src/interop/to_arrow_device.cu
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ int set_buffer(std::unique_ptr<T> device_buf, int64_t i, ArrowArray* out)
return NANOARROW_OK;
}

int set_null_mask(column::contents& contents, ArrowArray* out)
{
if (contents.null_mask) {
NANOARROW_RETURN_NOT_OK(set_buffer(std::move(contents.null_mask), validity_buffer_idx, out));
}
return NANOARROW_OK;
}

int set_contents(column::contents& contents, ArrowArray* out)
{
NANOARROW_RETURN_NOT_OK(set_null_mask(contents, out));
NANOARROW_RETURN_NOT_OK(set_buffer(std::move(contents.data), fixed_width_data_buffer_idx, out));
return NANOARROW_OK;
}

struct dispatch_to_arrow_device {
template <typename T,
CUDF_ENABLE_IF(not is_rep_layout_compatible<T>() and not is_fixed_point<T>())>
Expand All @@ -117,23 +132,16 @@ struct dispatch_to_arrow_device {
ArrowArrayMove(tmp.get(), out);
return NANOARROW_OK;
}

int set_null_mask(column::contents& contents, ArrowArray* out)
{
if (contents.null_mask) {
NANOARROW_RETURN_NOT_OK(set_buffer(std::move(contents.null_mask), validity_buffer_idx, out));
}
return NANOARROW_OK;
}

int set_contents(column::contents& contents, ArrowArray* out)
{
NANOARROW_RETURN_NOT_OK(set_null_mask(contents, out));
NANOARROW_RETURN_NOT_OK(set_buffer(std::move(contents.data), fixed_width_data_buffer_idx, out));
return NANOARROW_OK;
}
};

int handle_empty_type_column(ArrowArray* array, cudf::column& column)
{
NANOARROW_RETURN_NOT_OK(initialize_array(array, NANOARROW_TYPE_NA, column.view()));
auto child_contents = column.release();
NANOARROW_RETURN_NOT_OK(set_contents(child_contents, array));
return NANOARROW_OK;
}

template <>
int dispatch_to_arrow_device::operator()<bool>(cudf::column&& column,
rmm::cuda_stream_view stream,
Expand Down Expand Up @@ -227,8 +235,12 @@ int dispatch_to_arrow_device::operator()<cudf::struct_view>(cudf::column&& colum
for (size_t i = 0; i < size_t(tmp->n_children); ++i) {
ArrowArray* child_ptr = tmp->children[i];
auto& child = contents.children[i];
NANOARROW_RETURN_NOT_OK(cudf::type_dispatcher(
child->type(), dispatch_to_arrow_device{}, std::move(*child), stream, mr, child_ptr));
if (child->type().id() == cudf::type_id::EMPTY) {
Comment thread
vyasr marked this conversation as resolved.
NANOARROW_RETURN_NOT_OK(handle_empty_type_column(child_ptr, *child));
} else {
NANOARROW_RETURN_NOT_OK(cudf::type_dispatcher(
child->type(), dispatch_to_arrow_device{}, std::move(*child), stream, mr, child_ptr));

@kingcrimsontianyu kingcrimsontianyu Apr 14, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit paranoid on this: Do we have a guarantee that the initialization param_2 = std::move(*child) happens before param_0 = child->type()? If not, we may rely on the compiler's unspecified behavior for correctness.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cudf::type_dispatcher passes std::move(*child) with a forwarding reference, so the move has not happened yet when the function is entered, whereas param_0 = child->type() is sequenced before the function is entered. So perhaps it is fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that we are safe because while we may not be guaranteed anything about the order in which the two parameters are evaluated, we are guaranteed that all of the parameters are evaluated before the function call begins. std::move doesn't actually do anything, it's just a cast to an rvalue that then allows the object to be modified. Therefore, even if we pass an rvalue ref to *child into the type_dispatcher (which forwards it along to the dispatch_to_arrow_device functor), we are guaranteed that param1 = child->type() is evaluated before *child is actually modified in any way that could evaluate it.

}
}

ArrowArrayMove(tmp.get(), out);
Expand All @@ -253,8 +265,12 @@ int dispatch_to_arrow_device::operator()<cudf::list_view>(cudf::column&& column,
NANOARROW_RETURN_NOT_OK(set_buffer(std::move(offsets_contents.data), 1, tmp.get()));

auto& child = contents.children[cudf::lists_column_view::child_column_index];
NANOARROW_RETURN_NOT_OK(cudf::type_dispatcher(
child->type(), dispatch_to_arrow_device{}, std::move(*child), stream, mr, tmp->children[0]));
if (child->type().id() == cudf::type_id::EMPTY) {
NANOARROW_RETURN_NOT_OK(handle_empty_type_column(tmp->children[0], *child));
} else {
NANOARROW_RETURN_NOT_OK(cudf::type_dispatcher(
child->type(), dispatch_to_arrow_device{}, std::move(*child), stream, mr, tmp->children[0]));
Comment thread
vyasr marked this conversation as resolved.
}

ArrowArrayMove(tmp.get(), out);
return NANOARROW_OK;
Expand Down Expand Up @@ -535,8 +551,12 @@ unique_device_array_t to_arrow_device(cudf::table&& table,
for (size_t i = 0; i < cols.size(); ++i) {
auto child = tmp->children[i];
auto col = cols[i].get();
NANOARROW_THROW_NOT_OK(cudf::type_dispatcher(
col->type(), detail::dispatch_to_arrow_device{}, std::move(*col), stream, mr, child));
if (col->type().id() == cudf::type_id::EMPTY) {
NANOARROW_THROW_NOT_OK(handle_empty_type_column(child, *col));
} else {
NANOARROW_THROW_NOT_OK(cudf::type_dispatcher(
col->type(), detail::dispatch_to_arrow_device{}, std::move(*col), stream, mr, child));
Comment thread
vyasr marked this conversation as resolved.
}
}

return create_device_array(std::move(tmp), stream);
Expand All @@ -548,8 +568,12 @@ unique_device_array_t to_arrow_device(cudf::column&& col,
{
nanoarrow::UniqueArray tmp;

NANOARROW_THROW_NOT_OK(cudf::type_dispatcher(
col.type(), detail::dispatch_to_arrow_device{}, std::move(col), stream, mr, tmp.get()));
if (col.type().id() == cudf::type_id::EMPTY) {
NANOARROW_THROW_NOT_OK(handle_empty_type_column(tmp.get(), col));
} else {
NANOARROW_THROW_NOT_OK(cudf::type_dispatcher(
col.type(), detail::dispatch_to_arrow_device{}, std::move(col), stream, mr, tmp.get()));
Comment thread
vyasr marked this conversation as resolved.
}

return create_device_array(std::move(tmp), stream);
}
Expand Down
19 changes: 15 additions & 4 deletions cpp/src/interop/to_arrow_schema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,12 @@ int dispatch_to_arrow_type::operator()<cudf::struct_view>(column_view input,

child->flags = col.has_nulls() ? ARROW_FLAG_NULLABLE : 0;

NANOARROW_RETURN_NOT_OK(cudf::type_dispatcher(
col.type(), detail::dispatch_to_arrow_type{}, col, metadata.children_meta[i], child));
if (col.type().id() == cudf::type_id::EMPTY) {
NANOARROW_RETURN_NOT_OK(ArrowSchemaSetType(out->children[i], NANOARROW_TYPE_NA));
} else {
NANOARROW_RETURN_NOT_OK(cudf::type_dispatcher(
col.type(), detail::dispatch_to_arrow_type{}, col, metadata.children_meta[i], child));
}
}

return NANOARROW_OK;
Expand All @@ -174,6 +178,9 @@ int dispatch_to_arrow_type::operator()<cudf::list_view>(column_view input,
out->flags = input.has_nulls() ? ARROW_FLAG_NULLABLE : 0;
NANOARROW_RETURN_NOT_OK(ArrowSchemaSetName(out->children[0], child_meta.name.c_str()));
out->children[0]->flags = child.has_nulls() ? ARROW_FLAG_NULLABLE : 0;
if (child.type().id() == cudf::type_id::EMPTY) {
return ArrowSchemaSetType(out->children[0], NANOARROW_TYPE_NA);
}
return cudf::type_dispatcher(
child.type(), detail::dispatch_to_arrow_type{}, child, child_meta, out->children[0]);
}
Expand Down Expand Up @@ -219,8 +226,12 @@ unique_schema_t to_arrow_schema(cudf::table_view const& input,
NANOARROW_THROW_NOT_OK(ArrowSchemaSetName(child, metadata[i].name.c_str()));
child->flags = col.has_nulls() ? ARROW_FLAG_NULLABLE : 0;

NANOARROW_THROW_NOT_OK(
cudf::type_dispatcher(col.type(), detail::dispatch_to_arrow_type{}, col, metadata[i], child));
if (col.type().id() == cudf::type_id::EMPTY) {
NANOARROW_THROW_NOT_OK(ArrowSchemaSetType(child, NANOARROW_TYPE_NA));
} else {
NANOARROW_THROW_NOT_OK(cudf::type_dispatcher(
col.type(), detail::dispatch_to_arrow_type{}, col, metadata[i], child));
}
}

unique_schema_t out(new ArrowSchema, [](ArrowSchema* schema) {
Expand Down
9 changes: 8 additions & 1 deletion dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,14 @@ dependencies:
- fastavro>=0.22.9
- hypothesis
- mmh3
- pytest-benchmark
# Version 5.1 is incompatible with pytest<8.2.
# https://github.com/ionelmc/pytest-benchmark/commit/8dfeeeca8a5c640a4dc4455904ace4f97f62e655
# Remove upper bound when we unbound pytest
# https://github.com/rapidsai/build-planning/issues/105
# or when a fixed version of pytest-benchmark 5.1 with the necessary bounds is released
# https://github.com/conda-forge/pytest-benchmark-feedstock/pull/27
# https://github.com/conda-forge/conda-forge-repodata-patches-feedstock/pull/990
- pytest-benchmark<5.1.0
- pytest-cases>=3.8.2
- scipy
- zstandard
Expand Down
9 changes: 8 additions & 1 deletion python/cudf/benchmarks/API/bench_dataframe.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
# Copyright (c) 2022-2024, NVIDIA CORPORATION.
# Copyright (c) 2022-2025, NVIDIA CORPORATION.

"""Benchmarks of DataFrame methods."""

import string

import numba.cuda
import numpy
import pyarrow as pa
import pytest
import pytest_cases
from config import cudf, cupy
from utils import benchmark_with_object


@pytest.mark.parametrize("N", [100, 1_000_000, 100_000_000])
def bench_from_arrow(benchmark, N):
rng = numpy.random.default_rng(seed=10)
benchmark(cudf.DataFrame, {None: pa.array(rng.random(N))})


@pytest.mark.parametrize("N", [100, 1_000_000])
def bench_construction(benchmark, N):
benchmark(cudf.DataFrame, {None: cupy.random.rand(N)})
Expand Down
4 changes: 2 additions & 2 deletions python/cudf/cudf/core/column/column.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ def null_count(self) -> int:
else:
with acquire_spill_lock():
self._null_count = plc.null_mask.null_count(
self.base_mask.get_ptr(mode="read"), # type: ignore[union-attr]
plc.gpumemoryview(self.base_mask), # type: ignore[union-attr]
self.offset,
self.offset + self.size,
)
Expand Down Expand Up @@ -750,7 +750,7 @@ def to_arrow(self) -> pa.Array:
4
]
"""
return plc.interop.to_arrow(self.to_pylibcudf(mode="read")).chunk(0)
return plc.interop.to_arrow(self.to_pylibcudf(mode="read"))

@classmethod
def from_arrow(cls, array: pa.Array) -> ColumnBase:
Expand Down
2 changes: 1 addition & 1 deletion python/cudf/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ test = [
"hypothesis",
"mmh3",
"msgpack",
"pytest-benchmark",
"pytest-benchmark<5.1.0",
"pytest-cases>=3.8.2",
"pytest-cov",
"pytest-rerunfailures",
Expand Down
9 changes: 6 additions & 3 deletions python/pylibcudf/pylibcudf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(cython_sources
groupby.pyx
hashing.pyx
interop.pyx
_interop_helpers.pyx
jit.pyx
join.pyx
json.pyx
Expand Down Expand Up @@ -61,11 +62,13 @@ rapids_cython_create_modules(
LINKED_LIBRARIES "${linked_libraries}" MODULE_PREFIX pylibcudf_ ASSOCIATED_TARGETS cudf
)

target_include_directories(pylibcudf_interop PUBLIC "$<BUILD_INTERFACE:${DLPACK_INCLUDE_DIR}>")

include(${rapids-cmake-dir}/export/find_package_root.cmake)
include(../../../cpp/cmake/thirdparty/get_nanoarrow.cmake)
target_link_libraries(pylibcudf_interop PUBLIC nanoarrow)

foreach(source interop _interop_helpers table column)
target_include_directories(pylibcudf_${source} PUBLIC "$<BUILD_INTERFACE:${DLPACK_INCLUDE_DIR}>")
target_link_libraries(pylibcudf_${source} PUBLIC nanoarrow)
endforeach()

add_subdirectory(libcudf)
add_subdirectory(strings)
Expand Down
9 changes: 9 additions & 0 deletions python/pylibcudf/pylibcudf/_interop_helpers.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2025, NVIDIA CORPORATION.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think were missing type stubs _interop_helpers.pyi. Is that intentional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because this file pretty much exclusively exposes pure Cython functions. There is nothing visible to Python except the ColumnMetadata type, but that is (for the moment) primarily publicly exposed via the interop module that already has the type stubs for that.


from pylibcudf.libcudf.interop cimport column_metadata

cdef void _release_schema(object schema_capsule) noexcept

cdef void _release_array(object array_capsule) noexcept

cdef column_metadata _metadata_to_libcudf(metadata)
Loading