This is an automated email from the ASF dual-hosted git repository. github-bot pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/arrow-nanoarrow.git
The following commit(s) were added to refs/heads/main by this push: new ec81297 Update dist/ for commit ae5001937a39f3f8227d1468dedfa7144f87ced1 ec81297 is described below commit ec81297d1745e33fa64c5b69cd7e74ab084affac Author: GitHub Actions <acti...@github.com> AuthorDate: Fri Jun 23 01:33:34 2023 +0000 Update dist/ for commit ae5001937a39f3f8227d1468dedfa7144f87ced1 --- dist/nanoarrow.c | 13 ++ dist/nanoarrow.h | 16 +- dist/nanoarrow_device.c | 491 ++++++++++++++++++++++++++++++++++++++++++++++++ dist/nanoarrow_device.h | 334 ++++++++++++++++++++++++++++++++ 4 files changed, 852 insertions(+), 2 deletions(-) diff --git a/dist/nanoarrow.c b/dist/nanoarrow.c index 4ba74d9..ab3e337 100644 --- a/dist/nanoarrow.c +++ b/dist/nanoarrow.c @@ -2779,6 +2779,19 @@ ArrowErrorCode ArrowArrayViewSetArray(struct ArrowArrayView* array_view, return NANOARROW_OK; } +ArrowErrorCode ArrowArrayViewSetArrayMinimal(struct ArrowArrayView* array_view, + struct ArrowArray* array, + struct ArrowError* error) { + // Extract information from the array into the array view + NANOARROW_RETURN_NOT_OK(ArrowArrayViewSetArrayInternal(array_view, array, error)); + + // Run default validation. Because we've marked all non-NULL buffers as having unknown + // size, validation will also update the buffer sizes as it goes. + NANOARROW_RETURN_NOT_OK(ArrowArrayViewValidateMinimal(array_view, error)); + + return NANOARROW_OK; +} + static int ArrowAssertIncreasingInt32(struct ArrowBufferView view, struct ArrowError* error) { if (view.size_bytes <= (int64_t)sizeof(int32_t)) { diff --git a/dist/nanoarrow.h b/dist/nanoarrow.h index 05dcc19..5fa96f0 100644 --- a/dist/nanoarrow.h +++ b/dist/nanoarrow.h @@ -19,9 +19,9 @@ #define NANOARROW_BUILD_ID_H_INCLUDED #define NANOARROW_VERSION_MAJOR 0 -#define NANOARROW_VERSION_MINOR 2 +#define NANOARROW_VERSION_MINOR 3 #define NANOARROW_VERSION_PATCH 0 -#define NANOARROW_VERSION "0.2.0-SNAPSHOT" +#define NANOARROW_VERSION "0.3.0-SNAPSHOT" #define NANOARROW_VERSION_INT \ (NANOARROW_VERSION_MAJOR * 10000 + NANOARROW_VERSION_MINOR * 100 + \ @@ -873,6 +873,8 @@ static inline void ArrowDecimalSetBytes(struct ArrowDecimal* decimal, NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayInitFromSchema) #define ArrowArrayInitFromArrayView \ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayInitFromArrayView) +#define ArrowArrayInitFromArrayView \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayInitFromArrayView) #define ArrowArrayAllocateChildren \ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayAllocateChildren) #define ArrowArrayAllocateDictionary \ @@ -897,6 +899,8 @@ static inline void ArrowDecimalSetBytes(struct ArrowDecimal* decimal, NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayViewSetLength) #define ArrowArrayViewSetArray \ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayViewSetArray) +#define ArrowArrayViewSetArrayMinimal \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayViewSetArrayMinimal) #define ArrowArrayViewValidate \ NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayViewValidate) #define ArrowArrayViewReset NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowArrayViewReset) @@ -1736,6 +1740,12 @@ void ArrowArrayViewSetLength(struct ArrowArrayView* array_view, int64_t length); ArrowErrorCode ArrowArrayViewSetArray(struct ArrowArrayView* array_view, struct ArrowArray* array, struct ArrowError* error); +/// \brief Set buffer sizes and data pointers from an ArrowArray except for those +/// that require dereferencing buffer content. +ArrowErrorCode ArrowArrayViewSetArrayMinimal(struct ArrowArrayView* array_view, + struct ArrowArray* array, + struct ArrowError* error); + /// \brief Performs checks on the content of an ArrowArrayView /// /// If using ArrowArrayViewSetArray() to back array_view with an ArrowArray, @@ -2873,6 +2883,8 @@ static inline ArrowErrorCode ArrowArrayAppendString(struct ArrowArray* array, switch (private_data->storage_type) { case NANOARROW_TYPE_STRING: case NANOARROW_TYPE_LARGE_STRING: + case NANOARROW_TYPE_BINARY: + case NANOARROW_TYPE_LARGE_BINARY: return ArrowArrayAppendBytes(array, buffer_view); default: return EINVAL; diff --git a/dist/nanoarrow_device.c b/dist/nanoarrow_device.c new file mode 100644 index 0000000..4be7a93 --- /dev/null +++ b/dist/nanoarrow_device.c @@ -0,0 +1,491 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#include <errno.h> + +#include "nanoarrow.h" + +#include "nanoarrow_device.h" + +ArrowErrorCode ArrowDeviceCheckRuntime(struct ArrowError* error) { + const char* nanoarrow_runtime_version = ArrowNanoarrowVersion(); + const char* nanoarrow_ipc_build_time_version = NANOARROW_VERSION; + + if (strcmp(nanoarrow_runtime_version, nanoarrow_ipc_build_time_version) != 0) { + ArrowErrorSet(error, "Expected nanoarrow runtime version '%s' but found version '%s'", + nanoarrow_ipc_build_time_version, nanoarrow_runtime_version); + return EINVAL; + } + + return NANOARROW_OK; +} + +static void ArrowDeviceArrayInitDefault(struct ArrowDevice* device, + struct ArrowDeviceArray* device_array, + struct ArrowArray* array) { + memset(device_array, 0, sizeof(struct ArrowDeviceArray)); + device_array->device_type = device->device_type; + device_array->device_id = device->device_id; + ArrowArrayMove(array, &device_array->array); +} + +static ArrowErrorCode ArrowDeviceCpuBufferInit(struct ArrowDevice* device_src, + struct ArrowBufferView src, + struct ArrowDevice* device_dst, + struct ArrowBuffer* dst) { + if (device_dst->device_type != ARROW_DEVICE_CPU || + device_src->device_type != ARROW_DEVICE_CPU) { + return ENOTSUP; + } + + ArrowBufferInit(dst); + dst->allocator = ArrowBufferAllocatorDefault(); + NANOARROW_RETURN_NOT_OK(ArrowBufferAppend(dst, src.data.as_uint8, src.size_bytes)); + return NANOARROW_OK; +} + +static ArrowErrorCode ArrowDeviceCpuBufferMove(struct ArrowDevice* device_src, + struct ArrowBuffer* src, + struct ArrowDevice* device_dst, + struct ArrowBuffer* dst) { + if (device_dst->device_type != ARROW_DEVICE_CPU || + device_src->device_type != ARROW_DEVICE_CPU) { + return ENOTSUP; + } + + ArrowBufferMove(src, dst); + return NANOARROW_OK; +} + +static ArrowErrorCode ArrowDeviceCpuBufferCopy(struct ArrowDevice* device_src, + struct ArrowBufferView src, + struct ArrowDevice* device_dst, + struct ArrowBufferView dst) { + if (device_dst->device_type != ARROW_DEVICE_CPU || + device_src->device_type != ARROW_DEVICE_CPU) { + return ENOTSUP; + } + + memcpy((uint8_t*)dst.data.as_uint8, src.data.as_uint8, dst.size_bytes); + return NANOARROW_OK; +} + +static ArrowErrorCode ArrowDeviceCpuSynchronize(struct ArrowDevice* device, + void* sync_event, + struct ArrowError* error) { + switch (device->device_type) { + case ARROW_DEVICE_CPU: + if (sync_event != NULL) { + ArrowErrorSet(error, "Expected NULL sync_event for ARROW_DEVICE_CPU but got %p", + sync_event); + return EINVAL; + } else { + return NANOARROW_OK; + } + default: + return device->synchronize_event(device, sync_event, error); + } +} + +static void ArrowDeviceCpuRelease(struct ArrowDevice* device) { device->release = NULL; } + +struct ArrowDevice* ArrowDeviceCpu(void) { + static struct ArrowDevice* cpu_device_singleton = NULL; + if (cpu_device_singleton == NULL) { + cpu_device_singleton = (struct ArrowDevice*)ArrowMalloc(sizeof(struct ArrowDevice)); + ArrowDeviceInitCpu(cpu_device_singleton); + } + + return cpu_device_singleton; +} + +void ArrowDeviceInitCpu(struct ArrowDevice* device) { + device->device_type = ARROW_DEVICE_CPU; + device->device_id = 0; + device->array_init = NULL; + device->array_move = NULL; + device->buffer_init = &ArrowDeviceCpuBufferInit; + device->buffer_move = &ArrowDeviceCpuBufferMove; + device->buffer_copy = &ArrowDeviceCpuBufferCopy; + device->synchronize_event = &ArrowDeviceCpuSynchronize; + device->release = &ArrowDeviceCpuRelease; + device->private_data = NULL; +} + +#ifdef NANOARROW_DEVICE_WITH_METAL +struct ArrowDevice* ArrowDeviceMetalDefaultDevice(void); +#endif + +#ifdef NANOARROW_DEVICE_WITH_CUDA +struct ArrowDevice* ArrowDeviceCuda(ArrowDeviceType device_type, int64_t device_id); +#endif + +struct ArrowDevice* ArrowDeviceResolve(ArrowDeviceType device_type, int64_t device_id) { + if (device_type == ARROW_DEVICE_CPU && device_id == 0) { + return ArrowDeviceCpu(); + } + +#ifdef NANOARROW_DEVICE_WITH_METAL + if (device_type == ARROW_DEVICE_METAL) { + struct ArrowDevice* default_device = ArrowDeviceMetalDefaultDevice(); + if (device_id == default_device->device_id) { + return default_device; + } + } +#endif + +#ifdef NANOARROW_DEVICE_WITH_CUDA + if (device_type == ARROW_DEVICE_CUDA || device_type == ARROW_DEVICE_CUDA_HOST) { + return ArrowDeviceCuda(device_type, device_id); + } +#endif + + return NULL; +} + +ArrowErrorCode ArrowDeviceArrayInit(struct ArrowDevice* device, + struct ArrowDeviceArray* device_array, + struct ArrowArray* array) { + if (device->array_init != NULL) { + return device->array_init(device, device_array, array); + } else { + ArrowDeviceArrayInitDefault(device, device_array, array); + return NANOARROW_OK; + } +} + +ArrowErrorCode ArrowDeviceBufferInit(struct ArrowDevice* device_src, + struct ArrowBufferView src, + struct ArrowDevice* device_dst, + struct ArrowBuffer* dst) { + int result = device_dst->buffer_init(device_src, src, device_dst, dst); + if (result == ENOTSUP) { + result = device_src->buffer_init(device_src, src, device_dst, dst); + } + + return result; +} + +ArrowErrorCode ArrowDeviceBufferMove(struct ArrowDevice* device_src, + struct ArrowBuffer* src, + struct ArrowDevice* device_dst, + struct ArrowBuffer* dst) { + int result = device_dst->buffer_move(device_src, src, device_dst, dst); + if (result == ENOTSUP) { + result = device_src->buffer_move(device_src, src, device_dst, dst); + } + + return result; +} + +ArrowErrorCode ArrowDeviceBufferCopy(struct ArrowDevice* device_src, + struct ArrowBufferView src, + struct ArrowDevice* device_dst, + struct ArrowBufferView dst) { + int result = device_dst->buffer_copy(device_src, src, device_dst, dst); + if (result == ENOTSUP) { + result = device_src->buffer_copy(device_src, src, device_dst, dst); + } + + return result; +} + +struct ArrowBasicDeviceArrayStreamPrivate { + struct ArrowDevice* device; + struct ArrowArrayStream naive_stream; +}; + +static int ArrowDeviceBasicArrayStreamGetSchema( + struct ArrowDeviceArrayStream* array_stream, struct ArrowSchema* schema) { + struct ArrowBasicDeviceArrayStreamPrivate* private_data = + (struct ArrowBasicDeviceArrayStreamPrivate*)array_stream->private_data; + return private_data->naive_stream.get_schema(&private_data->naive_stream, schema); +} + +static int ArrowDeviceBasicArrayStreamGetNext(struct ArrowDeviceArrayStream* array_stream, + struct ArrowDeviceArray* device_array) { + struct ArrowBasicDeviceArrayStreamPrivate* private_data = + (struct ArrowBasicDeviceArrayStreamPrivate*)array_stream->private_data; + + struct ArrowArray tmp; + NANOARROW_RETURN_NOT_OK( + private_data->naive_stream.get_next(&private_data->naive_stream, &tmp)); + int result = ArrowDeviceArrayInit(private_data->device, device_array, &tmp); + if (result != NANOARROW_OK) { + tmp.release(&tmp); + return result; + } + + return NANOARROW_OK; +} + +static const char* ArrowDeviceBasicArrayStreamGetLastError( + struct ArrowDeviceArrayStream* array_stream) { + struct ArrowBasicDeviceArrayStreamPrivate* private_data = + (struct ArrowBasicDeviceArrayStreamPrivate*)array_stream->private_data; + return private_data->naive_stream.get_last_error(&private_data->naive_stream); +} + +static void ArrowDeviceBasicArrayStreamRelease( + struct ArrowDeviceArrayStream* array_stream) { + struct ArrowBasicDeviceArrayStreamPrivate* private_data = + (struct ArrowBasicDeviceArrayStreamPrivate*)array_stream->private_data; + private_data->naive_stream.release(&private_data->naive_stream); + ArrowFree(private_data); + array_stream->release = NULL; +} + +ArrowErrorCode ArrowDeviceBasicArrayStreamInit( + struct ArrowDeviceArrayStream* device_array_stream, + struct ArrowArrayStream* array_stream, struct ArrowDevice* device) { + struct ArrowBasicDeviceArrayStreamPrivate* private_data = + (struct ArrowBasicDeviceArrayStreamPrivate*)ArrowMalloc( + sizeof(struct ArrowBasicDeviceArrayStreamPrivate)); + if (private_data == NULL) { + return ENOMEM; + } + + private_data->device = device; + ArrowArrayStreamMove(array_stream, &private_data->naive_stream); + + device_array_stream->device_type = device->device_type; + device_array_stream->get_schema = &ArrowDeviceBasicArrayStreamGetSchema; + device_array_stream->get_next = &ArrowDeviceBasicArrayStreamGetNext; + device_array_stream->get_last_error = &ArrowDeviceBasicArrayStreamGetLastError; + device_array_stream->release = &ArrowDeviceBasicArrayStreamRelease; + device_array_stream->private_data = private_data; + return NANOARROW_OK; +} + +void ArrowDeviceArrayViewInit(struct ArrowDeviceArrayView* device_array_view) { + memset(device_array_view, 0, sizeof(struct ArrowDeviceArrayView)); +} + +void ArrowDeviceArrayViewReset(struct ArrowDeviceArrayView* device_array_view) { + ArrowArrayViewReset(&device_array_view->array_view); + device_array_view->device = NULL; +} + +static ArrowErrorCode ArrowDeviceBufferGetInt32(struct ArrowDevice* device, + struct ArrowBufferView buffer_view, + int64_t i, int32_t* out) { + struct ArrowBufferView out_view; + out_view.data.as_int32 = out; + out_view.size_bytes = sizeof(int32_t); + + struct ArrowBufferView device_buffer_view; + device_buffer_view.data.as_int32 = buffer_view.data.as_int32 + i; + device_buffer_view.size_bytes = sizeof(int32_t); + NANOARROW_RETURN_NOT_OK( + ArrowDeviceBufferCopy(device, device_buffer_view, ArrowDeviceCpu(), out_view)); + return NANOARROW_OK; +} + +static ArrowErrorCode ArrowDeviceBufferGetInt64(struct ArrowDevice* device, + struct ArrowBufferView buffer_view, + int64_t i, int64_t* out) { + struct ArrowBufferView out_view; + out_view.data.as_int64 = out; + out_view.size_bytes = sizeof(int64_t); + + struct ArrowBufferView device_buffer_view; + device_buffer_view.data.as_int64 = buffer_view.data.as_int64 + i; + device_buffer_view.size_bytes = sizeof(int64_t); + NANOARROW_RETURN_NOT_OK( + ArrowDeviceBufferCopy(device, device_buffer_view, ArrowDeviceCpu(), out_view)); + return NANOARROW_OK; +} + +static ArrowErrorCode ArrowDeviceArrayViewResolveBufferSizes( + struct ArrowDevice* device, struct ArrowArrayView* array_view) { + // Calculate buffer sizes that require accessing the offset buffer + // (at this point all other sizes have been resolved). + int64_t offset_plus_length = array_view->offset + array_view->length; + int32_t last_offset32; + int64_t last_offset64; + + switch (array_view->storage_type) { + case NANOARROW_TYPE_STRING: + case NANOARROW_TYPE_BINARY: + if (array_view->buffer_views[1].size_bytes == 0) { + array_view->buffer_views[2].size_bytes = 0; + } else if (array_view->buffer_views[2].size_bytes == -1) { + NANOARROW_RETURN_NOT_OK(ArrowDeviceBufferGetInt32( + device, array_view->buffer_views[1], offset_plus_length, &last_offset32)); + array_view->buffer_views[2].size_bytes = last_offset32; + } + break; + + case NANOARROW_TYPE_LARGE_STRING: + case NANOARROW_TYPE_LARGE_BINARY: + if (array_view->buffer_views[1].size_bytes == 0) { + array_view->buffer_views[2].size_bytes = 0; + } else if (array_view->buffer_views[2].size_bytes == -1) { + NANOARROW_RETURN_NOT_OK(ArrowDeviceBufferGetInt64( + device, array_view->buffer_views[1], offset_plus_length, &last_offset64)); + array_view->buffer_views[2].size_bytes = last_offset64; + } + break; + default: + break; + } + + // Recurse for children + for (int64_t i = 0; i < array_view->n_children; i++) { + NANOARROW_RETURN_NOT_OK( + ArrowDeviceArrayViewResolveBufferSizes(device, array_view->children[i])); + } + + return NANOARROW_OK; +} + +ArrowErrorCode ArrowDeviceArrayViewSetArrayMinimal( + struct ArrowDeviceArrayView* device_array_view, struct ArrowDeviceArray* device_array, + struct ArrowError* error) { + // Resolve device + struct ArrowDevice* device = + ArrowDeviceResolve(device_array->device_type, device_array->device_id); + if (device == NULL) { + ArrowErrorSet(error, "Can't resolve device with type %d and identifier %ld", + (int)device_array->device_type, (long)device_array->device_id); + return EINVAL; + } + + // Set the device array device + device_array_view->device = device; + + // Populate the array_view + NANOARROW_RETURN_NOT_OK(ArrowArrayViewSetArrayMinimal(&device_array_view->array_view, + &device_array->array, error)); + + return NANOARROW_OK; +} + +ArrowErrorCode ArrowDeviceArrayViewSetArray( + struct ArrowDeviceArrayView* device_array_view, struct ArrowDeviceArray* device_array, + struct ArrowError* error) { + NANOARROW_RETURN_NOT_OK( + ArrowDeviceArrayViewSetArrayMinimal(device_array_view, device_array, error)); + + // Wait on device_array to synchronize with the CPU + // TODO: This is not actually sufficient for CUDA, where the synchronization + // should happen after the cudaMemcpy, not before it. The ordering of + // these operations should be explicit and asynchronous (and is probably outside + // the scope of what can be done with a generic callback). + NANOARROW_RETURN_NOT_OK(device_array_view->device->synchronize_event( + device_array_view->device, device_array->sync_event, error)); + + // Resolve unknown buffer sizes (i.e., string, binary, large string, large binary) + NANOARROW_RETURN_NOT_OK_WITH_ERROR( + ArrowDeviceArrayViewResolveBufferSizes(device_array_view->device, + &device_array_view->array_view), + error); + + return NANOARROW_OK; +} + +static ArrowErrorCode ArrowDeviceArrayViewCopyInternal(struct ArrowDevice* device_src, + struct ArrowArrayView* src, + struct ArrowDevice* device_dst, + struct ArrowArray* dst) { + // Currently no attempt to minimize the amount of memory copied (i.e., + // by applying offset + length and copying potentially fewer bytes) + dst->length = src->length; + dst->offset = src->offset; + dst->null_count = src->null_count; + + for (int i = 0; i < 3; i++) { + if (src->layout.buffer_type[i] == NANOARROW_BUFFER_TYPE_NONE) { + break; + } + + NANOARROW_RETURN_NOT_OK(ArrowDeviceBufferInit(device_src, src->buffer_views[i], + device_dst, ArrowArrayBuffer(dst, i))); + } + + for (int64_t i = 0; i < src->n_children; i++) { + NANOARROW_RETURN_NOT_OK(ArrowDeviceArrayViewCopyInternal( + device_src, src->children[i], device_dst, dst->children[i])); + } + + if (src->dictionary != NULL) { + NANOARROW_RETURN_NOT_OK(ArrowDeviceArrayViewCopyInternal( + device_src, src->dictionary, device_dst, dst->dictionary)); + } + + return NANOARROW_OK; +} + +ArrowErrorCode ArrowDeviceArrayViewCopy(struct ArrowDeviceArrayView* src, + struct ArrowDevice* device_dst, + struct ArrowDeviceArray* dst) { + struct ArrowArray tmp; + NANOARROW_RETURN_NOT_OK(ArrowArrayInitFromArrayView(&tmp, &src->array_view, NULL)); + + int result = + ArrowDeviceArrayViewCopyInternal(src->device, &src->array_view, device_dst, &tmp); + if (result != NANOARROW_OK) { + tmp.release(&tmp); + return result; + } + + result = ArrowArrayFinishBuilding(&tmp, NANOARROW_VALIDATION_LEVEL_MINIMAL, NULL); + if (result != NANOARROW_OK) { + tmp.release(&tmp); + return result; + } + + result = ArrowDeviceArrayInit(device_dst, dst, &tmp); + if (result != NANOARROW_OK) { + tmp.release(&tmp); + return result; + } + + return result; +} + +ArrowErrorCode ArrowDeviceArrayMoveToDevice(struct ArrowDeviceArray* src, + struct ArrowDevice* device_dst, + struct ArrowDeviceArray* dst) { + // Can always move from the same device to the same device + if (src->device_type == device_dst->device_type && + src->device_id == device_dst->device_id) { + ArrowDeviceArrayMove(src, dst); + return NANOARROW_OK; + } + + struct ArrowDevice* device_src = ArrowDeviceResolve(src->device_type, src->device_id); + if (device_src == NULL) { + return EINVAL; + } + + // See if the source knows how to move + int result; + if (device_src->array_move != NULL) { + result = device_src->array_move(device_src, src, device_dst, dst); + if (result != ENOTSUP) { + return result; + } + } + + // See if the destination knows how to move + if (device_dst->array_move != NULL) { + NANOARROW_RETURN_NOT_OK(device_dst->array_move(device_src, src, device_dst, dst)); + } + + return ENOTSUP; +} diff --git a/dist/nanoarrow_device.h b/dist/nanoarrow_device.h new file mode 100644 index 0000000..96a2bae --- /dev/null +++ b/dist/nanoarrow_device.h @@ -0,0 +1,334 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#ifndef NANOARROW_DEVICE_H_INCLUDED +#define NANOARROW_DEVICE_H_INCLUDED + +#include "nanoarrow.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// \defgroup nanoarrow_device-arrow-cdata Arrow C Device interface +/// +/// The Arrow Device and Stream interfaces are part of the +/// Arrow C Device Data and Arrow C Device stream interfaces +/// (https://arrow.apache.org/docs/dev/format/CDeviceDataInterface.html). +/// See the Arrow documentation for detailed documentation of these structures. +/// +/// @{ + +#ifndef ARROW_C_DEVICE_DATA_INTERFACE +#define ARROW_C_DEVICE_DATA_INTERFACE + +// Device type for the allocated memory +typedef int32_t ArrowDeviceType; + +// CPU device, same as using ArrowArray directly +#define ARROW_DEVICE_CPU 1 +// CUDA GPU Device +#define ARROW_DEVICE_CUDA 2 +// Pinned CUDA CPU memory by cudaMallocHost +#define ARROW_DEVICE_CUDA_HOST 3 +// OpenCL Device +#define ARROW_DEVICE_OPENCL 4 +// Vulkan buffer for next-gen graphics +#define ARROW_DEVICE_VULKAN 7 +// Metal for Apple GPU +#define ARROW_DEVICE_METAL 8 +// Verilog simulator buffer +#define ARROW_DEVICE_VPI 9 +// ROCm GPUs for AMD GPUs +#define ARROW_DEVICE_ROCM 10 +// Pinned ROCm CPU memory allocated by hipMallocHost +#define ARROW_DEVICE_ROCM_HOST 11 +// Reserved for extension +// +// used to quickly test extension devices, semantics +// can differ based on implementation +#define ARROW_DEVICE_EXT_DEV 12 +// CUDA managed/unified memory allocated by cudaMallocManaged +#define ARROW_DEVICE_CUDA_MANAGED 13 +// Unified shared memory allocated on a oneAPI +// non-partitioned device. +// +// A call to the oneAPI runtime is required to determine the +// device type, the USM allocation type and the sycl context +// that it is bound to. +#define ARROW_DEVICE_ONEAPI 14 +// GPU support for next-gen WebGPU standard +#define ARROW_DEVICE_WEBGPU 15 +// Qualcomm Hexagon DSP +#define ARROW_DEVICE_HEXAGON 16 + +struct ArrowDeviceArray { + struct ArrowArray array; + int64_t device_id; + ArrowDeviceType device_type; + void* sync_event; + + // reserved bytes for future expansion + int64_t reserved[3]; +}; + +#endif // ARROW_C_DEVICE_DATA_INTERFACE + +#ifndef ARROW_C_DEVICE_STREAM_INTERFACE +#define ARROW_C_DEVICE_STREAM_INTERFACE + +struct ArrowDeviceArrayStream { + // device type that all arrays will be accessible from + ArrowDeviceType device_type; + // callbacks + int (*get_schema)(struct ArrowDeviceArrayStream*, struct ArrowSchema*); + int (*get_next)(struct ArrowDeviceArrayStream*, struct ArrowDeviceArray*); + const char* (*get_last_error)(struct ArrowDeviceArrayStream*); + + // release callback + void (*release)(struct ArrowDeviceArrayStream*); + + // opaque producer-specific data + void* private_data; +}; + +#endif // ARROW_C_DEVICE_STREAM_INTERFACE + +/// \brief Move the contents of src into dst and set src->array.release to NULL +static inline void ArrowDeviceArrayMove(struct ArrowDeviceArray* src, + struct ArrowDeviceArray* dst) { + memcpy(dst, src, sizeof(struct ArrowDeviceArray)); + src->array.release = 0; +} + +/// @} + +#ifdef NANOARROW_NAMESPACE + +#define ArrowDeviceCheckRuntime \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceCheckRuntime) +#define ArrowDeviceArrayInit NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceArrayInit) +#define ArrowDeviceArrayViewInit \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceArrayViewInit) +#define ArrowDeviceArrayViewReset \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceArrayViewReset) +#define ArrowDeviceArrayViewSetArrayMinimal \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceArrayViewSetArrayMinimal) +#define ArrowDeviceArrayViewSetArray \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceArrayViewSetArray) +#define ArrowDeviceArrayViewCopy \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceArrayViewCopy) +#define ArrowDeviceArrayViewCopyRequired \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceArrayViewCopyRequired) +#define ArrowDeviceArrayMoveToDevice \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceArrayMoveToDevice) +#define ArrowDeviceResolve NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceResolve) +#define ArrowDeviceCpu NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceCpu) +#define ArrowDeviceInitCpu NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceInitCpu) +#define ArrowDeviceBufferInit NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceBufferInit) +#define ArrowDeviceBufferMove NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceBufferMove) +#define ArrowDeviceBufferCopy NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceBufferCopy) +#define ArrowDeviceBasicArrayStreamInit \ + NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowDeviceBasicArrayStreamInit) + +#endif + +/// \defgroup nanoarrow_device Nanoarrow Device extension +/// +/// Except where noted, objects are not thread-safe and clients should +/// take care to serialize accesses to methods. +/// +/// @{ + +/// \brief Checks the nanoarrow runtime to make sure the run/build versions match +ArrowErrorCode ArrowDeviceCheckRuntime(struct ArrowError* error); + +/// \brief A Device wrapper with callbacks for basic memory management tasks +/// +/// All device objects are currently implemented as singletons; however, this +/// may change as implementations progress. +struct ArrowDevice { + /// \brief The device type integer identifier (see ArrowDeviceArray) + ArrowDeviceType device_type; + + /// \brief The device identifier (see ArrowDeviceArray) + int64_t device_id; + + /// \brief Initialize an ArrowDeviceArray from a previously allocated ArrowArray + /// + /// Given a device and an uninitialized device_array, populate the fields of the + /// device_array (including sync_event) appropriately. If NANOARROW_OK is returned, + /// ownership of array is transferred to device_array. This function must allocate + /// the appropriate sync_event and make its address available as + /// device_array->sync_event (if sync_event applies to this device type). + ArrowErrorCode (*array_init)(struct ArrowDevice* device, + struct ArrowDeviceArray* device_array, + struct ArrowArray* array); + + /// \brief Move an ArrowDeviceArray between devices without copying buffers + /// + /// Some devices can move an ArrowDeviceArray without an explicit buffer copy, + /// although the performance characteristics of the moved array may be different + /// than that of an explicitly copied one depending on the device. + ArrowErrorCode (*array_move)(struct ArrowDevice* device_src, + struct ArrowDeviceArray* src, + struct ArrowDevice* device_dst, + struct ArrowDeviceArray* dst); + + /// \brief Initialize an owning buffer from existing content + /// + /// Creates a new buffer whose data member can be accessed by the GPU by + /// copying existing content. + /// Implementations must check device_src and device_dst and return ENOTSUP if + /// not prepared to handle this operation. + ArrowErrorCode (*buffer_init)(struct ArrowDevice* device_src, + struct ArrowBufferView src, + struct ArrowDevice* device_dst, struct ArrowBuffer* dst); + + /// \brief Move an owning buffer to a device + /// + /// Creates a new buffer whose data member can be accessed by the GPU by + /// moving an existing buffer. If NANOARROW_OK is returned, src will have + /// been released or moved by the implementation and dst must be released by + /// the caller. + /// Implementations must check device_src and device_dst and return ENOTSUP if + /// not prepared to handle this operation. + ArrowErrorCode (*buffer_move)(struct ArrowDevice* device_src, struct ArrowBuffer* src, + struct ArrowDevice* device_dst, struct ArrowBuffer* dst); + + /// \brief Copy a section of memory into a preallocated buffer + /// + /// As opposed to the other buffer operations, this is designed to support + /// copying very small slices of memory. + /// Implementations must check device_src and device_dst and return ENOTSUP if + /// not prepared to handle this operation. + ArrowErrorCode (*buffer_copy)(struct ArrowDevice* device_src, + struct ArrowBufferView src, + struct ArrowDevice* device_dst, + struct ArrowBufferView dst); + + /// \brief Wait for an event on the CPU host + ArrowErrorCode (*synchronize_event)(struct ArrowDevice* device, void* sync_event, + struct ArrowError* error); + + /// \brief Release this device and any resources it holds + void (*release)(struct ArrowDevice* device); + + /// \brief Opaque, implementation-specific data. + void* private_data; +}; + +struct ArrowDeviceArrayView { + struct ArrowDevice* device; + struct ArrowArrayView array_view; +}; + +/// \brief Initialize an ArrowDeviceArray +/// +/// Given an ArrowArray whose buffers/release callback has been set appropriately, +/// initialize an ArrowDeviceArray. +ArrowErrorCode ArrowDeviceArrayInit(struct ArrowDevice* device, + struct ArrowDeviceArray* device_array, + struct ArrowArray* array); + +/// \brief Initialize an ArrowDeviceArrayView +/// +/// Zeroes memory for the device array view struct. Callers must initialize the +/// array_view member using nanoarrow core functions that can initialize from +/// a type identifier or schema. +void ArrowDeviceArrayViewInit(struct ArrowDeviceArrayView* device_array_view); + +/// \brief Release the underlying ArrowArrayView +void ArrowDeviceArrayViewReset(struct ArrowDeviceArrayView* device_array_view); + +/// \brief Set minimal ArrowArrayView buffer information from a device array +/// +/// A thin wrapper around ArrowArrayViewSetArrayMinimal() that does not attempt +/// to resolve buffer sizes of variable-length buffers by copying data from the device. +ArrowErrorCode ArrowDeviceArrayViewSetArrayMinimal( + struct ArrowDeviceArrayView* device_array_view, struct ArrowDeviceArray* device_array, + struct ArrowError* error); + +/// \brief Set ArrowArrayView buffer information from a device array +/// +/// Runs ArrowDeviceArrayViewSetArrayMinimal() but also sets buffer sizes for +/// variable-length buffers by copying data from the device. This function will block on +/// the device_array's sync_event. +ArrowErrorCode ArrowDeviceArrayViewSetArray( + struct ArrowDeviceArrayView* device_array_view, struct ArrowDeviceArray* device_array, + struct ArrowError* error); + +/// \brief Copy an ArrowDeviceArrayView to a device +ArrowErrorCode ArrowDeviceArrayViewCopy(struct ArrowDeviceArrayView* src, + struct ArrowDevice* device_dst, + struct ArrowDeviceArray* dst); + +/// \brief Move an ArrowDeviceArray to a device if possible +/// +/// Will attempt to move a device array to a device without copying buffers. +/// This may result in a device array with different performance charateristics +/// than an array that was copied. +ArrowErrorCode ArrowDeviceArrayMoveToDevice(struct ArrowDeviceArray* src, + struct ArrowDevice* device_dst, + struct ArrowDeviceArray* dst); + +/// \brief Pointer to a statically-allocated CPU device singleton +struct ArrowDevice* ArrowDeviceCpu(void); + +/// \brief Initialize a user-allocated device struct with a CPU device +void ArrowDeviceInitCpu(struct ArrowDevice* device); + +/// \brief Resolve a device pointer from a type + identifier +/// +/// Depending on which libraries this build of the device extension was built with, +/// some device types may or may not be supported. The CPU type is always supported. +/// Returns NULL for device that does not exist or cannot be returned as a singleton. +/// Callers must not release the pointed-to device. +struct ArrowDevice* ArrowDeviceResolve(ArrowDeviceType device_type, int64_t device_id); + +ArrowErrorCode ArrowDeviceBufferInit(struct ArrowDevice* device_src, + struct ArrowBufferView src, + struct ArrowDevice* device_dst, + struct ArrowBuffer* dst); + +ArrowErrorCode ArrowDeviceBufferMove(struct ArrowDevice* device_src, + struct ArrowBuffer* src, + struct ArrowDevice* device_dst, + struct ArrowBuffer* dst); + +ArrowErrorCode ArrowDeviceBufferCopy(struct ArrowDevice* device_src, + struct ArrowBufferView src, + struct ArrowDevice* device_dst, + struct ArrowBufferView dst); + +/// \brief Initialize an ArrowDeviceArrayStream from an existing ArrowArrayStream +/// +/// Wrap an ArrowArrayStream of ArrowDeviceArray objects already allocated by the +/// specified device as an ArrowDeviceArrayStream. This function moves the ownership of +/// array_stream to the device_array_stream. If this function returns NANOARROW_OK, the +/// caller is responsible for releasing the ArrowDeviceArrayStream. +ArrowErrorCode ArrowDeviceBasicArrayStreamInit( + struct ArrowDeviceArrayStream* device_array_stream, + struct ArrowArrayStream* array_stream, struct ArrowDevice* device); + +/// @} + +#ifdef __cplusplus +} +#endif + +#endif