diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index c24be384421..eb1b5ed20d3 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -16,30 +16,36 @@
# request a review from owners on PRs with changes to matching files.
# We currently do not enforce these reviews as required so it's only a tool
# for more granular notifications at the moment. For example component maintainers
-# can set a rule so that they are pinged on changes to the sections of the
+# can set a rule so that they are pinged on changes to the sections of the
# codebase that are relevant for their component.
# Only users that have write access to the repo can be added as owners.
# See the official docs for more details on syntax and precedence of rules: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#example-of-a-codeowners-file
+# Velox-cuDF codeowners
+* @rapidsai/velox-cudf-codeowners
+
# Build & CI
-CMake/ @assignUser @majetideepak
-*.cmake @assignUser @majetideepak
-**/CMakeLists.txt @assignUser @majetideepak
-scripts/ @assignUser @majetideepak
-.github/ @assignUser @majetideepak
+#CMake/ @assignUser @majetideepak
+#*.cmake @assignUser @majetideepak
+#**/CMakeLists.txt @assignUser @majetideepak
+#scripts/ @assignUser @majetideepak
+#.github/ @assignUser @majetideepak
+
+# Breeze
+#velox/experimental/breeze @dreveman
-# Breeze
-velox/experimental/breeze @dreveman
+# cuDF
+velox/experimental/cudf @bdice @karthikeyann @devavret
# Parquet
-velox/dwio/parquet/ @majetideepak
+#velox/dwio/parquet/ @majetideepak
# Storage Adapters
-velox/connectors/hive/storage_adapters/ @majetideepak
+#velox/connectors/hive/storage_adapters/ @majetideepak
# Connectors
-velox/connectors/ @majetideepak
+#velox/connectors/ @majetideepak
# Caching
-velox/common/caching/ @majetideepak
+#velox/common/caching/ @majetideepak
diff --git a/.github/copy-pr-bot.yaml b/.github/copy-pr-bot.yaml
new file mode 100644
index 00000000000..895ba83ee54
--- /dev/null
+++ b/.github/copy-pr-bot.yaml
@@ -0,0 +1,4 @@
+# Configuration file for `copy-pr-bot` GitHub App
+# https://docs.gha-runners.nvidia.com/apps/copy-pr-bot/
+
+enabled: true
diff --git a/.github/workflows/benchmark.yml b/.github/disabled-workflows/benchmark.yml
similarity index 99%
rename from .github/workflows/benchmark.yml
rename to .github/disabled-workflows/benchmark.yml
index 53d2b202700..317f83a1e25 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/disabled-workflows/benchmark.yml
@@ -38,7 +38,7 @@ defaults:
jobs:
benchmark:
if: github.repository == 'facebookincubator/velox'
- runs-on: 8-core-ubuntu-22.04
+ runs-on: linux-amd64-cpu8
env:
CCACHE_DIR: "${{ github.workspace }}/ccache/"
CCACHE_BASEDIR: "${{ github.workspace }}"
diff --git a/.github/workflows/breeze.yml b/.github/disabled-workflows/breeze.yml
similarity index 100%
rename from .github/workflows/breeze.yml
rename to .github/disabled-workflows/breeze.yml
diff --git a/.github/workflows/build-metrics.yml b/.github/disabled-workflows/build-metrics.yml
similarity index 99%
rename from .github/workflows/build-metrics.yml
rename to .github/disabled-workflows/build-metrics.yml
index 3677fd20ad9..f234b7a6414 100644
--- a/.github/workflows/build-metrics.yml
+++ b/.github/disabled-workflows/build-metrics.yml
@@ -42,7 +42,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- runner: ["16-core-ubuntu"]
+ runner: ["linux-amd64-cpu8"]
type: ["debug", "release"]
link-type: ["shared", "static"]
defaults:
diff --git a/.github/workflows/build_pyvelox.yml b/.github/disabled-workflows/build_pyvelox.yml
similarity index 100%
rename from .github/workflows/build_pyvelox.yml
rename to .github/disabled-workflows/build_pyvelox.yml
diff --git a/.github/workflows/docker.yml b/.github/disabled-workflows/docker.yml
similarity index 100%
rename from .github/workflows/docker.yml
rename to .github/disabled-workflows/docker.yml
diff --git a/.github/workflows/docs.yml b/.github/disabled-workflows/docs.yml
similarity index 100%
rename from .github/workflows/docs.yml
rename to .github/disabled-workflows/docs.yml
diff --git a/.github/workflows/experimental.yml b/.github/disabled-workflows/experimental.yml
similarity index 99%
rename from .github/workflows/experimental.yml
rename to .github/disabled-workflows/experimental.yml
index 73cf16c05a5..58f76bf292e 100644
--- a/.github/workflows/experimental.yml
+++ b/.github/disabled-workflows/experimental.yml
@@ -47,7 +47,7 @@ permissions:
jobs:
compile:
- runs-on: 16-core-ubuntu
+ runs-on: linux-amd64-cpu8
timeout-minutes: 120
env:
CCACHE_DIR: "${{ github.workspace }}/ccache/"
@@ -110,7 +110,7 @@ jobs:
path: velox/_build/debug/velox/expression/fuzzer/spark_expression_fuzzer_test
presto-java-aggregation-fuzzer-run:
- runs-on: 16-core-ubuntu
+ runs-on: linux-amd64-cpu8
container: ghcr.io/facebookincubator/velox-dev:presto-java
timeout-minutes: 120
env:
diff --git a/.github/workflows/linux-build-base.yml b/.github/disabled-workflows/linux-build-base.yml
similarity index 100%
rename from .github/workflows/linux-build-base.yml
rename to .github/disabled-workflows/linux-build-base.yml
diff --git a/.github/workflows/macos.yml b/.github/disabled-workflows/macos.yml
similarity index 100%
rename from .github/workflows/macos.yml
rename to .github/disabled-workflows/macos.yml
diff --git a/.github/workflows/scheduled.yml b/.github/disabled-workflows/scheduled.yml
similarity index 99%
rename from .github/workflows/scheduled.yml
rename to .github/disabled-workflows/scheduled.yml
index de383c1ad52..f60fa9fa057 100644
--- a/.github/workflows/scheduled.yml
+++ b/.github/disabled-workflows/scheduled.yml
@@ -93,7 +93,7 @@ jobs:
name: Build
# prevent errors when forks ff their main branch
if: ${{ github.repository == 'facebookincubator/velox' }}
- runs-on: 32-core-ubuntu
+ runs-on: linux-amd64-cpu8
container: ghcr.io/facebookincubator/velox-dev:centos9
timeout-minutes: 120
env:
diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml
index bca6e5f9498..3b723bdc599 100644
--- a/.github/workflows/linux-build.yml
+++ b/.github/workflows/linux-build.yml
@@ -17,33 +17,8 @@ name: Linux Build using GCC
on:
push:
branches:
- - "main"
- paths:
- - "velox/**"
- - "!velox/docs/**"
- - "CMakeLists.txt"
- - "CMake/**"
- - "third_party/**"
- - "scripts/setup-ubuntu.sh"
- - "scripts/setup-common.sh"
- - "scripts/setup-versions.sh"
- - "scripts/setup-helper-functions.sh"
- - ".github/workflows/linux-build.yml"
- - ".github/workflows/linux-build-base.yml"
-
- pull_request:
- paths:
- - "velox/**"
- - "!velox/docs/**"
- - "CMakeLists.txt"
- - "CMake/**"
- - "third_party/**"
- - "scripts/setup-common.sh"
- - "scripts/setup-versions.sh"
- - "scripts/setup-ubuntu.sh"
- - "scripts/setup-helper-functions.sh"
- - ".github/workflows/linux-build.yml"
- - ".github/workflows/linux-build-base.yml"
+ - "velox-cudf"
+ - "pull-request/[0-9]+"
permissions:
contents: read
@@ -53,6 +28,144 @@ concurrency:
cancel-in-progress: true
jobs:
- linux-gcc:
- name: Build with GCC
- uses: ./.github/workflows/linux-build-base.yml
+ adapters:
+ name: Linux release with adapters
+ runs-on: linux-amd64-cpu16
+ container: ghcr.io/facebookincubator/velox-dev:adapters
+ defaults:
+ run:
+ shell: bash
+ env:
+ CCACHE_DIR: "${{ github.workspace }}/ccache"
+ VELOX_DEPENDENCY_SOURCE: SYSTEM
+ GTest_SOURCE: BUNDLED
+ simdjson_SOURCE: BUNDLED
+ xsimd_SOURCE: BUNDLED
+ Arrow_SOURCE: BUNDLED
+ Thrift_SOURCE: BUNDLED
+ cudf_SOURCE: BUNDLED
+ CUDA_VERSION: "12.8"
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Fix git permissions
+ # Usually actions/checkout does this but as we run in a container
+ # it doesn't work
+ run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
+
+ - name: Install Dependencies
+ run: |
+ # Allows to install arbitrary cuda-version whithout needing to update
+ # docker container before. It simplifies testing new/different versions
+ if ! yum list installed cuda-nvcc-$(echo ${CUDA_VERSION} | tr '.' '-') 1>/dev/null; then
+ source scripts/setup-centos9.sh
+ install_cuda ${CUDA_VERSION}
+ fi
+
+ # TODO: Install a newer cmake here until we update the images upstream
+ pip install cmake==3.30.4
+
+ - uses: assignUser/stash/restore@v1
+ with:
+ path: '${{ env.CCACHE_DIR }}'
+ key: ccache-linux-adapters
+
+ - name: "Zero Ccache Statistics"
+ run: |
+ ccache -sz
+
+ - name: Make Release Build
+ env:
+ MAKEFLAGS: 'TREAT_WARNINGS_AS_ERRORS=0 NUM_THREADS=16 MAX_HIGH_MEM_JOBS=4'
+ CUDA_ARCHITECTURES: 70
+ CUDA_COMPILER: /usr/local/cuda-${CUDA_VERSION}/bin/nvcc
+ # Set compiler to GCC 12
+ CUDA_FLAGS: "-ccbin /opt/rh/gcc-toolset-12/root/usr/bin"
+ run: |
+ EXTRA_CMAKE_FLAGS=(
+ "-DVELOX_ENABLE_BENCHMARKS=ON"
+ "-DVELOX_ENABLE_ARROW=ON"
+ "-DVELOX_ENABLE_PARQUET=ON"
+ "-DVELOX_ENABLE_HDFS=ON"
+ "-DVELOX_ENABLE_S3=ON"
+ "-DVELOX_ENABLE_GCS=ON"
+ "-DVELOX_ENABLE_ABFS=ON"
+ "-DVELOX_ENABLE_REMOTE_FUNCTIONS=ON"
+ "-DVELOX_ENABLE_GPU=ON"
+ "-DVELOX_ENABLE_CUDF=ON"
+ "-DVELOX_MONO_LIBRARY=ON"
+ )
+ make release EXTRA_CMAKE_FLAGS="${EXTRA_CMAKE_FLAGS[*]}"
+
+ - name: Ccache after
+ run: ccache -s
+
+ - uses: assignUser/stash/save@v1
+ with:
+ path: '${{ env.CCACHE_DIR }}'
+ key: ccache-linux-adapters
+
+ - name: Run Tests
+ # Some of the adapters dependencies are in the 'adapters' conda env
+ shell: mamba run --no-capture-output -n adapters /usr/bin/bash -e {0}
+ env:
+ LIBHDFS3_CONF: "${{ github.workspace }}/scripts/hdfs-client.xml"
+ working-directory: _build/release
+ run: |
+ export CLASSPATH=`/usr/local/hadoop/bin/hdfs classpath --glob`
+ ctest -j 8 --label-exclude cuda_driver --output-on-failure --no-tests=error -E "velox_exec_test|velox_hdfs_file_test|velox_s3"
+
+# ubuntu-debug:
+# runs-on: linux-amd64-cpu16
+# name: "Ubuntu debug with resolve_dependency"
+# env:
+# CCACHE_DIR: "${{ github.workspace }}/ccache"
+# defaults:
+# run:
+# shell: bash
+# working-directory: velox
+# steps:
+
+# - name: Get Ccache Stash
+# uses: assignUser/stash/restore@v1
+# with:
+# path: '${{ env.CCACHE_DIR }}'
+# key: ccache-ubuntu-debug-default
+
+# - name: Ensure Stash Dirs Exists
+# working-directory: ${{ github.workspace }}
+# run: |
+# mkdir -p '${{ env.CCACHE_DIR }}'
+
+# - uses: actions/checkout@v4
+# with:
+# path: velox
+
+# - name: Install Dependencies
+# run: |
+# source scripts/setup-ubuntu.sh && install_apt_deps
+
+# - name: Clear CCache Statistics
+# run: |
+# ccache -sz
+
+# - name: Make Debug Build
+# env:
+# VELOX_DEPENDENCY_SOURCE: BUNDLED
+# MAKEFLAGS: "TREAT_WARNINGS_AS_ERRORS=0 NUM_THREADS=16 MAX_HIGH_MEM_JOBS=4"
+# EXTRA_CMAKE_FLAGS: "-DVELOX_ENABLE_ARROW=ON -DVELOX_ENABLE_PARQUET=ON"
+# run: |
+# make debug
+
+# - name: CCache after
+# run: |
+# ccache -vs
+
+# - uses: assignUser/stash/save@v1
+# with:
+# path: '${{ env.CCACHE_DIR }}'
+# key: ccache-ubuntu-debug-default
+
+# - name: Run Tests
+# run: |
+# cd _build/debug && ctest -j 8 --output-on-failure --no-tests=error -E "velox_exec_test"
diff --git a/.github/workflows/preliminary_checks.yml b/.github/workflows/preliminary_checks.yml
index 3e63212b5ec..e725ac1ca50 100644
--- a/.github/workflows/preliminary_checks.yml
+++ b/.github/workflows/preliminary_checks.yml
@@ -14,12 +14,9 @@
name: Run Checks
on:
- pull_request:
- types:
- - opened
- - reopened
- - edited
- - synchronize
+ push:
+ branches:
+ - "pull-request/[0-9]+"
permissions:
contents: read
@@ -37,10 +34,10 @@ jobs:
fail-fast: false
matrix:
config:
- - { name: "License Header",
- command: "header-fix",
- message: "Found missing License Header(s)",
- }
+# - { name: "License Header",
+# command: "header-fix",
+# message: "Found missing License Header(s)",
+# }
- { name: "Code Format",
command: "format-fix",
message: "Found format issues"
@@ -76,22 +73,22 @@ jobs:
exit 1
fi
- title-check:
- name: PR Title Format
- runs-on: ubuntu-latest
- steps:
- - shell: python
- env:
- title: "${{ github.event.pull_request.title }}"
- run: |
- import re
- import os
- title = os.environ["title"]
- title_re = r"^(feat|fix|build|test|docs|refactor|misc)(\(.+\))?!?: ([A-Z].+)[^.]$"
- match = re.search(title_re, title)
+# title-check:
+# name: PR Title Format
+# runs-on: ubuntu-latest
+# steps:
+# - shell: python
+# env:
+# title: "${{ github.event.pull_request.title }}"
+# run: |
+# import re
+# import os
+# title = os.environ["title"]
+# title_re = r"^(feat|fix|build|test|docs|refactor|misc)(\(.+\))?!?: ([A-Z].+)[^.]$"
+# match = re.search(title_re, title)
- if match is None:
- print("::error::Please follow conventional commit guidelines in commit titles as described in CONTRIBUTING.md: https://github.com/facebookincubator/velox/blob/main/CONTRIBUTING.md#commit-messages")
- exit(1)
- else:
- exit(0)
+# if match is None:
+# print("::error::Please follow conventional commit guidelines in commit titles as described in CONTRIBUTING.md: https://github.com/facebookincubator/velox/blob/main/CONTRIBUTING.md#commit-messages")
+# exit(1)
+# else:
+# exit(0)
diff --git a/.gitignore b/.gitignore
index 3ebb2942c5c..44273a09c1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -81,6 +81,7 @@ m4/lt~obsolete.m4
#m4/
build/
_build/
+.cache/
.ccache/
ccache/
#*.m4
@@ -310,7 +311,7 @@ third_party/imdb/data
.last_format
# Benchmarks
.last_benchmarked_commit
-benchmark_results/
+benchmark_results*
duckdb_unittest_tempdir/
grammar.y.tmp
src/amalgamation/
@@ -327,3 +328,9 @@ velox/docs/bindings/python/_generate/*
scripts/bm-report/report.html
dist/
wheelhouse/
+
+# Custom ignores
+aws-sdk-cpp
+cufile.log
+velox-tpch-*-data
+xsimd
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000000..6bb8dcebe16
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,20 @@
+repos:
+ - repo: local
+ hooks:
+ - id: check.py
+ name: check.py
+ entry: scripts/check.py format main --fix
+ language: python
+ # Note that pre-commit autoupdate does not update the versions
+ # of dependencies, so we'll have to update this manually.
+ additional_dependencies:
+ - clang-format==18.*
+ - cmakelang==0.6.13
+ - pyyaml
+ - regex
+ pass_filenames: false
+ verbose: true
+ require_serial: true
+
+default_language_version:
+ python: python3
diff --git a/CMake/resolve_dependency_modules/README.md b/CMake/resolve_dependency_modules/README.md
index 268617cbabe..201345e3e27 100644
--- a/CMake/resolve_dependency_modules/README.md
+++ b/CMake/resolve_dependency_modules/README.md
@@ -15,6 +15,7 @@ by Velox. See details on bundling below.
| glog | default | Yes |
| gtest (testing) | default | Yes |
| libevent | default | No |
+| libcudf | default | Yes |
| libsodium | default | No |
| lz4 | default | No |
| snappy | default | No |
diff --git a/CMake/resolve_dependency_modules/cudf.cmake b/CMake/resolve_dependency_modules/cudf.cmake
index 56f7b9bf1d1..81a88abaade 100644
--- a/CMake/resolve_dependency_modules/cudf.cmake
+++ b/CMake/resolve_dependency_modules/cudf.cmake
@@ -17,35 +17,35 @@ include_guard(GLOBAL)
# 3.30.4 is the minimum version required by cudf
cmake_minimum_required(VERSION 3.30.4)
-set(VELOX_rapids_cmake_VERSION 25.04)
+set(VELOX_rapids_cmake_VERSION 25.06)
set(VELOX_rapids_cmake_BUILD_SHA256_CHECKSUM
- 458c14eaff9000067b32d65c8c914f4521090ede7690e16eb57035ce731386db)
+ 812cef3478ef2ee02742d8cba68ab754603eb21e1333f1be03b91e7820ca0e27)
set(VELOX_rapids_cmake_SOURCE_URL
- "https://github.com/rapidsai/rapids-cmake/archive/7828fc8ff2e9f4fa86099f3c844505c2f47ac672.tar.gz"
+ "https://github.com/rapidsai/rapids-cmake/archive/4f203ce0126c91ff57289dfc70555f29cd81a8ee.tar.gz"
)
velox_resolve_dependency_url(rapids_cmake)
-set(VELOX_rmm_VERSION 25.04)
+set(VELOX_rmm_VERSION 25.06)
set(VELOX_rmm_BUILD_SHA256_CHECKSUM
- 294905094213a2d1fd8e024500359ff871bc52f913a3fbaca3514727c49f62de)
+ d2cabadc6c484620a7aaff533494920f4e8c8c027ae717cae83110883e3cb378)
set(VELOX_rmm_SOURCE_URL
- "https://github.com/rapidsai/rmm/archive/d8b7dacdeda302d2e37313c02d14ef5e1d1e98ea.tar.gz"
+ "https://github.com/rapidsai/rmm/archive/c7a331432f003bcaa8cc45d7bbcbd21a2876565f.tar.gz"
)
velox_resolve_dependency_url(rmm)
-set(VELOX_kvikio_VERSION 25.04)
+set(VELOX_kvikio_VERSION 25.06)
set(VELOX_kvikio_BUILD_SHA256_CHECKSUM
- 4a0b15295d0a397433930bf9a309e4ad2361b25dc7a7b3e6a35d0c9419d0cb62)
+ bdf756557ea6608ec5a00cde5130ec22b6059f96208c269a83c49917967804f8)
set(VELOX_kvikio_SOURCE_URL
- "https://github.com/rapidsai/kvikio/archive/5c710f37236bda76e447e929e17b1efbc6c632c3.tar.gz"
+ "https://github.com/rapidsai/kvikio/archive/9f143867a41c56d6df4c58572311c9bee004285a.tar.gz"
)
velox_resolve_dependency_url(kvikio)
-set(VELOX_cudf_VERSION 25.04)
+set(VELOX_cudf_VERSION 25.06)
set(VELOX_cudf_BUILD_SHA256_CHECKSUM
- e5a1900dfaf23dab2c5808afa17a2d04fa867d2892ecec1cb37908f3b73715c2)
+ 39e1b32c8491bb84f52388a3ae9cd28b1e82ce81f311e6678067741f9d0ac01d)
set(VELOX_cudf_SOURCE_URL
- "https://github.com/rapidsai/cudf/archive/4c1c99011da2c23856244e05adda78ba66697105.tar.gz"
+ "https://github.com/rapidsai/cudf/archive/191620472f3d3daeadf32003c37ee99eaa4773a9.tar.gz"
)
velox_resolve_dependency_url(cudf)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 999ad376645..ab7be3d0241 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -388,6 +388,8 @@ if(ENABLE_ALL_WARNINGS)
-Wno-unused-parameter \
-Wno-sign-compare \
-Wno-ignored-qualifiers \
+ -Wno-missing-field-initializers \
+ -Wno-deprecated-copy \
${KNOWN_COMPILER_SPECIFIC_WARNINGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra ${KNOWN_WARNINGS}")
diff --git a/README.md b/README.md
index 16fd3cd265c..5633efdeb77 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,23 @@
+> [!IMPORTANT]
+> # Experimental RAPIDS cuDF Backend for Velox
+> This repository is a fork of
+> [Velox](https://github.com/facebookincubator/velox) with support for [RAPIDS
+> cuDF](https://github.com/rapidsai/cudf/) as a GPU-accelerated Velox backend.
+> We are working to upstream the contents of this repository to the Velox
+> public repository. That effort is
+> tracked in [Velox issue
+> #12412](https://github.com/facebookincubator/velox/issues/12412), with a
+> description of the high level design and merge plan for upstreaming this
+> work.
+>
+> ### Quickstart
+> This repository contains scripts for quickly building and running Velox with
+> the cuDF backend. To launch the CUDA container:
+> ```
+> docker-compose run -e NUM_THREADS=$(nproc) --rm adapters-cuda /bin/bash
+> ```
+> Then invoke `./build.sh` to build Velox with cuDF support and run tests.
+
Velox is a composable execution engine distributed as an open source C++
diff --git a/benchmark.sh b/benchmark.sh
new file mode 100755
index 00000000000..5ed3d87360d
--- /dev/null
+++ b/benchmark.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+# Copyright (c) Facebook, Inc. and its affiliates.
+#
+# Licensed 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.
+
+set -euo pipefail
+
+# To get the data, copy from /datasets/velox-tpch-sf10-data to this repo:
+# cp -r /datasets/velox-tpch-sf10-data .
+
+# Run this to launch the CUDA container:
+# docker-compose run -e NUM_THREADS=$(nproc) --rm adapters-cuda /bin/bash
+# Then invoke ./build.sh to build with GPU support and run tests.
+
+# Run a GPU build and test
+pushd "$(dirname ${0})"
+
+mkdir -p benchmark_results
+
+queries=${1:-$(seq 1 22)}
+devices=${2:-"cpu gpu"}
+profile=${3:-"false"}
+
+num_drivers=${NUM_DRIVERS:-4}
+output_batch_rows=${BATCH_SIZE_ROWS:-100000}
+cudf_chunk_read_limit=$((1024 * 1024 * 1024 * 1))
+cudf_pass_read_limit=0
+VELOX_CUDF_MEMORY_RESOURCE="async"
+
+for query_number in ${queries}; do
+ printf -v query_number '%02d' "${query_number}"
+ for device in ${devices}; do
+ case "${device}" in
+ "cpu")
+ num_drivers=${NUM_DRIVERS:-32}
+ VELOX_CUDF_ENABLED=false;;
+ "gpu")
+ VELOX_CUDF_ENABLED=true;;
+ esac
+ echo "Running query ${query_number} on ${device} with ${num_drivers} drivers."
+ # The benchmarks segfault after reporting results, so we disable errors
+ PROFILE_CMD=""
+ if [[ "${profile}" == "true" ]]; then
+ PROFILE_CMD="nsys profile -t nvtx,cuda,osrt -f true --cuda-memory-usage=true --cuda-um-cpu-page-faults=true --cuda-um-gpu-page-faults=true --output=benchmark_results/q${query_number}_${device}_${num_drivers}_drivers.nsys-rep"
+ # Enable GPU metrics if supported (Ampere or newer)
+ if [[ "$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader -i 0 | cut -d '.' -f 1)" -gt 7 ]]; then
+ device_id=${CUDA_VISIBLE_DEVICES:-"0"}
+ PROFILE_CMD="${PROFILE_CMD} --gpu-metrics-devices=${device_id}"
+ fi
+ fi
+
+ set +e -x
+ ${PROFILE_CMD} \
+ ./_build/release/velox/benchmarks/tpch/velox_tpch_benchmark \
+ --data_path=velox-tpch-sf100-data \
+ --data_format=parquet \
+ --run_query_verbose=${query_number} \
+ --num_repeats=1 \
+ --velox_cudf_enabled=${VELOX_CUDF_ENABLED} \
+ --velox_cudf_memory_resource=${VELOX_CUDF_MEMORY_RESOURCE} \
+ --num_drivers=${num_drivers} \
+ --preferred_output_batch_rows=${output_batch_rows} \
+ --max_output_batch-rows=${output_batch_rows} 2>&1 \
+ --cudf_chunk_read_limit=${cudf_chunk_read_limit} \
+ --cudf_pass_read_limit=${cudf_pass_read_limit} \
+ | tee benchmark_results/q${query_number}_${device}_${num_drivers}_drivers
+ { set -e +x; } &> /dev/null
+ done
+done
+
+popd
diff --git a/build.sh b/build.sh
new file mode 100755
index 00000000000..12ca0786f3a
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# Copyright (c) Facebook, Inc. and its affiliates.
+#
+# Licensed 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.
+
+set -euo pipefail
+
+# Run this to launch the CUDA container:
+# docker-compose run -e NUM_THREADS=$(nproc) --rm adapters-cuda /bin/bash
+# Then invoke ./build.sh to build with GPU support and run tests.
+
+# Run a GPU build and test
+pushd "$(dirname ${0})"
+
+CUDA_ARCHITECTURES="native" EXTRA_CMAKE_FLAGS="-DVELOX_ENABLE_ARROW=ON -DVELOX_ENABLE_PARQUET=ON -DVELOX_ENABLE_BENCHMARKS=ON -DVELOX_ENABLE_BENCHMARKS_BASIC=ON" make cudf
+
+cd _build/release
+
+ctest -R cudf -V
+
+popd
diff --git a/docker-compose.yml b/docker-compose.yml
index 7dfc8752d24..15f4bbc1aa6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -29,6 +29,7 @@ services:
NUM_THREADS: 8 # default value for NUM_THREADS
VELOX_DEPENDENCY_SOURCE: BUNDLED # Build dependencies from source
CCACHE_DIR: "/velox/.ccache"
+ CMAKE_EXPORT_COMPILE_COMMANDS: 1
volumes:
- .:/velox:delegated
command: scripts/docker/docker-command.sh
diff --git a/install-aws-sdk.sh b/install-aws-sdk.sh
new file mode 100755
index 00000000000..9b9c2e9bbb4
--- /dev/null
+++ b/install-aws-sdk.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+# Copyright (c) Facebook, Inc. and its affiliates.
+#
+# Licensed 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.
+
+if [ ! -d "aws-sdk-cpp" ]; then
+ git clone https://github.com/aws/aws-sdk-cpp --recurse-submodules
+fi
+cd aws-sdk-cpp
+
+mkdir -p build
+cd build
+
+cmake ../ \
+ -DCMAKE_BUILD_TYPE=Debug \
+ -DCMAKE_PREFIX_PATH=/usr/local \
+ -DCMAKE_INSTALL_PREFIX=/usr/local \
+ -DBUILD_ONLY="s3;sts;cognito-identity;identity-management" \
+ -DENABLE_TESTING=OFF
+cmake --build . --config=Debug
+cmake --install . --config=Debug
+
+#cmake ../ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local
+#make
+#sudo make install
diff --git a/install-xsimd.sh b/install-xsimd.sh
new file mode 100755
index 00000000000..4d227ebc237
--- /dev/null
+++ b/install-xsimd.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Copyright (c) Facebook, Inc. and its affiliates.
+#
+# Licensed 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.
+
+if [ ! -d "xsimd" ]; then
+ git clone https://github.com/xtensor-stack/xsimd --recurse-submodules
+fi
+cd xsimd
+
+mkdir -p build
+cd build
+
+cmake ../
+cmake --build . --config=Debug
+cmake --install . --config=Debug
diff --git a/scripts/docker/adapters.dockerfile b/scripts/docker/adapters.dockerfile
index 47ce766b95b..f1575206591 100644
--- a/scripts/docker/adapters.dockerfile
+++ b/scripts/docker/adapters.dockerfile
@@ -36,7 +36,7 @@ ENV NVIDIA_VISIBLE_DEVICES=all
ENV NVIDIA_DRIVER_CAPABILITIES="compute,utility"
# install miniforge
-RUN curl -L -o /tmp/miniforge.sh https://github.com/conda-forge/miniforge/releases/download/23.11.0-0/Mambaforge-23.11.0-0-Linux-x86_64.sh && \
+RUN curl -L -o /tmp/miniforge.sh https://github.com/conda-forge/miniforge/releases/download/23.11.0-0/Mambaforge-23.11.0-0-Linux-$(uname -m).sh && \
bash /tmp/miniforge.sh -b -p /opt/miniforge && \
rm /tmp/miniforge.sh
ENV PATH=/opt/miniforge/condabin:${PATH}
diff --git a/scripts/setup-centos9.sh b/scripts/setup-centos9.sh
index 008f9302162..2ba94d17aff 100755
--- a/scripts/setup-centos9.sh
+++ b/scripts/setup-centos9.sh
@@ -85,8 +85,22 @@ function install_gflags {
}
function install_cuda {
+ dnf install -y patch
+ ARCH=$(uname -m)
+ case "$ARCH" in
+ x86_64)
+ CUDA_ARCH="x86_64"
+ ;;
+ aarch64)
+ CUDA_ARCH="sbsa"
+ ;;
+ *)
+ echo "Error: unsupported architecture $ARCH" >&2
+ exit 1
+ ;;
+ esac
# See https://developer.nvidia.com/cuda-downloads
- dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo
+ dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/${CUDA_ARCH}/cuda-rhel9.repo
local dashed="$(echo $1 | tr '.' '-')"
dnf install -y \
cuda-compat-$dashed \
diff --git a/scripts/setup-check.sh b/scripts/setup-check.sh
index d3d6573a8ed..5c54d0fb71f 100644
--- a/scripts/setup-check.sh
+++ b/scripts/setup-check.sh
@@ -19,7 +19,7 @@ set -x
export DEBIAN_FRONTEND=noninteractive
apt update
apt install --no-install-recommends -y clang-format-18 python3-pip git make ssh
-pip3 install --break-system-packages cmake==3.28.3 cmake_format black pyyaml regex
+pip3 install --break-system-packages cmake==3.30.4 cmake_format black pyyaml regex
pip3 cache purge
apt purge --auto-remove -y python3-pip
update-alternatives --install /usr/bin/clang-format clang-format "$(command -v clang-format-18)" 18
diff --git a/scripts/setup-ubuntu.sh b/scripts/setup-ubuntu.sh
index 33711884bc8..3ef490cfe83 100755
--- a/scripts/setup-ubuntu.sh
+++ b/scripts/setup-ubuntu.sh
@@ -88,7 +88,7 @@ function install_build_prerequisites {
fi
source ${PYTHON_VENV}/bin/activate;
# Install to /usr/local to make it available to all users.
- ${SUDO} pip3 install cmake==3.28.3
+ ${SUDO} pip3 install cmake==3.30.4
install_gcc11_if_needed
@@ -155,9 +155,22 @@ function install_conda {
}
function install_cuda {
- # See https://developer.nvidia.com/cuda-downloads
if ! dpkg -l cuda-keyring 1>/dev/null; then
- wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
+ ARCH=$(uname -m)
+ case "$ARCH" in
+ x86_64)
+ CUDA_ARCH="x86_64"
+ ;;
+ aarch64)
+ CUDA_ARCH="sbsa"
+ ;;
+ *)
+ echo "Error: unsupported architecture $ARCH" >&2
+ exit 1
+ ;;
+ esac
+ # See https://developer.nvidia.com/cuda-downloads
+ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/${CUDA_ARCH}/cuda-keyring_1.1-1_all.deb
$SUDO dpkg -i cuda-keyring_1.1-1_all.deb
rm cuda-keyring_1.1-1_all.deb
$SUDO apt update
diff --git a/scripts/velox_env_linux.yml b/scripts/velox_env_linux.yml
index 59ceeb0adb4..0fa15f13e87 100644
--- a/scripts/velox_env_linux.yml
+++ b/scripts/velox_env_linux.yml
@@ -26,7 +26,7 @@ dependencies:
- binutils
- bison
- clangxx=14
- - cmake=3.28.3
+ - cmake=3.30.4
- ccache
- flex
- gxx=12 # has to be installed to get clang to work...
diff --git a/velox/benchmarks/CMakeLists.txt b/velox/benchmarks/CMakeLists.txt
index 45466b1ed3f..cb235925115 100644
--- a/velox/benchmarks/CMakeLists.txt
+++ b/velox/benchmarks/CMakeLists.txt
@@ -60,6 +60,8 @@ target_link_libraries(
velox_type_fbhive
velox_caching
velox_vector_test_lib
+ velox_cudf_exec
+ velox_cudf_exec_test_lib
Folly::folly
Folly::follybenchmark
fmt::fmt)
diff --git a/velox/benchmarks/QueryBenchmarkBase.cpp b/velox/benchmarks/QueryBenchmarkBase.cpp
index 469b6c00542..12cc886b447 100644
--- a/velox/benchmarks/QueryBenchmarkBase.cpp
+++ b/velox/benchmarks/QueryBenchmarkBase.cpp
@@ -16,6 +16,11 @@
#include "velox/benchmarks/QueryBenchmarkBase.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetConfig.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetConnector.h"
+#include "velox/experimental/cudf/exec/CudfConversion.h"
+#include "velox/experimental/cudf/tests/utils/ParquetConnectorTestBase.h"
+
DEFINE_string(data_format, "parquet", "Data format");
DEFINE_validator(
@@ -89,7 +94,39 @@ DEFINE_int32(
"prefetch. 1 means prefetch the next row group before decoding "
"the current one");
-DEFINE_int32(split_preload_per_driver, 2, "Prefetch split metadata");
+DEFINE_uint64(
+ cudf_chunk_read_limit,
+ 0,
+ "Output table chunk read limit for cudf::parquet_chunked_reader.");
+
+DEFINE_uint64(
+ cudf_pass_read_limit,
+ 0,
+ "Pass read limit for cudf::parquet_chunked_reader.");
+
+DEFINE_int32(
+ cudf_gpu_batch_size_rows,
+ 100000,
+ "Preferred output batch size in rows for cudf operators.");
+
+DEFINE_int32(split_preload_per_driver, 1, "Prefetch split metadata");
+
+DEFINE_int64(
+ preferred_output_batch_bytes,
+ 10 << 20,
+ "Preferred output batch size in bytes");
+
+DEFINE_uint64(
+ max_partial_aggregation_memory,
+ 10 << 20,
+ "Maximum memory usage for partial aggregation");
+
+DEFINE_int32(
+ preferred_output_batch_rows,
+ 1024,
+ "Preferred output batch size in rows");
+
+DEFINE_int32(max_output_batch_rows, 10'000, "Max output batch size in rows");
using namespace facebook::velox::exec;
using namespace facebook::velox::exec::test;
@@ -195,6 +232,38 @@ void QueryBenchmarkBase::initialize() {
connector::registerConnector(hiveConnector);
parquet::registerParquetReaderFactory();
dwrf::registerDwrfReaderFactory();
+
+ // Add new values into the parquet configuration...
+ auto parquetConfigurationValues =
+ std::unordered_map();
+ parquetConfigurationValues
+ [cudf_velox::connector::parquet::ParquetConfig::kMaxChunkReadLimit] =
+ std::to_string(FLAGS_cudf_chunk_read_limit);
+ parquetConfigurationValues
+ [cudf_velox::connector::parquet::ParquetConfig::kMaxPassReadLimit] =
+ std::to_string(FLAGS_cudf_pass_read_limit);
+ parquetConfigurationValues[cudf_velox::connector::parquet::ParquetConfig::
+ kAllowMismatchedParquetSchemas] =
+ std::to_string(true);
+ auto parquetProperties = std::make_shared(
+ std::move(parquetConfigurationValues));
+
+ // Create parquet connector with config...
+ connector::registerConnectorFactory(
+ std::make_shared<
+ cudf_velox::connector::parquet::ParquetConnectorFactory>());
+ auto parquetConnector =
+ connector::getConnectorFactory(
+ cudf_velox::connector::parquet::ParquetConnectorFactory::
+ kParquetConnectorName)
+ ->newConnector(
+ cudf_velox::exec::test::kParquetConnectorId,
+ parquetProperties,
+ ioExecutor_.get());
+ connector::registerConnector(parquetConnector);
+
+ // Enable cuDF operators
+ cudf_velox::registerCudf();
}
std::vector>
@@ -211,7 +280,27 @@ QueryBenchmarkBase::listSplits(
return result;
}
+std::vector>
+QueryBenchmarkBase::listCudfSplits(
+ const std::string& path,
+ int32_t /*numSplitsPerFile*/,
+ const exec::test::TpchPlan& plan) {
+ std::vector> result;
+ auto temp = cudf_velox::exec::test::ParquetConnectorTestBase::
+ makeParquetConnectorSplits(path, 1);
+ for (auto& i : temp) {
+ result.push_back(i);
+ }
+ return result;
+}
+
void QueryBenchmarkBase::shutdown() {
+ cudf_velox::unregisterCudf();
+ facebook::velox::connector::unregisterConnector(
+ cudf_velox::exec::test::kParquetConnectorId);
+ facebook::velox::connector::unregisterConnectorFactory(
+ cudf_velox::connector::parquet::ParquetConnectorFactory::
+ kParquetConnectorName);
if (cache_) {
cache_->shutdown();
}
@@ -227,6 +316,16 @@ QueryBenchmarkBase::run(const TpchPlan& tpchPlan) {
params.planNode = tpchPlan.plan;
params.queryConfigs[core::QueryConfig::kMaxSplitPreloadPerDriver] =
std::to_string(FLAGS_split_preload_per_driver);
+ params.queryConfigs[core::QueryConfig::kPreferredOutputBatchBytes] =
+ std::to_string(FLAGS_preferred_output_batch_bytes);
+ params.queryConfigs[core::QueryConfig::kPreferredOutputBatchRows] =
+ std::to_string(FLAGS_preferred_output_batch_rows);
+ params.queryConfigs[core::QueryConfig::kMaxOutputBatchRows] =
+ std::to_string(FLAGS_max_output_batch_rows);
+ params.queryConfigs[cudf_velox::CudfFromVelox::kGpuBatchSizeRows] =
+ std::to_string(FLAGS_cudf_gpu_batch_size_rows);
+ params.queryConfigs[core::QueryConfig::kMaxPartialAggregationMemory] =
+ std::to_string(FLAGS_max_partial_aggregation_memory);
const int numSplitsPerFile = FLAGS_num_splits_per_file;
auto addSplits = [&](TaskCursor* taskCursor) {
@@ -234,7 +333,13 @@ QueryBenchmarkBase::run(const TpchPlan& tpchPlan) {
if (!taskCursor->noMoreSplits()) {
for (const auto& entry : tpchPlan.dataFiles) {
for (const auto& path : entry.second) {
- auto splits = listSplits(path, numSplitsPerFile, tpchPlan);
+ auto splits = facebook::velox::cudf_velox::cudfIsRegistered() &&
+ facebook::velox::connector::getAllConnectors().count(
+ cudf_velox::exec::test::kParquetConnectorId) > 0 &&
+ facebook::velox::cudf_velox::cudfTableScanEnabled()
+ ? listCudfSplits(
+ path, 1 /* numSplitsPerFile = 1 for cudf */, tpchPlan)
+ : listSplits(path, numSplitsPerFile, tpchPlan);
for (auto split : splits) {
task->addSplit(entry.first, exec::Split(std::move(split)));
}
diff --git a/velox/benchmarks/QueryBenchmarkBase.h b/velox/benchmarks/QueryBenchmarkBase.h
index a5a172a8216..6abad9c1be6 100644
--- a/velox/benchmarks/QueryBenchmarkBase.h
+++ b/velox/benchmarks/QueryBenchmarkBase.h
@@ -39,6 +39,7 @@
#include "velox/exec/Split.h"
#include "velox/exec/tests/utils/HiveConnectorTestBase.h"
#include "velox/exec/tests/utils/TpchQueryBuilder.h"
+#include "velox/experimental/cudf/exec/ToCudf.h"
#include "velox/functions/prestosql/aggregates/RegisterAggregateFunctions.h"
#include "velox/functions/prestosql/registration/RegistrationFunctions.h"
#include "velox/parse/TypeResolver.h"
@@ -94,6 +95,11 @@ class QueryBenchmarkBase {
int32_t numSplitsPerFile,
const exec::test::TpchPlan& plan);
+ std::vector> listCudfSplits(
+ const std::string& path,
+ int32_t numSplitsPerFile,
+ const exec::test::TpchPlan& plan);
+
static void ensureTaskCompletion(exec::Task* task);
static bool validateDataFormat(
diff --git a/velox/benchmarks/tpch/CMakeLists.txt b/velox/benchmarks/tpch/CMakeLists.txt
index 1ac7c3f1aee..15f2d16e435 100644
--- a/velox/benchmarks/tpch/CMakeLists.txt
+++ b/velox/benchmarks/tpch/CMakeLists.txt
@@ -18,6 +18,7 @@ target_link_libraries(
velox_tpch_benchmark_lib
velox_query_benchmark
velox_aggregates
+ velox_cudf_exec
velox_exec
velox_exec_test_lib
velox_dwio_common
diff --git a/velox/exec/tests/utils/CMakeLists.txt b/velox/exec/tests/utils/CMakeLists.txt
index acc1119c994..55b1966602c 100644
--- a/velox/exec/tests/utils/CMakeLists.txt
+++ b/velox/exec/tests/utils/CMakeLists.txt
@@ -38,6 +38,8 @@ add_library(
target_link_libraries(
velox_exec_test_lib
+ velox_cudf_exec
+ velox_cudf_parquet_connector
velox_vector_test_lib
velox_vector_fuzzer
velox_temp_path
diff --git a/velox/exec/tests/utils/PlanBuilder.cpp b/velox/exec/tests/utils/PlanBuilder.cpp
index 1ddf46c6197..1df09ad0be1 100644
--- a/velox/exec/tests/utils/PlanBuilder.cpp
+++ b/velox/exec/tests/utils/PlanBuilder.cpp
@@ -32,6 +32,10 @@
#include "velox/parse/Expressions.h"
#include "velox/parse/TypeResolver.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h"
+#include "velox/experimental/cudf/exec/ToCudf.h"
+#include "velox/experimental/cudf/tests/utils/ParquetConnectorTestBase.h"
+
using namespace facebook::velox;
using namespace facebook::velox::connector;
using namespace facebook::velox::connector::hive;
@@ -200,6 +204,7 @@ core::PlanNodePtr PlanBuilder::TableScanBuilder::build(core::PlanNodeId id) {
const RowTypePtr& parseType = dataColumns_ ? dataColumns_ : outputType_;
+ std::vector subfieldExprs;
core::TypedExprPtr filterNodeExpr;
common::SubfieldFilters filters;
filters.reserve(subfieldFilters_.size());
@@ -225,10 +230,33 @@ core::PlanNodePtr PlanBuilder::TableScanBuilder::build(core::PlanNodeId id) {
"Duplicate subfield: {}",
subfield.toString());
+ subfieldExprs.push_back(std::move(filterExpr));
filters[std::move(subfield)] = std::move(subfieldFilter);
}
}
+ // Create AND tree of subfieldExprs as combined_subfield_filter.
+ // replace every 2 subfieldExpr with a single AND node, until we have a single
+ // node.
+ while (subfieldExprs.size() > 1) {
+ std::vector combinedSubfieldExprs;
+ combinedSubfieldExprs.reserve(subfieldExprs.size() / 2 + 1);
+ for (size_t i = 0; i < subfieldExprs.size(); i += 2) {
+ if (i + 1 < subfieldExprs.size()) {
+ auto andCallExpr = std::make_shared(
+ BOOLEAN(),
+ std::vector{
+ subfieldExprs[i], subfieldExprs[i + 1]},
+ "and");
+ combinedSubfieldExprs.push_back(andCallExpr);
+ } else {
+ combinedSubfieldExprs.push_back(subfieldExprs[i]);
+ }
+ }
+ subfieldExprs = std::move(combinedSubfieldExprs);
+ }
+ core::TypedExprPtr subfieldFilterExpr =
+ subfieldExprs.empty() ? nullptr : subfieldExprs[0];
core::TypedExprPtr remainingFilterExpr;
if (remainingFilter_) {
remainingFilterExpr = core::Expressions::inferTypes(
@@ -241,13 +269,28 @@ core::PlanNodePtr PlanBuilder::TableScanBuilder::build(core::PlanNodeId id) {
}
if (!tableHandle_) {
- tableHandle_ = std::make_shared(
- connectorId_,
- tableName_,
- true,
- std::move(filters),
- remainingFilterExpr,
- dataColumns_);
+ // if cudfIsRegistered, then use cudftableScan tableHandle_ here.
+ if (facebook::velox::cudf_velox::cudfIsRegistered() &&
+ facebook::velox::connector::getAllConnectors().count(
+ cudf_velox::exec::test::kParquetConnectorId) > 0 &&
+ facebook::velox::cudf_velox::cudfTableScanEnabled()) {
+ tableHandle_ =
+ std::make_shared(
+ cudf_velox::exec::test::kParquetConnectorId,
+ tableName_,
+ subfieldFilterExpr != nullptr,
+ subfieldFilterExpr,
+ remainingFilterExpr,
+ dataColumns_);
+ } else {
+ tableHandle_ = std::make_shared(
+ connectorId_,
+ tableName_,
+ true,
+ std::move(filters),
+ remainingFilterExpr,
+ dataColumns_);
+ }
}
core::PlanNodePtr result = std::make_shared(
id, outputType_, tableHandle_, assignments_);
diff --git a/velox/experimental/cudf/.clang-tidy b/velox/experimental/cudf/.clang-tidy
index ec5b4e40784..61c04b1dbd5 100644
--- a/velox/experimental/cudf/.clang-tidy
+++ b/velox/experimental/cudf/.clang-tidy
@@ -44,11 +44,11 @@ CheckOptions:
value: CamelCase
- key: readability-identifier-naming.EnumConstantPrefix
value: k
-
+
# Use nullptr instead of NULL or 0
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
-
+
# Prefer enum class over enum
- key: modernize-use-using.IgnoreUsingStdAllocator
- value: 1
\ No newline at end of file
+ value: 1
diff --git a/velox/experimental/cudf/CMakeLists.txt b/velox/experimental/cudf/CMakeLists.txt
index 6d400056c35..96fcdb0d557 100644
--- a/velox/experimental/cudf/CMakeLists.txt
+++ b/velox/experimental/cudf/CMakeLists.txt
@@ -13,6 +13,8 @@
# limitations under the License.
add_subdirectory(exec)
+add_subdirectory(connectors)
+add_subdirectory(vector)
if(VELOX_BUILD_TESTING)
add_subdirectory(tests)
diff --git a/velox/experimental/cudf/connectors/CMakeLists.txt b/velox/experimental/cudf/connectors/CMakeLists.txt
new file mode 100644
index 00000000000..37a9408221c
--- /dev/null
+++ b/velox/experimental/cudf/connectors/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Copyright (c) Facebook, Inc. and its affiliates.
+#
+# Licensed 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.
+
+add_subdirectory(parquet)
diff --git a/velox/experimental/cudf/connectors/parquet/CMakeLists.txt b/velox/experimental/cudf/connectors/parquet/CMakeLists.txt
new file mode 100644
index 00000000000..88ae38df265
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Copyright (c) Facebook, Inc. and its affiliates.
+#
+# Licensed 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.
+
+add_library(velox_cudf_parquet_config ParquetConfig.cpp)
+
+set_target_properties(
+ velox_cudf_parquet_config
+ PROPERTIES CUDA_ARCHITECTURES native)
+
+target_link_libraries(
+ velox_cudf_parquet_config velox_core velox_exception cudf::cudf)
+
+add_library(
+ velox_cudf_parquet_connector OBJECT
+ ParquetConfig.cpp
+ ParquetConnector.cpp
+ ParquetConnectorSplit.cpp
+ ParquetDataSource.cpp
+ ParquetDataSink.cpp
+ ParquetTableHandle.cpp)
+
+set_target_properties(
+ velox_cudf_parquet_connector
+ PROPERTIES CUDA_ARCHITECTURES native)
+
+target_link_libraries(
+ velox_cudf_parquet_connector
+ PRIVATE
+ cudf::cudf
+ velox_common_io
+ velox_connector
+ velox_type_tz
+ velox_gcs)
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetConfig.cpp b/velox/experimental/cudf/connectors/parquet/ParquetConfig.cpp
new file mode 100644
index 00000000000..15449837930
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetConfig.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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 "velox/experimental/cudf/connectors/parquet/ParquetConfig.h"
+
+#include "velox/common/base/Exceptions.h"
+#include "velox/common/config/Config.h"
+
+#include
+
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+int64_t ParquetConfig::skipRows() const {
+ return config_->get(kSkipRows, 0);
+}
+
+std::optional ParquetConfig::numRows() const {
+ auto numRows = config_->get(kNumRows);
+ return numRows.has_value()
+ ? std::make_optional(numRows.value())
+ : std::nullopt;
+}
+
+std::size_t ParquetConfig::maxChunkReadLimit() const {
+ // chunk read limit = 0 means no limit
+ return config_->get(kMaxChunkReadLimit, 0);
+}
+
+std::size_t ParquetConfig::maxChunkReadLimitSession(
+ const config::ConfigBase* session) const {
+ // pass read limit = 0 means no limit
+ return session->get(
+ kMaxChunkReadLimitSession,
+ config_->get(kMaxChunkReadLimit, 0));
+}
+
+std::size_t ParquetConfig::maxPassReadLimit() const {
+ // pass read limit = 0 means no limit
+ return config_->get(kMaxPassReadLimit, 0);
+}
+
+std::size_t ParquetConfig::maxPassReadLimitSession(
+ const config::ConfigBase* session) const {
+ // pass read limit = 0 means no limit
+ return session->get(
+ kMaxPassReadLimitSession,
+ config_->get(kMaxPassReadLimit, 0));
+}
+
+bool ParquetConfig::isConvertStringsToCategories() const {
+ return config_->get(kConvertStringsToCategories, false);
+}
+
+bool ParquetConfig::isConvertStringsToCategoriesSession(
+ const config::ConfigBase* session) const {
+ return session->get(
+ kConvertStringsToCategoriesSession,
+ config_->get(kConvertStringsToCategories, false));
+}
+
+bool ParquetConfig::isUsePandasMetadata() const {
+ return config_->get(kUsePandasMetadata, true);
+}
+
+bool ParquetConfig::isUsePandasMetadataSession(
+ const config::ConfigBase* session) const {
+ return session->get(
+ kUsePandasMetadataSession, config_->get(kUsePandasMetadata, true));
+}
+
+bool ParquetConfig::isUseArrowSchema() const {
+ return config_->get(kUseArrowSchema, true);
+}
+
+bool ParquetConfig::isUseArrowSchemaSession(
+ const config::ConfigBase* session) const {
+ return session->get(
+ kUseArrowSchemaSession, config_->get(kUseArrowSchema, true));
+}
+
+bool ParquetConfig::isAllowMismatchedParquetSchemas() const {
+ return config_->get(kAllowMismatchedParquetSchemas, false);
+}
+
+bool ParquetConfig::isAllowMismatchedParquetSchemasSession(
+ const config::ConfigBase* session) const {
+ return session->get(
+ kAllowMismatchedParquetSchemasSession,
+ config_->get(kAllowMismatchedParquetSchemas, false));
+}
+
+cudf::data_type ParquetConfig::timestampType() const {
+ const auto unit = config_->get(
+ kTimestampType, cudf::type_id::TIMESTAMP_MILLISECONDS /*milli*/);
+ VELOX_CHECK(
+ unit == cudf::type_id::TIMESTAMP_DAYS /*days*/ ||
+ unit == cudf::type_id::TIMESTAMP_SECONDS /*seconds*/ ||
+ unit == cudf::type_id::TIMESTAMP_MILLISECONDS /*milli*/ ||
+ unit == cudf::type_id::TIMESTAMP_MICROSECONDS /*micro*/ ||
+ unit == cudf::type_id::TIMESTAMP_NANOSECONDS /*nano*/,
+ "Invalid timestamp unit.");
+ return cudf::data_type(cudf::type_id{unit});
+}
+
+cudf::data_type ParquetConfig::timestampTypeSession(
+ const config::ConfigBase* session) const {
+ const auto unit = session->get(
+ kTimestampTypeSession,
+ config_->get(
+ kTimestampType, cudf::type_id::TIMESTAMP_MILLISECONDS /*milli*/));
+ VELOX_CHECK(
+ unit == cudf::type_id::TIMESTAMP_DAYS /*days*/ ||
+ unit == cudf::type_id::TIMESTAMP_SECONDS /*seconds*/ ||
+ unit == cudf::type_id::TIMESTAMP_MILLISECONDS /*milli*/ ||
+ unit == cudf::type_id::TIMESTAMP_MICROSECONDS /*micro*/ ||
+ unit == cudf::type_id::TIMESTAMP_NANOSECONDS /*nano*/,
+ "Invalid timestamp unit.");
+ return cudf::data_type(cudf::type_id{unit});
+}
+
+bool ParquetConfig::immutableFiles() const {
+ return config_->get(kImmutableFiles, false);
+}
+
+uint64_t ParquetConfig::sortWriterFinishTimeSliceLimitMs(
+ const config::ConfigBase* session) const {
+ return session->get(
+ kSortWriterFinishTimeSliceLimitMsSession,
+ config_->get(kSortWriterFinishTimeSliceLimitMs, 5'000));
+}
+
+bool ParquetConfig::writeTimestampsAsUTC() const {
+ return config_->get(kWriteTimestampsAsUTC, true);
+}
+
+bool ParquetConfig::writeTimestampsAsUTCSession(
+ const config::ConfigBase* session) const {
+ return session->get(
+ kWriteTimestampsAsUTCSession,
+ config_->get(kWriteTimestampsAsUTC, true));
+}
+
+bool ParquetConfig::writeArrowSchema() const {
+ return config_->get(kWriteArrowSchema, false);
+}
+
+bool ParquetConfig::writeArrowSchemaSession(
+ const config::ConfigBase* session) const {
+ return session->get(
+ kWriteArrowSchemaSession, config_->get(kWriteArrowSchema, false));
+}
+
+bool ParquetConfig::writev2PageHeaders() const {
+ return config_->get(kWritev2PageHeaders, false);
+}
+
+bool ParquetConfig::writev2PageHeadersSession(
+ const config::ConfigBase* session) const {
+ return session->get(
+ kWritev2PageHeadersSession,
+ config_->get(kWritev2PageHeaders, false));
+}
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetConfig.h b/velox/experimental/cudf/connectors/parquet/ParquetConfig.h
new file mode 100644
index 00000000000..bf80ad8b0ec
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetConfig.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "velox/common/config/Config.h"
+
+#include
+
+#include
+
+namespace facebook::velox::config {
+class ConfigBase;
+}
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+class ParquetConfig {
+ public:
+ // Reader config options
+
+ // Number of rows to skip from the start; Parquet stores the number of rows as
+ // int64_t
+ static constexpr const char* kSkipRows = "parquet.reader.skip-rows";
+
+ // Number of rows to read; `nullopt` is all
+ static constexpr const char* kNumRows = "parquet.reader.num-rows";
+
+ // This isn't a typo; parquet connector and session config names are different
+ // ('-' vs '_').
+ static constexpr const char* kMaxChunkReadLimit =
+ "parquet.reader.chunk-read-limit";
+ static constexpr const char* kMaxChunkReadLimitSession =
+ "parquet.reader.chunk_read_limit";
+
+ static constexpr const char* kMaxPassReadLimit =
+ "parquet.reader.pass-read-limit";
+ static constexpr const char* kMaxPassReadLimitSession =
+ "parquet.reader.pass_read_limit";
+
+ // Whether to store string data as categorical type
+ static constexpr const char* kConvertStringsToCategories =
+ "parquet.reader.convert-strings-to-categories";
+ static constexpr const char* kConvertStringsToCategoriesSession =
+ "parquet.reader.convert_strings_to_categories";
+
+ // Whether to use PANDAS metadata to load columns
+ static constexpr const char* kUsePandasMetadata =
+ "parquet.reader.use-pandas-metadata";
+ static constexpr const char* kUsePandasMetadataSession =
+ "parquet.reader.use_pandas_metadata";
+
+ // Whether to read and use ARROW schema
+ static constexpr const char* kUseArrowSchema =
+ "parquet.reader.use-arrow-schema";
+ static constexpr const char* kUseArrowSchemaSession =
+ "parquet.reader.use_arrow_schema";
+
+ // Whether to allow reading matching select columns from mismatched Parquet
+ // files.
+ static constexpr const char* kAllowMismatchedParquetSchemas =
+ "parquet.reader.allow-mismatched-parquet-schemas";
+ static constexpr const char* kAllowMismatchedParquetSchemasSession =
+ "parquet.reader.allow_mismatched_parquet_schemas";
+
+ // Cast timestamp columns to a specific type
+ static constexpr const char* kTimestampType = "parquet.reader.timestamp-type";
+ static constexpr const char* kTimestampTypeSession =
+ "parquet.reader.timestamp_type";
+
+ // Writer config options
+
+ /// Whether new data can be inserted into a Parquet file
+ /// Cudf-Velox currently does not support appending data to existing files.
+ static constexpr const char* kImmutableFiles = "parquet.immutable-files";
+
+ /// Sort Writer will exit finish() method after this many milliseconds even if
+ /// it has not completed its work yet. Zero means no time limit.
+ static constexpr const char* kSortWriterFinishTimeSliceLimitMs =
+ "sort-writer_finish_time_slice_limit_ms";
+ static constexpr const char* kSortWriterFinishTimeSliceLimitMsSession =
+ "sort_writer_finish_time_slice_limit_ms";
+
+ static constexpr const char* kWriteTimestampsAsUTC =
+ "parquet.writer.write-timestamps-as-utc";
+ static constexpr const char* kWriteTimestampsAsUTCSession =
+ "parquet.writer.write_timestamps_as_utc";
+
+ static constexpr const char* kWriteArrowSchema =
+ "parquet.writer.write-arrow-schema";
+ static constexpr const char* kWriteArrowSchemaSession =
+ "parquet.writer.write_arrow_schema";
+
+ static constexpr const char* kWritev2PageHeaders =
+ "parquet.writer.write-v2-page-headers";
+ static constexpr const char* kWritev2PageHeadersSession =
+ "parquet.writer.write_v2_page_headers";
+
+ ParquetConfig(std::shared_ptr config) {
+ VELOX_CHECK_NOT_NULL(
+ config, "Config is null for ParquetConfig initialization");
+ config_ = std::move(config);
+ }
+
+ const std::shared_ptr& config() const {
+ return config_;
+ }
+
+ uint64_t sortWriterFinishTimeSliceLimitMs(
+ const config::ConfigBase* session) const;
+
+ std::size_t maxChunkReadLimit() const;
+ std::size_t maxChunkReadLimitSession(const config::ConfigBase* session) const;
+
+ std::size_t maxPassReadLimit() const;
+ std::size_t maxPassReadLimitSession(const config::ConfigBase* session) const;
+
+ int64_t skipRows() const;
+ std::optional numRows() const;
+
+ bool isConvertStringsToCategories() const;
+ bool isConvertStringsToCategoriesSession(
+ const config::ConfigBase* session) const;
+
+ bool isUsePandasMetadata() const;
+ bool isUsePandasMetadataSession(const config::ConfigBase* session) const;
+
+ bool isUseArrowSchema() const;
+ bool isUseArrowSchemaSession(const config::ConfigBase* session) const;
+
+ bool isAllowMismatchedParquetSchemas() const;
+ bool isAllowMismatchedParquetSchemasSession(
+ const config::ConfigBase* session) const;
+
+ cudf::data_type timestampType() const;
+ cudf::data_type timestampTypeSession(const config::ConfigBase* session) const;
+
+ bool immutableFiles() const;
+
+ bool writeTimestampsAsUTC() const;
+ bool writeTimestampsAsUTCSession(const config::ConfigBase* session) const;
+
+ bool writeArrowSchema() const;
+ bool writeArrowSchemaSession(const config::ConfigBase* session) const;
+
+ bool writev2PageHeaders() const;
+ bool writev2PageHeadersSession(const config::ConfigBase* session) const;
+
+ private:
+ std::shared_ptr config_;
+};
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetConnector.cpp b/velox/experimental/cudf/connectors/parquet/ParquetConnector.cpp
new file mode 100644
index 00000000000..d53d02f0f4a
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetConnector.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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 "velox/experimental/cudf/connectors/parquet/ParquetConnector.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetDataSource.h"
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+using namespace facebook::velox::connector;
+
+ParquetConnector::ParquetConnector(
+ const std::string& id,
+ std::shared_ptr config,
+ folly::Executor* executor)
+ : Connector(id),
+ parquetConfig_(std::make_shared(config)),
+ executor_(executor) {
+ LOG(INFO) << "cudf::Parquet connector " << connectorId() << " created.";
+}
+
+std::unique_ptr ParquetConnector::createDataSource(
+ const std::shared_ptr& outputType,
+ const std::shared_ptr& tableHandle,
+ const std::unordered_map>&
+ columnHandles,
+ ConnectorQueryCtx* connectorQueryCtx) {
+ return std::make_unique(
+ outputType,
+ tableHandle,
+ columnHandles,
+ executor_,
+ connectorQueryCtx,
+ parquetConfig_);
+}
+
+std::unique_ptr ParquetConnector::createDataSink(
+ RowTypePtr inputType,
+ std::shared_ptr connectorInsertTableHandle,
+ ConnectorQueryCtx* connectorQueryCtx,
+ CommitStrategy /*commitStrategy*/) {
+ auto parquetInsertHandle =
+ std::dynamic_pointer_cast(
+ connectorInsertTableHandle);
+ VELOX_CHECK_NOT_NULL(
+ parquetInsertHandle, "Parquet connector expecting parquet write handle!");
+ return std::make_unique(
+ inputType,
+ parquetInsertHandle,
+ connectorQueryCtx,
+ CommitStrategy::kNoCommit,
+ parquetConfig_);
+}
+
+std::shared_ptr ParquetConnectorFactory::newConnector(
+ const std::string& id,
+ std::shared_ptr config,
+ folly::Executor* ioExecutor,
+ folly::Executor* cpuExecutor) {
+ return std::make_shared(id, config, ioExecutor);
+}
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetConnector.h b/velox/experimental/cudf/connectors/parquet/ParquetConnector.h
new file mode 100644
index 00000000000..2e6b24a3be6
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetConnector.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "velox/experimental/cudf/connectors/parquet/ParquetConfig.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetDataSink.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetDataSource.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h"
+
+#include "velox/connectors/Connector.h"
+
+#include
+#include
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+using namespace facebook::velox::connector;
+using namespace facebook::velox::config;
+
+class ParquetConnector final : public Connector {
+ public:
+ ParquetConnector(
+ const std::string& id,
+ std::shared_ptr config,
+ folly::Executor* executor);
+
+ std::unique_ptr createDataSource(
+ const std::shared_ptr& outputType,
+ const std::shared_ptr& tableHandle,
+ const std::unordered_map>&
+ columnHandles,
+ ConnectorQueryCtx* connectorQueryCtx) override final;
+
+ const std::shared_ptr& connectorConfig() const override {
+ return parquetConfig_->config();
+ }
+
+ std::unique_ptr createDataSink(
+ RowTypePtr inputType,
+ std::shared_ptr connectorInsertTableHandle,
+ ConnectorQueryCtx* connectorQueryCtx,
+ CommitStrategy commitStrategy) override final;
+
+ folly::Executor* executor() const override {
+ return executor_;
+ }
+
+ protected:
+ const std::shared_ptr parquetConfig_;
+ folly::Executor* executor_;
+};
+
+class ParquetConnectorFactory : public ConnectorFactory {
+ public:
+ static constexpr const char* kParquetConnectorName = "parquet";
+
+ ParquetConnectorFactory() : ConnectorFactory(kParquetConnectorName) {}
+
+ explicit ParquetConnectorFactory(const char* connectorName)
+ : ConnectorFactory(connectorName) {}
+
+ std::shared_ptr newConnector(
+ const std::string& id,
+ std::shared_ptr config,
+ folly::Executor* ioExecutor = nullptr,
+ folly::Executor* cpuExecutor = nullptr) override;
+};
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.cpp b/velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.cpp
new file mode 100644
index 00000000000..1dc05127659
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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 "velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.h"
+
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+std::string ParquetConnectorSplit::toString() const {
+ return fmt::format("Parquet: {}", filePath);
+}
+
+std::string ParquetConnectorSplit::getFileName() const {
+ const auto i = filePath.rfind('/');
+ return i == std::string::npos ? filePath : filePath.substr(i + 1);
+}
+
+// static
+std::shared_ptr ParquetConnectorSplit::create(
+ const folly::dynamic& obj) {
+ const auto connectorId = obj["connectorId"].asString();
+ const auto splitWeight = obj["splitWeight"].asInt();
+ const auto filePath = obj["filePath"].asString();
+
+ return std::make_shared(
+ connectorId, filePath, splitWeight);
+}
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.h b/velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.h
new file mode 100644
index 00000000000..72e9ba7a572
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "velox/connectors/Connector.h"
+#include "velox/dwio/common/Options.h"
+
+#include
+
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+struct ParquetConnectorSplit
+ : public facebook::velox::connector::ConnectorSplit {
+ const std::string filePath;
+ const facebook::velox::dwio::common::FileFormat fileFormat{
+ facebook::velox::dwio::common::FileFormat::PARQUET};
+ const cudf::io::source_info cudfSourceInfo;
+
+ ParquetConnectorSplit(
+ const std::string& connectorId,
+ const std::string& _filePath,
+ int64_t _splitWeight = 0)
+ : facebook::velox::connector::ConnectorSplit(connectorId, _splitWeight),
+ filePath(_filePath),
+ cudfSourceInfo({filePath}) {}
+
+ std::string toString() const override;
+ std::string getFileName() const;
+
+ const cudf::io::source_info& getCudfSourceInfo() const {
+ return cudfSourceInfo;
+ }
+
+ static std::shared_ptr create(
+ const folly::dynamic& obj);
+};
+
+class ParquetConnectorSplitBuilder {
+ public:
+ explicit ParquetConnectorSplitBuilder(std::string filePath)
+ : filePath_{std::move(filePath)} {}
+
+ ParquetConnectorSplitBuilder& splitWeight(int64_t splitWeight) {
+ splitWeight_ = splitWeight;
+ return *this;
+ }
+
+ ParquetConnectorSplitBuilder& connectorId(const std::string& connectorId) {
+ connectorId_ = connectorId;
+ return *this;
+ }
+
+ std::shared_ptr build() const {
+ return std::make_shared(
+ connectorId_, filePath_, splitWeight_);
+ }
+
+ private:
+ const std::string filePath_;
+ std::string connectorId_;
+ int64_t splitWeight_{0};
+};
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetDataSink.cpp b/velox/experimental/cudf/connectors/parquet/ParquetDataSink.cpp
new file mode 100644
index 00000000000..00c27048fc9
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetDataSink.cpp
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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 "velox/experimental/cudf/connectors/parquet/ParquetConfig.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetDataSink.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h"
+#include "velox/experimental/cudf/exec/Utilities.h"
+#include "velox/experimental/cudf/exec/VeloxCudfInterop.h"
+#include "velox/experimental/cudf/vector/CudfVector.h"
+
+#include "velox/common/base/Counters.h"
+#include "velox/common/base/Fs.h"
+#include "velox/common/base/StatsReporter.h"
+#include "velox/dwio/common/Options.h"
+#include "velox/exec/OperatorUtils.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+using facebook::velox::common::testutil::TestValue;
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+namespace {
+
+std::unordered_map tableTypeNames() {
+ return {
+ {LocationHandle::TableType::kNew, "kNew"},
+ };
+}
+
+template
+std::unordered_map invertMap(const std::unordered_map& mapping) {
+ std::unordered_map inverted;
+ for (const auto& [key, value] : mapping) {
+ inverted.emplace(value, key);
+ }
+ return inverted;
+}
+
+uint64_t getFinishTimeSliceLimitMsFromParquetConfig(
+ const std::shared_ptr& config,
+ const config::ConfigBase* sessions) {
+ const uint64_t flushTimeSliceLimitMsFromConfig =
+ config->sortWriterFinishTimeSliceLimitMs(sessions);
+ // NOTE: if the flush time slice limit is set to 0, then we treat it as no
+ // limit.
+ return flushTimeSliceLimitMsFromConfig == 0
+ ? std::numeric_limits::max()
+ : flushTimeSliceLimitMsFromConfig;
+}
+
+std::string makeUuid() {
+ return boost::lexical_cast(boost::uuids::random_generator()());
+}
+
+cudf::io::compression_type getCompressionType(
+ facebook::velox::common::CompressionKind name) {
+ using CompressionType = cudf::io::compression_type;
+
+ static std::unordered_map<
+ facebook::velox::common::CompressionKind,
+ CompressionType> const kMap = {
+ {facebook::velox::common::CompressionKind::CompressionKind_NONE,
+ CompressionType::NONE},
+ {facebook::velox::common::CompressionKind::CompressionKind_SNAPPY,
+ CompressionType::SNAPPY},
+ {facebook::velox::common::CompressionKind::CompressionKind_LZ4,
+ CompressionType::LZ4},
+ {facebook::velox::common::CompressionKind::CompressionKind_ZSTD,
+ CompressionType::ZSTD}};
+
+ VELOX_CHECK(
+ kMap.find(name) != kMap.end(),
+ "Unsupported compression type requested. Supported compression types are: "
+ "NONE, SNAPPY, LZ4, ZSTD");
+
+ return kMap.at(name);
+}
+
+std::shared_ptr createSinkPool(
+ const std::shared_ptr& writerPool) {
+ return writerPool->addLeafChild(fmt::format("{}.sink", writerPool->name()));
+}
+
+std::shared_ptr createSortPool(
+ const std::shared_ptr& writerPool) {
+ return writerPool->addLeafChild(fmt::format("{}.sort", writerPool->name()));
+}
+
+} // namespace
+
+const std::string LocationHandle::tableTypeName(
+ LocationHandle::TableType type) {
+ static const auto kTableTypes = tableTypeNames();
+ return kTableTypes.at(type);
+}
+
+LocationHandle::TableType LocationHandle::tableTypeFromName(
+ const std::string& name) {
+ static const auto kNameTableTypes = invertMap(tableTypeNames());
+ return kNameTableTypes.at(name);
+}
+
+ParquetDataSink::ParquetDataSink(
+ RowTypePtr inputType,
+ std::shared_ptr insertTableHandle,
+ const ConnectorQueryCtx* connectorQueryCtx,
+ CommitStrategy commitStrategy,
+ const std::shared_ptr& parquetConfig)
+ : inputType_(std::move(inputType)),
+ insertTableHandle_(std::move(insertTableHandle)),
+ connectorQueryCtx_(connectorQueryCtx),
+ commitStrategy_(commitStrategy),
+ parquetConfig_(parquetConfig),
+ spillConfig_(connectorQueryCtx->spillConfig()),
+ sortWriterFinishTimeSliceLimitMs_(
+ getFinishTimeSliceLimitMsFromParquetConfig(
+ parquetConfig_,
+ connectorQueryCtx->sessionProperties())) {
+ VELOX_USER_CHECK(
+ (commitStrategy_ == CommitStrategy::kNoCommit) ||
+ (commitStrategy_ == CommitStrategy::kTaskCommit),
+ "Unsupported commit strategy: {}",
+ commitStrategyToString(commitStrategy_));
+
+ const auto& writerOptions = dynamic_cast(
+ insertTableHandle_->writerOptions().get());
+
+ if (writerOptions != nullptr) {
+ sortingColumns_ = std::move(writerOptions->sortingColumns);
+ }
+}
+
+void ParquetDataSink::appendData(RowVectorPtr input) {
+ checkRunning();
+
+ // Convert the input RowVectorPtr to cudf::table
+ auto stream = cudfGlobalStreamPool().get_stream();
+ auto cudfInput = with_arrow::toCudfTable(input, input->pool(), stream);
+ stream.synchronize();
+ VELOX_CHECK_NOT_NULL(
+ cudfInput, "Failed to convert input RowVectorPtr to cudf::table");
+
+ // Check if the writer doesn't already exist
+ if (writer_ == nullptr) {
+ writer_ = createCudfWriter(cudfInput->view());
+ }
+
+ // Write the table to the sink
+ writer_->write(cudfInput->view());
+ writerInfo_->inputSizeInBytes += input->estimateFlatSize();
+ writerInfo_->numWrittenRows += input->size();
+}
+
+std::unique_ptr
+ParquetDataSink::createCudfWriter(cudf::table_view cudfTable) {
+ // Create a table_input_metadata from the input
+ auto tableInputMetadata = createCudfTableInputMetadata(cudfTable);
+
+ auto compressionKind =
+ getCompressionType(insertTableHandle_->compressionKind().value_or(
+ facebook::velox::common::CompressionKind::CompressionKind_NONE));
+
+ // Create a sink and writer
+ const auto& locationHandle = insertTableHandle_->locationHandle();
+ const auto targetFileName = locationHandle->targetFileName().empty()
+ ? fmt::format("{}{}", makeUuid(), ".parquet")
+ : locationHandle->targetFileName();
+
+ auto writerParameters = ParquetWriterParameters(
+ ParquetWriterParameters::UpdateMode::kNew,
+ targetFileName,
+ locationHandle->targetPath());
+
+ const auto writePath = fs::path(writerParameters.writeDirectory()) /
+ writerParameters.writeFileName();
+
+ makeWriterOptions(writerParameters);
+
+ // Create writer options for the given sink
+ const auto sinkInfo = cudf::io::sink_info(
+ fmt::format("{}/{}", locationHandle->targetPath(), targetFileName));
+ auto cudfWriterOptions =
+ cudf::io::chunked_parquet_writer_options::builder(sinkInfo)
+ .metadata(tableInputMetadata)
+ .utc_timestamps(parquetConfig_->writeTimestampsAsUTC())
+ .write_arrow_schema(parquetConfig_->writeArrowSchema())
+ .write_v2_headers(parquetConfig_->writev2PageHeaders())
+ .compression(compressionKind)
+ .build();
+
+ const auto& writerOptions = dynamic_cast(
+ insertTableHandle_->writerOptions().get());
+
+ // If non-null writerOptions were passed, pass them to the chunked parquet
+ // writer options
+ if (writerOptions != nullptr) {
+ // Set encoding for all columns
+ std::for_each(
+ tableInputMetadata.column_metadata.begin(),
+ tableInputMetadata.column_metadata.end(),
+ [=](auto& colMeta) { colMeta.set_encoding(writerOptions->encoding); });
+
+ cudfWriterOptions.set_row_group_size_bytes(
+ writerOptions->rowGroupSizeBytes);
+ cudfWriterOptions.set_row_group_size_rows(writerOptions->rowGroupSizeRows);
+ cudfWriterOptions.set_max_page_size_bytes(writerOptions->maxPageSizeBytes);
+ cudfWriterOptions.set_max_page_size_rows(writerOptions->maxPageSizeRows);
+ cudfWriterOptions.set_dictionary_policy(writerOptions->dictionaryPolicy);
+ cudfWriterOptions.set_max_dictionary_size(writerOptions->maxDictionarySize);
+ cudfWriterOptions.enable_int96_timestamps(
+ writerOptions->writeTimestampsAsInt96);
+
+ // Enable if enabled in the session or the writerOptions
+ cudfWriterOptions.enable_utc_timestamps(
+ parquetConfig_->writeTimestampsAsUTC() or
+ writerOptions->writeTimestampsAsUTC);
+ cudfWriterOptions.enable_write_arrow_schema(
+ parquetConfig_->writeArrowSchema() or writerOptions->writeArrowSchema);
+ cudfWriterOptions.enable_write_v2_headers(
+ parquetConfig_->writev2PageHeaders() or writerOptions->v2PageHeaders);
+ cudfWriterOptions.set_stats_level(writerOptions->statsLevel);
+
+ if (writerOptions->maxPageFragmentSize.has_value()) {
+ cudfWriterOptions.set_max_page_fragment_size(
+ writerOptions->maxPageFragmentSize.value());
+ }
+ // Get compression stats if needed
+ if (writerOptions->compressionStats != nullptr) {
+ cudfWriterOptions.set_compression_statistics(
+ writerOptions->compressionStats);
+ }
+ // Write sorting columns if available
+ if (sortingColumns_.empty()) {
+ cudfWriterOptions.set_sorting_columns(sortingColumns_);
+ }
+ }
+
+ return std::make_unique(cudfWriterOptions);
+}
+
+cudf::io::table_input_metadata ParquetDataSink::createCudfTableInputMetadata(
+ cudf::table_view cudfTable) {
+ auto tableInputMetadata = cudf::io::table_input_metadata(cudfTable);
+ auto inputColumns = insertTableHandle_->inputColumns();
+
+ // Check if equal number of columns in the input and
+ // ParquetInsertTableHandle
+ VELOX_CHECK_EQ(
+ tableInputMetadata.column_metadata.size(),
+ inputColumns.size(),
+ "Unequal number of columns in the input and ParquetInsertTableHandle");
+
+ std::function
+ setColumnName = [&](cudf::io::column_in_metadata& colMeta,
+ const ParquetColumnHandle& columnHandle) {
+ // Check if equal number of children
+ const auto& childrenHandles = columnHandle.children();
+
+ // Warn if the mismatch in the number of child cols in Parquet
+ // table_metadata and columnHandles
+ if (colMeta.num_children() != childrenHandles.size()) {
+ LOG(WARNING) << fmt::format(
+ "({} vs {}): Unequal number of child columns in Parquet table_metadata and ColumnHandles",
+ colMeta.num_children(),
+ childrenHandles.size());
+ }
+
+ // Set children's names
+ for (int32_t i = 0; i <
+ std::min(colMeta.num_children(), childrenHandles.size());
+ ++i) {
+ setColumnName(colMeta.child(i), childrenHandles[i]);
+ }
+ // Set this column's name
+ colMeta.set_name(columnHandle.name());
+ };
+
+ // Set names for all columns and their children
+ for (int32_t i = 0; i < tableInputMetadata.column_metadata.size(); ++i) {
+ setColumnName(tableInputMetadata.column_metadata[i], *inputColumns[i]);
+ }
+
+ return tableInputMetadata;
+}
+
+std::string ParquetDataSink::stateString(State state) {
+ switch (state) {
+ case State::kRunning:
+ return "RUNNING";
+ case State::kFinishing:
+ return "FLUSHING";
+ case State::kClosed:
+ return "CLOSED";
+ case State::kAborted:
+ return "ABORTED";
+ default:
+ VELOX_UNREACHABLE("BAD STATE: {}", static_cast(state));
+ }
+}
+
+DataSink::Stats ParquetDataSink::stats() const {
+ Stats stats;
+ if (state_ == State::kAborted) {
+ return stats;
+ }
+
+ int64_t numWrittenBytes{0};
+ int64_t writeIOTimeUs{0};
+
+ numWrittenBytes += ioStats_->rawBytesWritten();
+ writeIOTimeUs += ioStats_->writeIOTimeUs();
+
+ stats.numWrittenBytes = numWrittenBytes;
+ stats.writeIOTimeUs = writeIOTimeUs;
+
+ if (state_ != State::kClosed) {
+ return stats;
+ }
+
+ stats.numWrittenFiles = 1;
+ VELOX_CHECK_NOT_NULL(writerInfo_);
+ const auto spillStats = writerInfo_->spillStats->rlock();
+ if (!spillStats->empty()) {
+ stats.spillStats += *spillStats;
+ }
+
+ return stats;
+}
+
+void ParquetDataSink::setState(State newState) {
+ checkStateTransition(state_, newState);
+ state_ = newState;
+}
+
+/// Validates the state transition from 'oldState' to 'newState'.
+void ParquetDataSink::checkStateTransition(State oldState, State newState) {
+ switch (oldState) {
+ case State::kRunning:
+ if (newState == State::kAborted || newState == State::kFinishing) {
+ return;
+ }
+ break;
+ case State::kFinishing:
+ if (newState == State::kAborted || newState == State::kClosed ||
+ // The finishing state is reentry state if we yield in the
+ // middle of finish processing if a single run takes too long.
+ newState == State::kFinishing) {
+ return;
+ }
+ [[fallthrough]];
+ case State::kAborted:
+ case State::kClosed:
+ default:
+ break;
+ }
+ VELOX_FAIL("Unexpected state transition from {} to {}", oldState, newState);
+}
+
+bool ParquetDataSink::finish() {
+ VELOX_CHECK_NOT_NULL(writer_, "ParquetDataSink has no writer");
+
+ setState(State::kFinishing);
+ return true;
+}
+
+std::vector ParquetDataSink::close() {
+ setState(State::kClosed);
+ closeInternal();
+
+ std::vector partitionUpdates{};
+
+ partitionUpdates.reserve(1);
+ VELOX_CHECK_NOT_NULL(writerInfo_);
+ // clang-format off
+ auto partitionUpdateJson = folly::toJson(
+ folly::dynamic::object
+ ("writePath", writerInfo_->writerParameters.writeDirectory())
+ ("targetPath", writerInfo_->writerParameters.targetDirectory())
+ ("fileWriteInfos", folly::dynamic::array(
+ folly::dynamic::object
+ ("writeFileName", writerInfo_->writerParameters.writeFileName())
+ ("targetFileName", writerInfo_->writerParameters.targetFileName())
+ ("fileSize", ioStats_->rawBytesWritten())))
+ ("rowCount", writerInfo_->numWrittenRows)
+ ("inMemoryDataSizeInBytes", writerInfo_->inputSizeInBytes)
+ ("onDiskDataSizeInBytes", ioStats_->rawBytesWritten())
+ ("containsNumberedFileNames", true));
+ // clang-format on
+ partitionUpdates.emplace_back(partitionUpdateJson);
+
+ return partitionUpdates;
+}
+
+void ParquetDataSink::abort() {
+ setState(State::kAborted);
+ closeInternal();
+}
+
+void ParquetDataSink::closeInternal() {
+ VELOX_CHECK_NE(state_, State::kRunning);
+ VELOX_CHECK_NE(state_, State::kFinishing);
+ VELOX_CHECK_NOT_NULL(writer_, "ParquetDataSink has no writer");
+
+ TestValue::adjust(
+ "facebook::velox::connector::parquet::ParquetDataSink::closeInternal",
+ this);
+
+ // Close cudf writer
+ writer_->close();
+
+ // Reset the unique pointers to Cudf writer and options
+ writer_.reset();
+}
+
+std::shared_ptr ParquetDataSink::createWriterPool() {
+ auto* connectorPool = connectorQueryCtx_->connectorMemoryPool();
+ return connectorPool->addAggregateChild(
+ fmt::format("{}.{}", connectorPool->name(), "parquet-writer"));
+}
+
+void ParquetDataSink::makeWriterOptions(
+ ParquetWriterParameters writerParameters) {
+ auto writerPool = createWriterPool();
+ auto sinkPool = createSinkPool(writerPool);
+ std::shared_ptr sortPool{nullptr};
+ if (sortWrite()) {
+ sortPool = createSortPool(writerPool);
+ }
+
+ writerInfo_ = std::make_shared(
+ std::move(writerParameters),
+ std::move(writerPool),
+ std::move(sinkPool),
+ std::move(sortPool));
+
+ ioStats_ = std::make_shared();
+
+ // Take the writer options provided by the user as a starting point,
+ // or allocate a new one.
+ auto options = insertTableHandle_->writerOptions();
+ if (!options) {
+ options = std::make_unique();
+ }
+
+ const auto* connectorSessionProperties =
+ connectorQueryCtx_->sessionProperties();
+
+ if (options->memoryPool == nullptr) {
+ options->memoryPool = writerInfo_->writerPool.get();
+ }
+
+ if (!options->compressionKind) {
+ options->compressionKind = insertTableHandle_->compressionKind();
+ }
+
+ const auto& sessionTimeZoneName = connectorQueryCtx_->sessionTimezone();
+ if (!sessionTimeZoneName.empty()) {
+ options->sessionTimezoneName = sessionTimeZoneName;
+ }
+ options->adjustTimestampToTimezone =
+ connectorQueryCtx_->adjustTimestampToTimezone();
+}
+
+folly::dynamic ParquetInsertTableHandle::serialize() const {
+ folly::dynamic obj = folly::dynamic::object;
+ obj["name"] = "ParquetInsertTableHandle";
+ folly::dynamic arr = folly::dynamic::array;
+ for (const auto& ic : inputColumns_) {
+ arr.push_back(ic->serialize());
+ }
+
+ obj["inputColumns"] = arr;
+ obj["locationHandle"] = locationHandle_->serialize();
+ obj["tableStorageFormat"] = dwio::common::toString(storageFormat_);
+
+ if (compressionKind_.has_value()) {
+ obj["compressionKind"] = common::compressionKindToString(*compressionKind_);
+ }
+
+ return obj;
+}
+
+ParquetInsertTableHandlePtr ParquetInsertTableHandle::create(
+ const folly::dynamic& obj) {
+ auto inputColumns =
+ ISerializable::deserialize>(
+ obj["inputColumns"]);
+ auto locationHandle =
+ ISerializable::deserialize(obj["locationHandle"]);
+ std::optional compressionKind = std::nullopt;
+ if (obj.count("compressionKind") > 0) {
+ compressionKind =
+ common::stringToCompressionKind(obj["compressionKind"].asString());
+ }
+ std::unordered_map serdeParameters;
+ for (const auto& pair : obj["serdeParameters"].items()) {
+ serdeParameters.emplace(pair.first.asString(), pair.second.asString());
+ }
+ return std::make_shared(
+ inputColumns, locationHandle, compressionKind, serdeParameters);
+}
+
+std::string ParquetInsertTableHandle::toString() const {
+ std::ostringstream out;
+ out << "ParquetInsertTableHandle [" << dwio::common::toString(storageFormat_);
+ if (compressionKind_.has_value()) {
+ out << " " << common::compressionKindToString(compressionKind_.value());
+ } else {
+ out << " none";
+ }
+ out << "], [inputColumns: [";
+ for (const auto& i : inputColumns_) {
+ out << " " << i->toString();
+ }
+ out << " ], locationHandle: " << locationHandle_->toString();
+
+ out << "]";
+ return out.str();
+}
+
+void ParquetInsertTableHandle::registerSerDe() {
+ auto& registry = DeserializationRegistryForSharedPtr();
+ registry.Register("HiveInsertTableHandle", ParquetInsertTableHandle::create);
+}
+
+std::string LocationHandle::toString() const {
+ return fmt::format(
+ "LocationHandle [targetPath: {}, tableType: {},",
+ targetPath_,
+ tableTypeName(tableType_));
+}
+
+folly::dynamic LocationHandle::serialize() const {
+ folly::dynamic obj = folly::dynamic::object;
+ obj["name"] = "LocationHandle";
+ obj["targetPath"] = targetPath_;
+ obj["tableType"] = tableTypeName(tableType_);
+ return obj;
+}
+
+LocationHandlePtr LocationHandle::create(const folly::dynamic& obj) {
+ auto targetPath = obj["targetPath"].asString();
+ auto tableType = tableTypeFromName(obj["tableType"].asString());
+ return std::make_shared(targetPath, tableType);
+}
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetDataSink.h b/velox/experimental/cudf/connectors/parquet/ParquetDataSink.h
new file mode 100644
index 00000000000..f1dc47a8389
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetDataSink.h
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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.
+ */
+#pragma once
+
+#include "velox/experimental/cudf/connectors/parquet/ParquetConfig.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h"
+#include "velox/experimental/cudf/connectors/parquet/WriterOptions.h"
+
+#include "velox/common/compression/Compression.h"
+#include "velox/connectors/Connector.h"
+#include "velox/dwio/common/Options.h"
+#include "velox/dwio/common/Statistics.h"
+#include "velox/dwio/common/WriterFactory.h"
+#include "velox/exec/MemoryReclaimer.h"
+#include "velox/type/Type.h"
+
+#include
+#include
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+using namespace facebook::velox::connector;
+
+class LocationHandle;
+using LocationHandlePtr = std::shared_ptr;
+
+/// Location related properties of the Parquet table to be written.
+class LocationHandle : public ISerializable {
+ public:
+ enum class TableType {
+ /// Write to a new table to be created.
+ kNew,
+ };
+
+ LocationHandle(
+ std::string targetPath,
+ TableType tableType,
+ std::string targetFileName = "")
+ : targetPath_(std::move(targetPath)),
+ targetFileName_(std::move(targetFileName)),
+ tableType_(tableType) {}
+
+ const std::string& targetPath() const {
+ return targetPath_;
+ }
+
+ const std::string& targetFileName() const {
+ return targetFileName_;
+ }
+
+ TableType tableType() const {
+ return tableType_;
+ }
+
+ std::string toString() const;
+
+ static void registerSerDe();
+
+ folly::dynamic serialize() const override;
+
+ static LocationHandlePtr create(const folly::dynamic& obj);
+
+ static const std::string tableTypeName(LocationHandle::TableType type);
+
+ static LocationHandle::TableType tableTypeFromName(const std::string& name);
+
+ private:
+ // Target directory path.
+ const std::string targetPath_;
+ // If non-empty, use this name instead of generating our own.
+ const std::string targetFileName_;
+ // Whether the table to be written is new, already existing or temporary.
+ const TableType tableType_;
+};
+
+/// Parameters for Hive writers.
+class ParquetWriterParameters {
+ public:
+ enum class UpdateMode {
+ kNew, // Write files to a new directory.
+ };
+
+ /// @param updateMode Write the files to a new directory, or append to an
+ /// existing directory or overwrite an existing directory.
+ /// @param targetFileName The final name of a file after committing.
+ /// @param targetDirectory The final directory that a file should be in after
+ /// committing.
+ /// @param writeFileName The temporary name of the file that a running writer
+ /// writes to. If a running writer writes directory to the target file, set
+ /// writeFileName to targetFileName by default.
+ /// @param writeDirectory The temporary directory that a running writer writes
+ /// to. If a running writer writes directory to the target directory, set
+ /// writeDirectory to targetDirectory by default.
+ ParquetWriterParameters(
+ UpdateMode updateMode,
+ std::string targetFileName,
+ std::string targetDirectory,
+ std::optional writeFileName = std::nullopt,
+ std::optional writeDirectory = std::nullopt)
+ : updateMode_(updateMode),
+ targetFileName_(std::move(targetFileName)),
+ targetDirectory_(std::move(targetDirectory)),
+ writeFileName_(writeFileName.value_or(targetFileName_)),
+ writeDirectory_(writeDirectory.value_or(targetDirectory_)) {}
+
+ UpdateMode updateMode() const {
+ return updateMode_;
+ }
+
+ static std::string updateModeToString(UpdateMode updateMode) {
+ switch (updateMode) {
+ case UpdateMode::kNew:
+ return "NEW";
+ default:
+ VELOX_UNSUPPORTED("Unsupported update mode.");
+ }
+ }
+
+ const std::string& targetFileName() const {
+ return targetFileName_;
+ }
+
+ const std::string& writeFileName() const {
+ return writeFileName_;
+ }
+
+ const std::string& targetDirectory() const {
+ return targetDirectory_;
+ }
+
+ const std::string& writeDirectory() const {
+ return writeDirectory_;
+ }
+
+ private:
+ const UpdateMode updateMode_;
+ const std::optional partitionName_;
+ const std::string targetFileName_;
+ const std::string targetDirectory_;
+ const std::string writeFileName_;
+ const std::string writeDirectory_;
+};
+
+struct ParquetWriterInfo {
+ ParquetWriterInfo(
+ ParquetWriterParameters parameters,
+ std::shared_ptr _writerPool,
+ std::shared_ptr _sinkPool,
+ std::shared_ptr _sortPool)
+ : writerParameters(std::move(parameters)),
+ nonReclaimableSectionHolder(new tsan_atomic(false)),
+ spillStats(std::make_unique>()),
+ writerPool(std::move(_writerPool)),
+ sinkPool(std::move(_sinkPool)),
+ sortPool(std::move(_sortPool)) {}
+
+ const ParquetWriterParameters writerParameters;
+ const std::unique_ptr> nonReclaimableSectionHolder;
+ /// Collects the spill stats from sort writer if the spilling has been
+ /// triggered.
+ const std::unique_ptr> spillStats;
+ const std::shared_ptr writerPool;
+ const std::shared_ptr sinkPool;
+ const std::shared_ptr sortPool;
+ int64_t numWrittenRows = 0;
+ int64_t inputSizeInBytes = 0;
+};
+
+class ParquetInsertTableHandle;
+using ParquetInsertTableHandlePtr = std::shared_ptr;
+
+/// Represents a request for Parquet write.
+class ParquetInsertTableHandle : public ConnectorInsertTableHandle {
+ public:
+ ParquetInsertTableHandle(
+ std::vector> inputColumns,
+ std::shared_ptr locationHandle,
+ std::optional compressionKind = {},
+ const std::unordered_map& serdeParameters = {},
+ const std::shared_ptr& writerOptions =
+ nullptr)
+ : inputColumns_(std::move(inputColumns)),
+ locationHandle_(std::move(locationHandle)),
+ compressionKind_(compressionKind),
+ serdeParameters_(serdeParameters),
+ writerOptions_(writerOptions) {
+ if (compressionKind.has_value()) {
+ VELOX_CHECK(
+ compressionKind.value() != common::CompressionKind_MAX,
+ "Unsupported compression type: CompressionKind_MAX");
+ VELOX_CHECK(
+ compressionKind.value() == common::CompressionKind_NONE or
+ compressionKind.value() == common::CompressionKind_SNAPPY or
+ compressionKind.value() == common::CompressionKind_LZ4 or
+ compressionKind.value() == common::CompressionKind_ZSTD,
+ "Parquet DataSink only supports NONE, SNAPPY, LZ4, and ZSTD compressions.");
+ }
+ }
+
+ virtual ~ParquetInsertTableHandle() = default;
+
+ const std::vector>& inputColumns()
+ const {
+ return inputColumns_;
+ }
+
+ const std::shared_ptr& locationHandle() const {
+ return locationHandle_;
+ }
+
+ std::optional compressionKind() const {
+ return compressionKind_;
+ }
+
+ const dwio::common::FileFormat storageFormat() const {
+ return storageFormat_;
+ }
+
+ const std::unordered_map& serdeParameters() const {
+ return serdeParameters_;
+ }
+
+ const std::shared_ptr& writerOptions() const {
+ return writerOptions_;
+ }
+
+ bool supportsMultiThreading() const override {
+ return true; // TODO: Needs more testing if this is ok
+ }
+
+ bool isExistingTable() const {
+ return false; // This is always false as cudf's Parquet writer doesn't yet
+ // support updating existing Parquet files
+ }
+
+ folly::dynamic serialize() const override;
+
+ static ParquetInsertTableHandlePtr create(const folly::dynamic& obj);
+
+ static void registerSerDe();
+
+ std::string toString() const override;
+
+ private:
+ const std::vector> inputColumns_;
+ const std::shared_ptr locationHandle_;
+ const std::optional compressionKind_;
+ const dwio::common::FileFormat storageFormat_ =
+ dwio::common::FileFormat::PARQUET;
+ const std::unordered_map serdeParameters_;
+ const std::shared_ptr writerOptions_;
+};
+
+class ParquetDataSink : public DataSink {
+ public:
+ /// The list of runtime stats reported by parquet data sink
+ static constexpr const char* kEarlyFlushedRawBytes = "earlyFlushedRawBytes";
+
+ /// Defines the execution states of a parquet data sink running internally.
+ enum class State {
+ /// The data sink accepts new append data in this state.
+ kRunning = 0,
+ /// The data sink flushes any buffered data to the underlying file writer
+ /// but no more data can be appended.
+ kFinishing = 1,
+ /// The data sink is aborted on error and no more data can be appended.
+ kAborted = 2,
+ /// The data sink is closed on error and no more data can be appended.
+ kClosed = 3
+ };
+ static std::string stateString(State state);
+
+ ParquetDataSink(
+ RowTypePtr inputType,
+ std::shared_ptr insertTableHandle,
+ const ConnectorQueryCtx* connectorQueryCtx,
+ CommitStrategy commitStrategy,
+ const std::shared_ptr& parquetConfig);
+
+ void appendData(RowVectorPtr input) override;
+
+ bool finish() override;
+
+ Stats stats() const override;
+
+ std::vector close() override;
+
+ void abort() override;
+
+ bool canReclaim() const {
+ return false;
+ };
+
+ private:
+ // Creates a new cudf chunked parquet writer.
+ std::unique_ptr createCudfWriter(
+ cudf::table_view cudfTable);
+ cudf::io::table_input_metadata createCudfTableInputMetadata(
+ cudf::table_view cudfTable);
+
+ // Validates the state transition from 'oldState' to 'newState'.
+ void checkStateTransition(State oldState, State newState);
+ void setState(State newState);
+
+ std::shared_ptr createWriterPool();
+
+ FOLLY_ALWAYS_INLINE bool sortWrite() const {
+ return not sortingColumns_.empty();
+ }
+
+ FOLLY_ALWAYS_INLINE bool isCommitRequired() const {
+ return false; // Since we always immediately write
+ }
+
+ FOLLY_ALWAYS_INLINE void checkRunning() const {
+ VELOX_CHECK_EQ(state_, State::kRunning, "Parquet data sink is not running");
+ }
+
+ void closeInternal();
+ void makeWriterOptions(ParquetWriterParameters writerParameters);
+
+ const RowTypePtr inputType_;
+ const std::shared_ptr insertTableHandle_;
+ const ConnectorQueryCtx* const connectorQueryCtx_;
+ const CommitStrategy commitStrategy_;
+ const std::shared_ptr parquetConfig_;
+ const common::SpillConfig* const spillConfig_;
+ const uint64_t sortWriterFinishTimeSliceLimitMs_{0};
+ State state_{State::kRunning};
+
+ // Below are structures for partitions from all inputs. writerInfo_ and
+ // writers_ are both indexed by partitionId.
+ std::unique_ptr writer_;
+
+ std::vector sortingColumns_;
+
+ std::shared_ptr writerInfo_;
+
+ // IO statistics collected for writer.
+ std::shared_ptr ioStats_;
+};
+
+FOLLY_ALWAYS_INLINE std::ostream& operator<<(
+ std::ostream& os,
+ ParquetDataSink::State state) {
+ os << ParquetDataSink::stateString(state);
+ return os;
+}
+} // namespace facebook::velox::cudf_velox::connector::parquet
+
+template <>
+struct fmt::formatter<
+ facebook::velox::cudf_velox::connector::parquet::ParquetDataSink::State>
+ : formatter {
+ auto format(
+ facebook::velox::cudf_velox::connector::parquet::ParquetDataSink::State s,
+ format_context& ctx) const {
+ return formatter::format(
+ facebook::velox::cudf_velox::connector::parquet::ParquetDataSink::
+ stateString(s),
+ ctx);
+ }
+};
+
+template <>
+struct fmt::formatter<
+ facebook::velox::cudf_velox::connector::parquet::LocationHandle::TableType>
+ : formatter {
+ auto format(
+ facebook::velox::cudf_velox::connector::parquet::LocationHandle::TableType
+ s,
+ format_context& ctx) const {
+ return formatter::format(static_cast(s), ctx);
+ }
+};
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetDataSource.cpp b/velox/experimental/cudf/connectors/parquet/ParquetDataSource.cpp
new file mode 100644
index 00000000000..57da2d68bd9
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetDataSource.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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 "velox/experimental/cudf/connectors/parquet/ParquetConfig.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetDataSource.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h"
+#include "velox/experimental/cudf/exec/ToCudf.h"
+#include "velox/experimental/cudf/exec/Utilities.h"
+#include "velox/experimental/cudf/exec/VeloxCudfInterop.h"
+#include "velox/experimental/cudf/vector/CudfVector.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+using namespace facebook::velox::connector;
+
+ParquetDataSource::ParquetDataSource(
+ const std::shared_ptr& outputType,
+ const std::shared_ptr& tableHandle,
+ const std::unordered_map>&
+ columnHandles,
+ folly::Executor* executor,
+ const ConnectorQueryCtx* connectorQueryCtx,
+ const std::shared_ptr& parquetConfig)
+ : NvtxHelper(
+ nvtx3::rgb{80, 171, 241}, // Parquet blue,
+ std::nullopt,
+ fmt::format("[{}]", tableHandle->name())),
+ parquetConfig_(parquetConfig),
+ executor_(executor),
+ connectorQueryCtx_(connectorQueryCtx),
+ pool_(connectorQueryCtx->memoryPool()),
+ outputType_(outputType),
+ expressionEvaluator_(connectorQueryCtx->expressionEvaluator()) {
+ // Set up column projection if needed
+ auto readColumnTypes = outputType_->children();
+ for (const auto& outputName : outputType_->names()) {
+ auto it = columnHandles.find(outputName);
+ VELOX_CHECK(
+ it != columnHandles.end(),
+ "ColumnHandle is missing for output column: {}",
+ outputName);
+
+ auto* handle = static_cast(it->second.get());
+ readColumnNames_.emplace_back(handle->name());
+ }
+
+ // Dynamic cast tableHandle to ParquetTableHandle
+ tableHandle_ = std::dynamic_pointer_cast(tableHandle);
+ VELOX_CHECK_NOT_NULL(
+ tableHandle_, "TableHandle must be an instance of ParquetTableHandle");
+
+ // Create empty IOStats for later use
+ ioStats_ = std::make_shared();
+
+ // Create subfield filter
+ auto subfieldFilter = tableHandle_->subfieldFilterExpr();
+ if (subfieldFilter) {
+ subfieldFilterExprSet_ = expressionEvaluator_->compile(subfieldFilter);
+ }
+
+ // Create remaining filter
+ auto remainingFilter = tableHandle_->remainingFilter();
+ if (remainingFilter) {
+ remainingFilterExprSet_ = expressionEvaluator_->compile(remainingFilter);
+ cudfExpressionEvaluator_ = velox::cudf_velox::ExpressionEvaluator(
+ remainingFilterExprSet_->exprs(), outputType_);
+ // TODO(kn): Get column names and subfields from remaining filter and add to
+ // readColumnNames_
+ }
+}
+
+std::optional ParquetDataSource::next(
+ uint64_t /*size*/,
+ velox::ContinueFuture& /* future */) {
+ VELOX_NVTX_OPERATOR_FUNC_RANGE();
+ // Basic sanity checks
+ VELOX_CHECK_NOT_NULL(split_, "No split to process. Call addSplit first.");
+ VELOX_CHECK_NOT_NULL(splitReader_, "No split reader present");
+
+ if (not splitReader_->has_next()) {
+ return nullptr;
+ }
+
+ // Read a table chunk
+ auto [table, metadata] = splitReader_->read_chunk();
+ auto cudfTable = std::move(table);
+ // Fill in the column names if reading the first chunk.
+ if (columnNames_.empty()) {
+ for (auto schema : metadata.schema_info) {
+ columnNames_.emplace_back(schema.name);
+ }
+ }
+
+ // Apply remaining filter if present
+ if (remainingFilterExprSet_) {
+ auto cudfTableColumns = cudfTable->release();
+ const auto originalNumColumns = cudfTableColumns.size();
+ // Filter may need addtional computed columns which are added to
+ // cudfTableColumns
+ auto filterResult = cudfExpressionEvaluator_.compute(
+ cudfTableColumns, stream_, cudf::get_current_device_resource_ref());
+ // discard computed columns
+ std::vector> originalColumns;
+ originalColumns.reserve(originalNumColumns);
+ std::move(
+ cudfTableColumns.begin(),
+ cudfTableColumns.begin() + originalNumColumns,
+ std::back_inserter(originalColumns));
+ auto originalTable =
+ std::make_unique(std::move(originalColumns));
+ // Keep only rows where the filter is true
+ cudfTable = cudf::apply_boolean_mask(
+ *originalTable,
+ *filterResult[0],
+ stream_,
+ cudf::get_current_device_resource_ref());
+ }
+
+ // Output RowVectorPtr
+ const auto nRows = cudfTable->num_rows();
+
+ // keep only outputType_.size() columns in cudfTable_
+ if (outputType_->size() < cudfTable->num_columns()) {
+ auto cudfTableColumns = cudfTable->release();
+ std::vector> originalColumns;
+ originalColumns.reserve(outputType_->size());
+ std::move(
+ cudfTableColumns.begin(),
+ cudfTableColumns.begin() + outputType_->size(),
+ std::back_inserter(originalColumns));
+ cudfTable = std::make_unique(std::move(originalColumns));
+ }
+
+ auto output = cudfIsRegistered()
+ ? std::make_shared(
+ pool_, outputType_, nRows, std::move(cudfTable), stream_)
+ : with_arrow::toVeloxColumn(
+ cudfTable->view(), pool_, outputType_->names(), stream_);
+ stream_.synchronize();
+
+ // Check if conversion yielded a nullptr
+ VELOX_CHECK_NOT_NULL(output, "Cudf to Velox conversion yielded a nullptr");
+
+ // Update completedRows_.
+ completedRows_ += output->size();
+
+ // TODO: Update `completedBytes_` here instead of in `addSplit()`
+
+ return output;
+}
+
+void ParquetDataSource::addSplit(std::shared_ptr split) {
+ // Dynamic cast split to `ParquetConnectorSplit`
+ split_ = std::dynamic_pointer_cast(split);
+ VLOG(1) << "Adding split " << split_->toString();
+
+ // Split reader already exists, reset
+ if (splitReader_) {
+ splitReader_.reset();
+ }
+
+ // Clear columnNames if not empty
+ if (not columnNames_.empty()) {
+ columnNames_.clear();
+ }
+
+ // Create a `cudf::io::chunked_parquet_reader` SplitReader
+ splitReader_ = createSplitReader();
+
+ // TODO: `completedBytes_` should be updated in `next()` as we read more and
+ // more table bytes
+ const auto& filePaths = split_->getCudfSourceInfo().filepaths();
+ for (const auto& filePath : filePaths) {
+ completedBytes_ += std::filesystem::file_size(filePath);
+ }
+}
+
+std::unique_ptr
+ParquetDataSource::createSplitReader() {
+ // Reader options
+ auto readerOptions =
+ cudf::io::parquet_reader_options::builder(split_->getCudfSourceInfo())
+ .skip_rows(parquetConfig_->skipRows())
+ .use_pandas_metadata(parquetConfig_->isUsePandasMetadata())
+ .use_arrow_schema(parquetConfig_->isUseArrowSchema())
+ .allow_mismatched_pq_schemas(
+ parquetConfig_->isAllowMismatchedParquetSchemas())
+ .timestamp_type(parquetConfig_->timestampType())
+ .build();
+
+ // Set num_rows only if available
+ if (parquetConfig_->numRows().has_value()) {
+ readerOptions.set_num_rows(parquetConfig_->numRows().value());
+ }
+
+ // Set column projection if needed
+ if (readColumnNames_.size()) {
+ readerOptions.set_columns(readColumnNames_);
+ }
+ if (subfieldFilterExprSet_) {
+ auto subfieldFilterExpr = subfieldFilterExprSet_->expr(0);
+ // non-ast instructions in filter is not supported for SubFieldFilter.
+ // precomputeInstructions which are non-ast instructions should be empty.
+ std::vector precomputeInstructions;
+ createAstTree(
+ subfieldFilterExpr,
+ subfieldTree_,
+ subfieldScalars_,
+ outputType_,
+ precomputeInstructions);
+ VELOX_CHECK_EQ(precomputeInstructions.size(), 0);
+ readerOptions.set_filter(subfieldTree_.back());
+ }
+ stream_ = cudfGlobalStreamPool().get_stream();
+ // Create a parquet reader
+ return std::make_unique(
+ parquetConfig_->maxChunkReadLimit(),
+ parquetConfig_->maxPassReadLimit(),
+ readerOptions,
+ stream_,
+ cudf::get_current_device_resource_ref());
+}
+
+void ParquetDataSource::resetSplit() {
+ split_.reset();
+ splitReader_.reset();
+ columnNames_.clear();
+}
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetDataSource.h b/velox/experimental/cudf/connectors/parquet/ParquetDataSource.h
new file mode 100644
index 00000000000..4de52278da4
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetDataSource.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "velox/experimental/cudf/connectors/parquet/ParquetConfig.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetConnectorSplit.h"
+#include "velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h"
+#include "velox/experimental/cudf/exec/ExpressionEvaluator.h"
+#include "velox/experimental/cudf/exec/NvtxHelper.h"
+
+#include "velox/common/base/RandomUtil.h"
+#include "velox/common/io/IoStatistics.h"
+#include "velox/connectors/Connector.h"
+#include "velox/dwio/common/Statistics.h"
+#include "velox/type/Type.h"
+
+#include
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+using namespace facebook::velox::connector;
+
+class ParquetDataSource : public DataSource, public NvtxHelper {
+ public:
+ ParquetDataSource(
+ const std::shared_ptr& outputType,
+ const std::shared_ptr& tableHandle,
+ const std::unordered_map>&
+ columnHandles,
+ folly::Executor* executor,
+ const ConnectorQueryCtx* connectorQueryCtx,
+ const std::shared_ptr& ParquetConfig);
+
+ void addSplit(std::shared_ptr split) override;
+
+ void addDynamicFilter(
+ column_index_t /*outputChannel*/,
+ const std::shared_ptr& /*filter*/)
+ override {
+ VELOX_NYI("Dynamic filters not yet implemented by cudf::ParquetConnector.");
+ }
+
+ std::optional next(
+ uint64_t size,
+ velox::ContinueFuture& /* future */) override;
+
+ uint64_t getCompletedRows() override {
+ return completedRows_;
+ }
+
+ uint64_t getCompletedBytes() override {
+ return completedBytes_;
+ }
+
+ std::unordered_map runtimeStats() override {
+ // TODO: Which stats do we want to expose here?
+ return {};
+ }
+
+ private:
+ // Create a cudf::io::chunked_parquet_reader with the given split.
+ std::unique_ptr createSplitReader();
+ // Clear split_ and splitReader after split has been fully processed. Keep
+ // readers around to hold adaptation.
+ void resetSplit();
+ // Clear cudfTable_ and currentCudfTableView_ once we have successfully
+ // converted it to `RowVectorPtr` and returned.
+ void resetCudfTableAndView();
+ const RowVectorPtr& getEmptyOutput() {
+ if (!emptyOutput_) {
+ emptyOutput_ = RowVector::createEmpty(outputType_, pool_);
+ }
+ return emptyOutput_;
+ }
+ RowVectorPtr emptyOutput_;
+
+ std::shared_ptr split_;
+ std::shared_ptr tableHandle_;
+
+ const std::shared_ptr parquetConfig_;
+
+ folly::Executor* const executor_;
+ const ConnectorQueryCtx* const connectorQueryCtx_;
+
+ memory::MemoryPool* const pool_;
+
+ // cuDF Parquet reader stuff.
+ cudf::io::parquet_reader_options readerOptions_;
+ std::unique_ptr splitReader_;
+ rmm::cuda_stream_view stream_;
+
+ // Table column names read from the Parquet file
+ std::vector columnNames_;
+
+ // Output type from file reader. This is different from outputType_ that it
+ // contains column names before assignment, and columns that only used in
+ // remaining filter.
+ RowTypePtr readerOutputType_;
+
+ // Columns to read.
+ std::vector readColumnNames_;
+
+ std::shared_ptr ioStats_;
+
+ size_t completedRows_{0};
+ size_t completedBytes_{0};
+
+ // The row type for the data source output, not including filter-only columns
+ const RowTypePtr outputType_;
+
+ // Expression evaluator for remaining filter.
+ core::ExpressionEvaluator* const expressionEvaluator_;
+ std::unique_ptr remainingFilterExprSet_;
+ velox::cudf_velox::ExpressionEvaluator cudfExpressionEvaluator_;
+
+ // Expression evaluator for subfield filter.
+ std::vector> subfieldScalars_;
+ cudf::ast::tree subfieldTree_;
+ std::unique_ptr subfieldFilterExprSet_;
+
+ dwio::common::RuntimeStatistics runtimeStats_;
+};
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetTableHandle.cpp b/velox/experimental/cudf/connectors/parquet/ParquetTableHandle.cpp
new file mode 100644
index 00000000000..dcb51868516
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetTableHandle.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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 "velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h"
+
+#include "velox/connectors/Connector.h"
+#include "velox/type/Type.h"
+
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+using namespace facebook::velox::connector;
+
+std::string ParquetColumnHandle::toString() const {
+ std::ostringstream out;
+ out << fmt::format(
+ "ParquetColumnHandle [name: {}, Type: {},", name_, type_->toString());
+ return out.str();
+}
+
+ParquetTableHandle::ParquetTableHandle(
+ std::string connectorId,
+ const std::string& tableName,
+ bool filterPushdownEnabled,
+ const core::TypedExprPtr& subfieldFilterExpr,
+ const core::TypedExprPtr& remainingFilter,
+ const RowTypePtr& dataColumns)
+ : ConnectorTableHandle(std::move(connectorId)),
+ tableName_(tableName),
+ filterPushdownEnabled_(filterPushdownEnabled),
+ subfieldFilterExpr_(subfieldFilterExpr),
+ remainingFilter_(remainingFilter),
+ dataColumns_(dataColumns) {}
+
+std::string ParquetTableHandle::toString() const {
+ std::stringstream out;
+ out << "table: " << tableName_;
+ if (dataColumns_) {
+ out << ", data columns: " << dataColumns_->toString();
+ }
+ return out.str();
+}
+
+ConnectorTableHandlePtr ParquetTableHandle::create(
+ const folly::dynamic& obj,
+ void* context) {
+ VELOX_NYI("ParquetTableHandle::create() not yet implemented");
+}
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h b/velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h
new file mode 100644
index 00000000000..b8e1fae7b88
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/ParquetTableHandle.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "velox/connectors/Connector.h"
+#include "velox/core/Expressions.h"
+#include "velox/expression/Expr.h"
+#include "velox/type/Type.h"
+
+#include
+
+#include
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+using namespace facebook::velox::connector;
+
+// Parquet column handle only needs the column name (all columns are generated
+// in the same way).
+class ParquetColumnHandle : public ColumnHandle {
+ public:
+ explicit ParquetColumnHandle(
+ const std::string& name,
+ const TypePtr type,
+ const cudf::data_type cudfDataType,
+ std::vector children = {})
+ : name_(name),
+ type_(type),
+ cudfDataType_(cudfDataType),
+ children_(std::move(children)) {}
+
+ const std::string& name() const {
+ return name_;
+ }
+
+ const TypePtr& type() const {
+ return type_;
+ }
+
+ const cudf::data_type cudfDataType() const {
+ return cudfDataType_;
+ }
+
+ const std::vector& children() const {
+ return children_;
+ }
+
+ std::string toString() const;
+
+ private:
+ const std::string name_;
+ const TypePtr type_;
+ const cudf::data_type cudfDataType_;
+ const std::vector children_;
+};
+
+class ParquetTableHandle : public ConnectorTableHandle {
+ public:
+ ParquetTableHandle(
+ std::string connectorId,
+ const std::string& tableName,
+ bool filterPushdownEnabled,
+ const core::TypedExprPtr& subfieldFilterExpr,
+ const core::TypedExprPtr& remainingFilter = nullptr,
+ const RowTypePtr& dataColumns = nullptr);
+
+ const std::string& name() const override {
+ return tableName_;
+ }
+
+ bool isFilterPushdownEnabled() const {
+ return filterPushdownEnabled_;
+ }
+
+ const core::TypedExprPtr& subfieldFilterExpr() const {
+ return subfieldFilterExpr_;
+ }
+
+ const core::TypedExprPtr& remainingFilter() const {
+ return remainingFilter_;
+ }
+
+ // Schema of the table. Need this for reading TEXTFILE.
+ const RowTypePtr& dataColumns() const {
+ return dataColumns_;
+ }
+
+ std::string toString() const override;
+
+ static ConnectorTableHandlePtr create(
+ const folly::dynamic& obj,
+ void* context);
+
+ private:
+ const std::string tableName_;
+ const bool filterPushdownEnabled_;
+ // This expression is used for predicate pushdown.
+ const core::TypedExprPtr subfieldFilterExpr_;
+ // This expression is used for post-scan filtering.
+ const core::TypedExprPtr remainingFilter_;
+ const RowTypePtr dataColumns_;
+};
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/connectors/parquet/WriterOptions.h b/velox/experimental/cudf/connectors/parquet/WriterOptions.h
new file mode 100644
index 00000000000..09d9527ddae
--- /dev/null
+++ b/velox/experimental/cudf/connectors/parquet/WriterOptions.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "velox/dwio/common/Options.h"
+
+#include
+#include
+#include
+
+#include
+
+namespace facebook::velox::cudf_velox::connector::parquet {
+
+using namespace cudf::io;
+
+/**
+ * @brief Struct to 1:1 correspond with cudf::io::chunked_parquet_reader_options
+ * except sink_info and a few others which are provided to the ParquetDataSink
+ * from elsewhere.
+ */
+struct ParquetWriterOptions
+ : public facebook::velox::dwio::common::WriterOptions {
+ // Specify the level of statistics in the output file
+ statistics_freq statsLevel = statistics_freq::STATISTICS_ROWGROUP;
+
+ // Parquet writer can write INT96 or TIMESTAMP_MICROS. Defaults to
+ // TIMESTAMPMICROS. If true then overrides any per-column setting in
+ // Metadata.
+ bool writeTimestampsAsInt96 = false;
+
+ // Parquet writer can write timestamps as UTC
+ // Defaults to true because libcudf timestamps are implicitly UTC
+ bool writeTimestampsAsUTC = true;
+
+ // Whether to write ARROW schema
+ bool writeArrowSchema = false;
+
+ // Maximum size of each row group (unless smaller than a single page)
+ size_t rowGroupSizeBytes = default_row_group_size_bytes;
+
+ // Maximum number of rows in row group (unless smaller than a single page)
+ cudf::size_type rowGroupSizeRows = default_row_group_size_rows;
+
+ // Maximum size of each page (uncompressed) - Velox uses 1KB (2 x cudf limit)
+ size_t maxPageSizeBytes = 2 * default_max_page_size_bytes;
+
+ // Maximum number of rows in a page
+ cudf::size_type maxPageSizeRows = default_max_page_size_rows;
+
+ // Maximum size of min or max values in column index
+ int32_t columnIndexTruncateLength = default_column_index_truncate_length;
+
+ // When to use dictionary encoding for data
+ dictionary_policy dictionaryPolicy = dictionary_policy::ADAPTIVE;
+
+ // Maximum size of column chunk dictionary (in bytes)
+ size_t maxDictionarySize = default_max_dictionary_size;
+
+ // Maximum number of rows in a page fragment
+ std::optional maxPageFragmentSize;
+
+ // Optional compression statistics
+ std::shared_ptr compressionStats;
+
+ // Write V2 page headers?
+ bool v2PageHeaders = false;
+
+ // Encoding to use for columns
+ column_encoding encoding = column_encoding::PLAIN;
+
+ // Sorting columns
+ std::vector sortingColumns;
+};
+
+} // namespace facebook::velox::cudf_velox::connector::parquet
diff --git a/velox/experimental/cudf/exec/CMakeLists.txt b/velox/experimental/cudf/exec/CMakeLists.txt
index c5c2711f812..7d1ca8b7557 100644
--- a/velox/experimental/cudf/exec/CMakeLists.txt
+++ b/velox/experimental/cudf/exec/CMakeLists.txt
@@ -15,19 +15,28 @@
add_library(
velox_cudf_exec
CudfConversion.cpp
+ CudfFilterProject.cpp
CudfHashAggregation.cpp
+ CudfHashJoin.cpp
+ CudfLimit.cpp
+ CudfLocalPartition.cpp
CudfOrderBy.cpp
+ DebugUtil.cpp
+ ExpressionEvaluator.cpp
+ PrestoAggregates.cpp
ToCudf.cpp
Utilities.cpp
VeloxCudfInterop.cpp)
target_link_libraries(
velox_cudf_exec
- cudf::cudf
- arrow
- velox_arrow_bridge
- velox_exception
- velox_common_base
- velox_exec)
+ PUBLIC cudf::cudf
+ PRIVATE
+ arrow
+ velox_arrow_bridge
+ velox_exception
+ velox_common_base
+ velox_cudf_vector
+ velox_exec)
target_compile_options(velox_cudf_exec PRIVATE -Wno-missing-field-initializers)
diff --git a/velox/experimental/cudf/exec/CudfConversion.cpp b/velox/experimental/cudf/exec/CudfConversion.cpp
index 903b01fe814..09c8f4774fd 100644
--- a/velox/experimental/cudf/exec/CudfConversion.cpp
+++ b/velox/experimental/cudf/exec/CudfConversion.cpp
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#include "velox/experimental/cudf/exec/CudfConversion.h"
#include "velox/experimental/cudf/exec/NvtxHelper.h"
+#include "velox/experimental/cudf/exec/ToCudf.h"
#include "velox/experimental/cudf/exec/Utilities.h"
#include "velox/experimental/cudf/exec/VeloxCudfInterop.h"
#include "velox/experimental/cudf/vector/CudfVector.h"
@@ -23,7 +25,7 @@
#include "velox/exec/Operator.h"
#include "velox/vector/ComplexVector.h"
-#include
+#include
#include
#include
@@ -172,6 +174,11 @@ CudfToVelox::CudfToVelox(
operatorId,
fmt::format("[{}]", planNodeId)) {}
+bool CudfToVelox::isPassthroughMode() const {
+ return operatorCtx_->driverCtx()->queryConfig().get(
+ kPassthroughMode, true);
+}
+
void CudfToVelox::addInput(RowVectorPtr input) {
// Accumulate inputs
if (input->size() > 0) {
@@ -181,6 +188,14 @@ void CudfToVelox::addInput(RowVectorPtr input) {
}
}
+std::optional CudfToVelox::averageRowSize() {
+ if (!averageRowSize_) {
+ averageRowSize_ =
+ inputs_.front()->estimateFlatSize() / inputs_.front()->size();
+ }
+ return averageRowSize_;
+}
+
RowVectorPtr CudfToVelox::getOutput() {
VELOX_NVTX_OPERATOR_FUNC_RANGE();
if (finished_ || inputs_.empty()) {
@@ -188,16 +203,97 @@ RowVectorPtr CudfToVelox::getOutput() {
return nullptr;
}
+ // Get the target batch size
+ const auto targetBatchSize = outputBatchRows(averageRowSize());
auto stream = inputs_.front()->stream();
- std::unique_ptr