Skip to content

Commit 56bfcfd

Browse files
committed
Add CUDA Arrow device schema export
Signed-off-by: Alexander Droste <alexander.droste@protonmail.com>
1 parent bed0d4d commit 56bfcfd

8 files changed

Lines changed: 278 additions & 24 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ jobs:
270270
run: |
271271
git ls-files vortex-cuda vortex-cxx vortex-duckdb vortex-ffi \
272272
| grep -E '\.(cpp|hpp|cu|cuh|h)$' \
273-
| grep -v 'arrow/reference/abi\.h$' \
273+
| grep -v 'arrow/reference/arrow_c_device\.h$' \
274274
| grep -v 'kernels/src/bit_unpack_.*\.cu$' \
275275
| grep -v 'kernels/src/bit_unpack_.*_lanes\.cuh$' \
276276
| xargs clang-format --dry-run --Werror --style=file

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vortex-cuda/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ unstable_encodings = ["vortex/unstable_encodings"]
2424

2525
[dependencies]
2626
arc-swap = { workspace = true }
27+
arrow-schema = { workspace = true, features = ["ffi"] }
2728
async-trait = { workspace = true }
2829
bytes = { workspace = true }
2930
cudarc = { workspace = true, features = ["f16"] }

vortex-cuda/build.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn main() {
5656
generate_unpack::<u64>(&kernels_src, 16).expect("Failed to generate unpack for u64");
5757

5858
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
59-
generate_arrow_c_abi_bindings(Path::new(&manifest_dir), &out_dir);
59+
generate_arrow_device_array_bindings(Path::new(&manifest_dir), &out_dir);
6060
generate_dynamic_dispatch_bindings(&kernels_src, &out_dir);
6161
generate_patches_bindings(&kernels_src, &out_dir);
6262

@@ -197,9 +197,9 @@ fn nvcc_compile_ptx(
197197
Ok(())
198198
}
199199

200-
/// Generate bindings for the vendored Arrow C ABI header.
201-
fn generate_arrow_c_abi_bindings(manifest_dir: &Path, out_dir: &Path) {
202-
let header = manifest_dir.join("src/arrow/reference/abi.h");
200+
/// Generate bindings for the vendored Arrow C Device ABI header.
201+
fn generate_arrow_device_array_bindings(manifest_dir: &Path, out_dir: &Path) {
202+
let header = manifest_dir.join("src/arrow/reference/arrow_c_device.h");
203203
println!("cargo:rerun-if-changed={}", header.display());
204204

205205
let bindings = bindgen::Builder::default()
@@ -212,7 +212,7 @@ fn generate_arrow_c_abi_bindings(manifest_dir: &Path, out_dir: &Path) {
212212
.derive_debug(true)
213213
.layout_tests(false)
214214
.generate()
215-
.expect("Failed to generate Arrow C ABI bindings");
215+
.expect("Failed to generate Arrow C Device bindings");
216216

217217
bindings
218218
.write_to_file(out_dir.join("arrow_c_abi.rs"))

vortex-cuda/src/arrow/canonical.rs

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@ impl ExportDeviceArray for CanonicalDeviceArrayExport {
4949
) -> VortexResult<ArrowDeviceArray> {
5050
let cuda_array = array.execute_cuda(ctx).await?;
5151

52-
let (arrow_array, _) = export_canonical(cuda_array, ctx).await?;
52+
let (arrow_array, sync_event) = export_canonical(cuda_array, ctx).await?;
5353

5454
Ok(ArrowDeviceArray {
5555
array: arrow_array,
5656
device_id: ctx.stream().context().ordinal() as i64,
5757
device_type: ARROW_DEVICE_CUDA,
58-
sync_event: ptr::null_mut(),
58+
sync_event,
5959
reserved: Default::default(),
6060
})
6161
}
@@ -232,8 +232,8 @@ fn export_fixed_size(
232232
"buffer must already be copied to device before calling"
233233
);
234234

235-
// TODO(aduffy): currently the null buffer is always None, in the future we will need
236-
// to pass it.
235+
// Non-trivial validity is rejected before fixed-size export, so the Arrow null bitmap slot is
236+
// always null for now. Future nullable export support should pass the validity bitmap here.
237237
let mut private_data = PrivateData::new(vec![None, Some(buffer)], vec![], ctx)?;
238238
let sync_event: SyncEvent = private_data.sync_event();
239239

@@ -271,7 +271,9 @@ unsafe extern "C" fn release_array(array: *mut ArrowArray) {
271271
let children = mem::take(&mut private_data.children);
272272
for child in children {
273273
if !child.is_null() {
274-
release_array(child);
274+
if let Some(release) = (*child).release {
275+
release(child);
276+
}
275277
// Children are allocated with Box::into_raw in PrivateData::new, so the
276278
// release callback must also reclaim the ArrowArray allocation itself.
277279
drop(Box::from_raw(child));
@@ -286,6 +288,9 @@ unsafe extern "C" fn release_array(array: *mut ArrowArray) {
286288

287289
#[cfg(test)]
288290
mod tests {
291+
use arrow_schema::DataType;
292+
use arrow_schema::Field;
293+
use arrow_schema::Schema;
289294
use rstest::rstest;
290295
use vortex::array::ArrayRef;
291296
use vortex::array::IntoArray;
@@ -479,4 +484,79 @@ mod tests {
479484
unsafe { release_array(&raw mut device_array.array) };
480485
Ok(())
481486
}
487+
488+
#[crate::test]
489+
async fn test_export_struct_with_schema() -> VortexResult<()> {
490+
let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty())
491+
.vortex_expect("failed to create execution context");
492+
493+
let array = StructArray::new(
494+
FieldNames::from_iter(["a", "b", "c"]),
495+
vec![
496+
PrimitiveArray::from_iter(0u32..5).into_array(),
497+
PrimitiveArray::from_iter(0i64..5).into_array(),
498+
VarBinViewArray::from_iter_str(["one", "two", "three", "four", "five"])
499+
.into_array(),
500+
],
501+
5,
502+
Validity::NonNullable,
503+
)
504+
.into_array();
505+
let mut exported = array.export_device_array_with_schema(&mut ctx).await?;
506+
507+
let schema = Schema::try_from(&exported.schema)?;
508+
assert_eq!(
509+
schema,
510+
Schema::new(vec![
511+
Field::new("a", DataType::UInt32, false),
512+
Field::new("b", DataType::Int64, false),
513+
Field::new("c", DataType::Utf8, false),
514+
])
515+
);
516+
assert_eq!(exported.array.array.length, 5);
517+
assert_eq!(exported.array.array.n_buffers, 1);
518+
assert_eq!(exported.array.array.n_children, 3);
519+
assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA);
520+
521+
unsafe { release_array(&raw mut exported.array.array) };
522+
Ok(())
523+
}
524+
525+
#[crate::test]
526+
async fn test_export_primitive_with_schema_is_column_shaped() -> VortexResult<()> {
527+
let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty())
528+
.vortex_expect("failed to create execution context");
529+
530+
let array = PrimitiveArray::from_iter(0u32..5).into_array();
531+
let mut exported = array.export_device_array_with_schema(&mut ctx).await?;
532+
533+
let field = Field::try_from(&exported.schema)?;
534+
assert_eq!(field, Field::new("", DataType::UInt32, false));
535+
assert_eq!(exported.array.array.length, 5);
536+
assert_eq!(exported.array.array.n_buffers, 2);
537+
assert_eq!(exported.array.array.n_children, 0);
538+
assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA);
539+
540+
unsafe { release_array(&raw mut exported.array.array) };
541+
Ok(())
542+
}
543+
544+
#[crate::test]
545+
async fn test_export_varbinview_with_schema_uses_utf8_layout() -> VortexResult<()> {
546+
let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty())
547+
.vortex_expect("failed to create execution context");
548+
549+
let array = VarBinViewArray::from_iter_str(["one", "two", "three"]).into_array();
550+
let mut exported = array.export_device_array_with_schema(&mut ctx).await?;
551+
552+
let field = Field::try_from(&exported.schema)?;
553+
assert_eq!(field, Field::new("", DataType::Utf8, false));
554+
assert_eq!(exported.array.array.length, 3);
555+
assert_eq!(exported.array.array.n_buffers, 3);
556+
assert_eq!(exported.array.array.n_children, 0);
557+
assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA);
558+
559+
unsafe { release_array(&raw mut exported.array.array) };
560+
Ok(())
561+
}
482562
}

0 commit comments

Comments
 (0)