nvidia/udf-convert-to-cuda
Assists with converting a non-aggregating Apache Spark UDF to a native CUDA RapidsUDF using JNI and libcudf. This is step 2 of 3 in the UDF conversion workflow (udf-gen-test -> udf-convert-to-cuda -> udf-benchmark). Use this skill when you have a CPU UDF with a unit test and need to convert it to a native CUDA implementation. Prefer udf-convert-to-cudf unless a CUDA implementation is necessary for performance or correctness, or if requested by the user.
npx skills add https://github.com/NVIDIA/cudf-spark --skill udf-convert-to-cuda
cuda-native-udf Maven profileBefore making any edits, create a visible TODO checklist for every workflow step in this skill and keep it updated. Do not produce a final answer until every required checklist item is marked complete.
udf-gen-test) with a passing unit testgit, and unzipDerive <CamelName> and <snake_name> from the UDF class name.
> Note: Commands require access to /tmp (Spark temp storage) and /dev (GPU device). If commands fail due to sandbox restrictions, re-run them unsandboxed.
Copy this skill's CUDA templates into the existing project:
cp -r templates/cuda/* <project_root>/<CamelName>/
chmod +x <project_root>/<CamelName>/native/scripts/extract-cudf-libs.sh
The udf-gen-test Maven template already contains an inactive cuda-native-udf profile. The native profile is activated only when you build with -Pcuda-native-udf.
Read NATIVE_BUILD_ENV.md before changing build configuration.
Read examples/ for native RapidsUDF examples.
Use src/main/java/com/udf/PlaceholderUDFNameNativeRapidsUDF.java as a starting point:
<CamelName>NativeRapidsUDF.java.<CamelName>NativeRapidsUDF.evaluateColumnar to validate column count/types and call the native method.cosineSimilarityNative.The project joint-compiles Java, so keep this Java wrapper under src/main/java/com/udf/ and register it from the Scala test. JNI can be used from Scala, but the Java wrapper keeps native symbol names and examples simpler.
If the Java wrapper's CPU fallback needs to call a Scala object, direct references can fail before scala-maven-plugin compiles the Scala classes; use reflection in the row-by-row fallback only, and keep evaluateColumnar on the normal JNI path.
Read JNI_CUDA_GUIDE.md for the evaluateColumnar contract, type mapping, pointer ownership, NativeDepsLoader, and native memory rules.
Note: memory allocations must use the active RMM resource; avoid direct usage of ad hoc CUDA or Thrust allocators.
Rename and edit:
native/src/main/cpp/src/PlaceholderUDFNameJni.cppnative/src/main/cpp/src/placeholder_udf_name.cunative/src/main/cpp/src/placeholder_udf_name.hppUpdate native/src/main/cpp/CMakeLists.txt SOURCE_FILES to match the renamed files. If libcudf ABI/version compatibility is unclear, defer to the user.
Read JNI_CUDA_GUIDE.md before writing kernels.
Verify cuDF header names before choosing includes or APIs. After dependency extraction, the active header tree will be cloned under target/cudf-repo/cpp/include.
copyToHost() or native methods that copy inputs from GPU to CPU. This defeats the purpose of GPU accelerationThe native Maven profile uses the RAPIDS dependency already declared in pom.xml.
mvn package -Pcuda-native-udf -DskipTests
To use the Docker build environment:
docker build -t cuda-udf-build .
mkdir -p "$HOME/.m2"
docker run --rm --gpus all \
--user "$(id -u):$(id -g)" \
-e HOME=/workspace \
-v "$PWD":/workspace \
-v "$HOME/.m2":/workspace/.m2 \
-w /workspace \
cuda-udf-build \
-c "mvn -B -Dmaven.repo.local=/workspace/.m2/repository package -Pcuda-native-udf -DskipTests -Dnative.build.path=/workspace/target/native-build-docker"
If the build fails while resolving cuDF headers or RAPIDS CMake, check network access and the generated cudf.git.branch / rapids.cmake.branch properties. These properties may contain either a branch or a tag.
Fill in the target-specific TODOs in src/test/scala/com/udf/CudfComparisonTest.scala:
<CamelName>NativeRapidsUDF as the GPU implementationRun:
mvn test -Dsuites=com.udf.CudfComparisonTest -Pcuda-native-udf
To run the tests inside the Docker build environment:
docker run --rm --gpus all \
--user "$(id -u):$(id -g)" \
-e HOME=/workspace \
-v "$PWD":/workspace \
-v "$HOME/.m2":/workspace/.m2 \
-v /etc/passwd:/etc/passwd:ro \
-v /etc/group:/etc/group:ro \
-w /workspace \
cuda-udf-build \
-c "mvn -B -Dmaven.repo.local=/workspace/.m2/repository test -Dsuites=com.udf.CudfComparisonTest -Pcuda-native-udf -Dnative.build.path=/workspace/target/native-build-docker -DskipCudfExtraction=true"
If tests fail, iterate on the Java bridge or native implementation.
Treat the unit test as the CPU behavior specification. Do not weaken or remove test cases silently.
TODO/NOTE in the implementation explaining the mismatch.If the user explicitly asked for the judge, a judge subagent, or a review agent, treat that as an explicit request for delegation: you MUST launch a separate subagent with model: inherit and instruct it to use the udf-judge-conversion skill. Ask it to review the UnitTest, CudfComparisonTest, Java bridge, and JNI/CUDA sources.
If the user did not request a judge/review agent, mark this step as skipped and continue to Step 7. If a required judge subagent is blocked by tool policy, stop and tell the user that explicit permission/instruction is needed.
If you run the judge, wait for it to complete and review its report. If the judge finds any issues, 1) fix the issues, 2) re-run the tests, and 3) re-run the judge subagent.
Review your own work to ensure:
copyToHost() or row-by-row GPU-to-CPU copying is used for computationTableDebug.get().debug(...)) remain in final outputUpon successful completion:
src/main/java/com/udf/<CamelName>NativeRapidsUDF.javanative/src/main/cpp/src/These outputs are required for Step 3: Benchmark.
Take nvidia/udf-convert-to-cuda from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference docker.
Without those the skill loads but fails at the first command.