Skip to content

Commit 086793a

Browse files
feat(extensions/nanoarrow_device): Draft DeviceArray interface (#205)
After: - https://github.com/zeroshade/arrow-non-cpu/tree/main - https://lists.apache.org/thread/o2hsw7o1gm3qgw5z51rmz6zqxh0p7bvk - apache/arrow#34972 Still in very much draft form; however, it *does* implement arbitrary ArrowArray copy to/from `ARROW_DEVICE_METAL`, `ARROW_DEVICE_CUDA`, `ARROW_DEVICE_CUDA_HOST`, and `ARROW_DEVICE_CPU`. The nanoarrow_device extension as drafted here serves a similar purpose to nanoarrow: a means by which to create and consume the C ABI with the intention of shipping those structures to other libraries to do transformations, and potentially retrieving them again after the computation is complete. Perhaps another way to put it is that nanoarrow is designed to help at the edges: it can create and consume. Similarly, the nanoarrow_device extension is designed to help at the edges: it can copy/move arrays to and from CPU-land. With this PR, you can currently do something like: ```c struct ArrowDevice* gpu = ArrowDeviceMetalDefaultDevice(); // Alternatively, ArrowDeviceCuda(ARROW_DEVICE_CUDA, 0) // or ArrowDeviceCuda(ARROW_DEVICE_CUDA_HOST, 0) struct ArrowDevice* cpu = ArrowDeviceCpu(); struct ArrowArray array; struct ArrowDeviceArray device_array; struct ArrowDeviceArrayView device_array_view; // Build a CPU array ASSERT_EQ(ArrowArrayInitFromType(&array, NANOARROW_TYPE_STRING), NANOARROW_OK); ASSERT_EQ(ArrowArrayStartAppending(&array), NANOARROW_OK); ASSERT_EQ(ArrowArrayAppendString(&array, ArrowCharView("abc")), NANOARROW_OK); ASSERT_EQ(ArrowArrayAppendString(&array, ArrowCharView("defg")), NANOARROW_OK); ASSERT_EQ(ArrowArrayAppendNull(&array, 1), NANOARROW_OK); ASSERT_EQ(ArrowArrayFinishBuildingDefault(&array, nullptr), NANOARROW_OK); // Convert to a DeviceArray, still on the CPU ArrowDeviceArrayInit(&device_array, cpu); ArrowArrayMove(&array, &device_array.array); // Parse contents into a view that can be copied to another device ArrowDeviceArrayViewInit(&device_array_view); ArrowArrayViewInitFromType(&device_array_view.array_view, string_type); ASSERT_EQ(ArrowDeviceArrayViewSetArray(&device_array_view, &device_array, nullptr), NANOARROW_OK); // Try to zero-copy move to another device or copy if that is not possible. Zero-copy move // is implemented for ARROW_DEVICE_METAL and ARROW_DEVICE_CUDA_HOST for the // gpu -> cpu case. struct ArrowDeviceArray device_array2; device_array2.array.release = nullptr; ASSERT_EQ( ArrowDeviceArrayTryMove(&device_array, &device_array_view, gpu, &device_array2), NANOARROW_OK); ``` In concrete terms, that means we to know enough about a device to (1) copy and/or move an arbitrary `ArrowArray`/`ArrowSchema` pair to a device from the CPU and (2) copy/move an arbitrary `ArrowDeviceArray`/`ArrowSchema` pair back to the CPU. The three types of copying I support (and maybe there could be fewer/need to be more) are: - `ArrowDeviceBufferInit()`: Make a non-owning buffer into an owning buffer on a device. The entry point if you want to take a slice of an `ArrowArrayView` and ship it to a device. - `ArrowDeviceBufferMove()`: Move an existing (owning) buffer to a device. For devices like the CPU, this is a true zero-copy move; for shared memory this can also sometimes be zero copy (e.g., Apple Metal -> CPU) but might also involve a copy. - `ArrowDeviceBufferCopy()`: Copy a section of a buffer into a preallocated section of another buffer. I'm envisioning this to be necessary when copying a String, Binary, List...we need the first and last values of the offsets buffer in order to know what portion of the data buffer to copy. It seems unnecessary to copy 4 bytes of a buffer into an owning variant covered by the first bullet but 🤷 . This PR currently provides support for the CPU device, Apple Metal, CUDA, and CUDA_HOST (i.e., CPU memory that has been registered with CUDA which CUDA copies under the hood). --------- Co-authored-by: Keith Kraus <keith.j.kraus@gmail.com>
1 parent d4f038c commit 086793a

33 files changed

Lines changed: 3629 additions & 9 deletions
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
name: test-c-device
19+
20+
on:
21+
push:
22+
branches:
23+
- main
24+
pull_request:
25+
branches:
26+
- main
27+
paths:
28+
- 'CMakeLists.txt'
29+
- '.github/workflows/build-and-test-device.yaml'
30+
- 'src/nanoarrow/**'
31+
- 'extensions/nanoarrow_device/**'
32+
33+
jobs:
34+
test-c-device:
35+
36+
runs-on: ubuntu-latest
37+
38+
name: ${{ matrix.config.label }}
39+
40+
strategy:
41+
fail-fast: false
42+
matrix:
43+
config:
44+
- {label: default-build}
45+
- {label: namespaced-build, cmake_args: "-DNANOARROW_NAMESPACE=SomeUserNamespace"}
46+
- {label: bundled-build, cmake_args: "-DNANOARROW_DEVICE_BUNDLE=ON"}
47+
48+
env:
49+
SUBDIR: 'extensions/nanoarrow_device'
50+
NANOARROW_ARROW_TESTING_DIR: '${{ github.workspace }}/arrow-testing'
51+
52+
steps:
53+
- name: Checkout repo
54+
uses: actions/checkout@v3
55+
with:
56+
fetch-depth: 0
57+
58+
- name: Checkout arrow-testing
59+
uses: actions/checkout@v3
60+
with:
61+
repository: apache/arrow-testing
62+
fetch-depth: 0
63+
path: arrow-testing
64+
65+
- name: Install dependencies
66+
run: |
67+
sudo apt-get update
68+
sudo apt install -y -V ca-certificates lsb-release wget cmake valgrind
69+
wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
70+
sudo apt-get install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
71+
sudo apt-get update
72+
sudo apt-get install -y -V libarrow-dev
73+
rm apache-arrow-apt-*.deb
74+
75+
- name: Build
76+
run: |
77+
cd $SUBDIR
78+
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/dist/lib
79+
sudo ldconfig
80+
mkdir build
81+
cd build
82+
cmake .. -DCMAKE_BUILD_TYPE=Debug -DNANOARROW_DEVICE_BUILD_TESTS=ON ${{ matrix.config.cmake_args }}
83+
cmake --build .
84+
85+
- name: Check for non-namespaced symbols in namespaced build
86+
if: matrix.config.label == 'namespaced-build'
87+
run: |
88+
cd $SUBDIR
89+
90+
# Dump all symbols
91+
nm --extern-only build/libnanoarrow_device.a
92+
93+
# Check for non-namespaced ones
94+
ARROW_SYMBOLS=`nm --extern-only build/libnanoarrow_device.a | grep "T Arrow" || true`
95+
if [ -z "$ARROW_SYMBOLS" ]; then
96+
exit 0
97+
fi
98+
99+
echo "Found the following non-namespaced extern symbols:"
100+
echo $ARROW_SYMBOLS
101+
exit 1
102+
103+
- name: Run tests
104+
run: |
105+
cd $SUBDIR
106+
107+
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/dist/lib
108+
sudo ldconfig
109+
cd build
110+
ctest -T test --output-on-failure .
111+
112+
- name: Run tests with valgrind
113+
if: matrix.config.label == 'default-build' || matrix.config.label == 'default-noatomics'
114+
run: |
115+
cd $SUBDIR
116+
117+
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/dist/lib
118+
sudo ldconfig
119+
cd build
120+
ctest -T memcheck .
121+
122+
- name: Upload memcheck results
123+
if: failure() && matrix.config.label == 'default-build'
124+
uses: actions/upload-artifact@main
125+
with:
126+
name: nanoarrow-device-memcheck
127+
path: extensions/nanoarrow_device/build/Testing/Temporary/MemoryChecker.*.log

.github/workflows/bundle.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ jobs:
5757
cmake --build .
5858
cmake --install . --prefix=../../../nanoarrow-latest
5959
60+
- name: Bundle nanoarrow_device
61+
run: |
62+
cd extensions/nanoarrow_device
63+
mkdir build && cd build
64+
cmake .. -DNANOARROW_DEVICE_BUNDLE=ON
65+
cmake --build .
66+
cmake --install . --prefix=../../../nanoarrow-latest
67+
6068
- name: Compress bundle
6169
run: |
6270
zip nanoarrow-latest.zip $(find nanoarrow-latest -type f)

ci/scripts/build-docs.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,21 @@ main() {
5959
rm -rf docs/_build
6060
mkdir -p docs/_build
6161

62-
# Run doxygen
6362
show_header "Run Doxygen for C library"
6463
pushd src/apidoc
6564
doxygen
6665
popd
6766

68-
# Run doxygen
6967
show_header "Run Doxygen for IPC extension"
7068
pushd extensions/nanoarrow_ipc/src/apidoc
7169
doxygen
7270
popd
7371

72+
show_header "Run Doxygen for device extension"
73+
pushd extensions/nanoarrow_device/src/apidoc
74+
doxygen
75+
popd
76+
7477
pushd docs
7578

7679
show_header "Build Sphinx project"

ci/scripts/coverage.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ function main() {
8585

8686
pushd "${SANDBOX_DIR}"
8787

88+
# Build + run tests with gcov for device extension
89+
show_header "Build + test nanoarrow_device"
90+
mkdir "${SANDBOX_DIR}/nanoarrow_device"
91+
pushd "${SANDBOX_DIR}/nanoarrow_device"
92+
93+
cmake "${TARGET_NANOARROW_DIR}/extensions/nanoarrow_device" \
94+
-DNANOARROW_DEVICE_BUILD_TESTS=ON -DNANOARROW_DEVICE_CODE_COVERAGE=ON
95+
cmake --build .
96+
ctest .
97+
98+
popd
99+
100+
pushd "${SANDBOX_DIR}"
101+
88102
# Generate coverage.info file for both cmake projects using lcov
89103
show_header "Calculate CMake project coverage"
90104
lcov --capture --directory . \

docs/source/conf.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
# Breathe configuration
5353
breathe_projects = {
5454
"nanoarrow_c": "../../src/apidoc/xml",
55-
"nanoarrow_ipc": "../../extensions/nanoarrow_ipc/src/apidoc/xml"
55+
"nanoarrow_ipc": "../../extensions/nanoarrow_ipc/src/apidoc/xml",
56+
"nanoarrow_device": "../../extensions/nanoarrow_device/src/apidoc/xml"
5657
}
5758
breathe_default_project = "nanoarrow_c"
5859

@@ -75,9 +76,7 @@
7576
html_theme_options = {
7677
"show_toc_level": 2,
7778
"use_edit_page_button": True,
78-
"external_links": [
79-
{"name": "R Package", "url": "r/index.html"},
80-
],
79+
"external_links": [],
8180
}
8281

8382
html_context = {

docs/source/index.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,4 @@ Contents
2424
:maxdepth: 2
2525

2626
Getting Started <getting-started>
27-
C API Reference <c>
28-
C++ API Reference <cpp>
29-
IPC Extension Reference <ipc>
27+
API Reference <reference/index>
File renamed without changes.
File renamed without changes.

docs/source/reference/device.rst

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.. Licensed to the Apache Software Foundation (ASF) under one
2+
.. or more contributor license agreements. See the NOTICE file
3+
.. distributed with this work for additional information
4+
.. regarding copyright ownership. The ASF licenses this file
5+
.. to you under the Apache License, Version 2.0 (the
6+
.. "License"); you may not use this file except in compliance
7+
.. with the License. You may obtain a copy of the License at
8+
9+
.. http://www.apache.org/licenses/LICENSE-2.0
10+
11+
.. Unless required by applicable law or agreed to in writing,
12+
.. software distributed under the License is distributed on an
13+
.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
.. KIND, either express or implied. See the License for the
15+
.. specific language governing permissions and limitations
16+
.. under the License.
17+
18+
Device Extension Reference
19+
==========================
20+
21+
C API
22+
------------------------
23+
24+
.. doxygengroup:: nanoarrow_device
25+
:project: nanoarrow_device
26+
:members:
27+
28+
C++ Helpers
29+
------------------------
30+
31+
.. doxygengroup:: nanoarrow_device_hpp-unique
32+
:project: nanoarrow_device
33+
:members:
34+
35+
Arrow C Device Interface
36+
------------------------
37+
38+
.. doxygengroup:: nanoarrow_device-arrow-cdata
39+
:project: nanoarrow_device
40+
:members:
41+
:undoc-members:
42+
43+
.. doxygengroup:: arrow-device-types
44+
:project: nanoarrow_device
45+
:members:
46+
:undoc-members:

docs/source/reference/index.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.. Licensed to the Apache Software Foundation (ASF) under one
2+
.. or more contributor license agreements. See the NOTICE file
3+
.. distributed with this work for additional information
4+
.. regarding copyright ownership. The ASF licenses this file
5+
.. to you under the Apache License, Version 2.0 (the
6+
.. "License"); you may not use this file except in compliance
7+
.. with the License. You may obtain a copy of the License at
8+
9+
.. http://www.apache.org/licenses/LICENSE-2.0
10+
11+
.. Unless required by applicable law or agreed to in writing,
12+
.. software distributed under the License is distributed on an
13+
.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
.. KIND, either express or implied. See the License for the
15+
.. specific language governing permissions and limitations
16+
.. under the License.
17+
18+
API Reference
19+
=============
20+
21+
.. toctree::
22+
:maxdepth: 2
23+
24+
R API Reference <r>
25+
C API Reference <c>
26+
C++ API Reference <cpp>
27+
IPC Extension Reference <ipc>
28+
Device Extension Reference <device>

0 commit comments

Comments
 (0)