Files
project_6/cccl_upstream/ci/bench/compare_paths.sh
muh-bot 2a7ca101d7 feat(cccl): integrate missing CCCL directories — python/, ci/, .agent/, docs/, test/
Sparse-checkout from NVIDIA/cccl main branch to complete cccl_upstream:

Added:
- python/cuda_cccl/ (226 files) — Python bindings for device-level algorithms
  Critical for muh toolchain: cuda.compute.reduce_into, scan, radix_sort, etc.
  Includes 204 .py files with full test coverage for all 27 algorithms
- ci/ (163 files) — Build/test infrastructure
  build_cub.sh, test_cub.sh, build_and_test_targets.sh, matrix.yaml
  Directly maps to our [INFRA-CI] and [INFRA-BUILD] items
- .agent/skills/ (7 files) — NVIDIA's own agent skills for CCCL
  cccl-style/SKILL.md, cccl-test/SKILL.md, sass-diff/SKILL.md
- docs/ (491 files) — Official CCCL documentation
  CI references, CMake guides, Python compute docs, libcudacxx PTX docs
- test/ (12 files) — Top-level integration tests (cuda_smoke, stdpar)
- Root configs: .clang-format, .clang-tidy, CONTRIBUTING.md, pyproject.toml
- CLAUDE.md symlink → AGENTS.md (NVIDIA's standard)

cccl_upstream now mirrors full NVIDIA/cccl structure:
  Before: 42M (cub + thrust + libcudacxx + cudax + c + examples + benchmarks)
  After:  53M (+python +ci +docs +.agent +test +configs)

This completes the CCCL base needed for:
- [muh-bench] items: ci/util/build_and_test_targets.sh for targeted builds
- [CCCL-verify] items: python/cuda_cccl/tests/ as reference implementations
- [CCCL-test] items: ci/test_cub.sh, ci/test_thrust.sh
- Agent workflow: .agent/skills/ for consistent style and test patterns
2026-08-07 02:34:33 +00:00

1049 lines
30 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
die() {
local message="$1"
local code="${2:-2}"
echo "${message}" >&2
exit "${code}"
}
usage() {
cat <<EOF
Usage: $0 <base-path> <test-path> \
[--cub-filter "<regex>"] \
[--python-filter "<regex>"] \
[--arch "<arch>"] \
[--nvbench-args "<args>"] \
[--nvbench-compare-args "<args>"]
Compare benchmark performance between two checked-out CCCL trees.
At least one --cub-filter or --python-filter must be provided.
CUB filters are regex patterns matched against ninja target names.
Python filters are regex patterns matched against benchmark script paths
under python/cuda_cccl/benchmarks/ (e.g. compute/reduce/sum.py).
Arguments:
<base-path> Path to baseline CCCL source tree.
<test-path> Path to comparison CCCL source tree.
Options:
--cub-filter <regex> CUB benchmark regex filter (repeatable).
--python-filter <regex> Python benchmark regex filter (repeatable).
--arch <arch> CMAKE_CUDA_ARCHITECTURES for CUB builds.
--nvbench-args <args> Extra args passed to benchmark binaries/scripts.
--nvbench-compare-args <args> Extra args passed to nvbench_compare.
Environment:
CCCL_BENCH_ARTIFACT_ROOT Root directory for outputs.
Default: "\$(pwd)/bench-artifacts"
CCCL_BENCH_ARTIFACT_TAG Optional explicit artifact directory name.
CCCL_BENCH_BASE_LABEL Optional label used in auto-generated artifact names.
CCCL_BENCH_TEST_LABEL Optional label used in auto-generated artifact names.
CCCL_BENCH_BASE_BUILD_DIR Optional preconfigured build tree for base path.
CCCL_BENCH_TEST_BUILD_DIR Optional preconfigured build tree for test path.
If either is set, both must be set.
CCCL_BENCH_GPU_NAME Optional GPU name included in artifact directory names.
CCCL_BENCH_BUILD_ROOT Root directory for generated build trees.
Default: "/tmp/cccl-bench-builds"
EOF
}
sanitize_label() {
local label="$1"
label="${label//[^a-zA-Z0-9._-]/_}"
label="${label#_}"
label="${label%_}"
label="${label:-unknown}"
printf "%s" "${label}"
}
resolve_repo_label() {
local repo_path="$1"
local branch=""
branch="$(git -C "${repo_path}" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [[ -n "${branch}" && "${branch}" != "HEAD" ]]; then
printf "%s" "${branch}"
return 0
fi
local short_sha=""
short_sha="$(git -C "${repo_path}" rev-parse --short=12 HEAD 2>/dev/null || true)"
if [[ -n "${short_sha}" ]]; then
printf "%s" "${short_sha}"
return 0
fi
basename "${repo_path}"
}
validate_repo_path() {
local repo_path="$1"
if [[ ! -d "${repo_path}" ]]; then
die "Path does not exist: ${repo_path}"
fi
if [[ ! -f "${repo_path}/cccl-version.json" ]]; then
die "Path is not a CCCL source tree: ${repo_path}"
fi
}
validate_filter_array() {
local -n _validate_filters_ref="$1"
local label="$2"
local filter=""
for filter in "${_validate_filters_ref[@]}"; do
grep -Eq -- "${filter}" <<< "" >/dev/null 2>&1 || {
[[ "$?" -eq 1 ]] || die "Invalid ${label} regex filter: ${filter}"
}
done
}
print_shell_command() {
# Print a shell-escaped command line so it can be copied and re-run directly.
# Usage: print_shell_command [--env "VAR=value" ...] cmd [args...]
local -a env_prefixes=()
while [[ "$#" -gt 0 && "$1" == --env ]]; do
shift
env_prefixes+=("$1")
shift
done
printf '$'
local item=""
for item in "${env_prefixes[@]}"; do
printf ' %q' "${item}"
done
for item in "$@"; do
printf ' %q' "${item}"
done
printf '\n'
}
# ============================================================================
# CUB helpers
# ============================================================================
configure_build_tree() {
local src_path="$1"
local build_path="$2"
local side="$3"
local log_path="$4"
local target_arch="$5"
local -a cmake_cmd
cmake_cmd=(cmake --preset "cub-benchmark" -S "${src_path}" -B "${build_path}")
if [[ -n "${target_arch}" ]]; then
cmake_cmd+=("-DCMAKE_CUDA_ARCHITECTURES=${target_arch}")
fi
run_grouped_logged_command "[configure:${side}]" "${log_path}" "${cmake_cmd[@]}"
}
validate_build_dir() {
local build_path="$1"
local label="$2"
if [[ ! -d "${build_path}" ]]; then
die "Configured ${label} build tree does not exist: ${build_path}"
fi
if [[ ! -f "${build_path}/build.ninja" ]]; then
die "Configured ${label} build tree is missing build.ninja: ${build_path}"
fi
}
list_all_benchmark_targets() {
local build_path="$1"
ninja -C "${build_path}" -t targets all \
| awk -F':' '/^.*\.bench\./ { print $1 }' \
| sort -u
}
target_matches_filters() {
local target="$1"
local filter=""
if [[ "${#FILTERS[@]}" -eq 0 ]]; then
return 0
fi
for filter in "${FILTERS[@]}"; do
if grep -Eq -- "${filter}" <<< "${target}"; then
return 0
fi
done
return 1
}
resolve_compare_script() {
local build_path="$1"
local nvbench_src="${build_path}/_deps/nvbench-src"
local candidate=""
# Diff versions have the script at diff locations:
for candidate in \
"${nvbench_src}/python/scripts/nvbench_compare.py" \
"${nvbench_src}/scripts/nvbench_compare.py"; do
if [[ -f "${candidate}" ]]; then
printf "%s" "${candidate}"
return 0
fi
done
return 1
}
run_target_for_side() {
local side="$1"
local build_path="$2"
local target="$3"
local json_path="$4"
local md_path="$5"
local run_log="$6"
local binary_path="${build_path}/bin/${target}"
local -a bench_cmd
if [[ ! -x "${binary_path}" ]]; then
echo "Benchmark binary missing: ${binary_path}" >&2
return 127
fi
bench_cmd=(
"${binary_path}"
-d 0
"${NVBENCH_RUN_ARGS[@]}"
--json "${json_path}"
--md "${md_path}"
)
run_grouped_logged_command \
"[run:${side}] ${target}" \
"${run_log}" \
"${bench_cmd[@]}"
}
select_targets() {
local base_build_path="$1"
local test_build_path="$2"
local -n selected_targets_ref="$3"
local -a base_targets
local -a test_targets
local -a common_targets
local target=""
mapfile -t base_targets < <(list_all_benchmark_targets "${base_build_path}")
mapfile -t test_targets < <(list_all_benchmark_targets "${test_build_path}")
if [[ "${#base_targets[@]}" -eq 0 ]]; then
die "No CUB benchmark targets were found in base build tree." 1
fi
if [[ "${#test_targets[@]}" -eq 0 ]]; then
die "No CUB benchmark targets were found in test build tree." 1
fi
mapfile -t common_targets < <(
comm -12 \
<(printf "%s\n" "${base_targets[@]}" | sort -u) \
<(printf "%s\n" "${test_targets[@]}" | sort -u)
)
selected_targets_ref=()
for target in "${common_targets[@]}"; do
[[ -n "${target}" ]] || continue
if target_matches_filters "${target}"; then
selected_targets_ref+=("${target}")
fi
done
if [[ "${#selected_targets_ref[@]}" -eq 0 ]]; then
die "No CUB benchmark targets matched the supplied filters." 1
fi
}
# ============================================================================
# Python helpers
# ============================================================================
detect_cuda_major_version() {
local cuda_major=""
if command -v nvcc >/dev/null 2>&1; then
cuda_major="$(nvcc --version 2>/dev/null | sed -n 's/.*release \([0-9]*\)\..*/\1/p')"
fi
if [[ -z "${cuda_major}" ]]; then
cuda_major="12"
fi
printf "%s" "${cuda_major}"
}
python_path_to_target_name() {
local py_path="$1"
# compute/reduce/sum.py -> py.compute.reduce.sum
local name="${py_path%.py}"
name="${name//\//.}"
printf "py.%s" "${name}"
}
list_all_python_benchmarks() {
local benchmarks_path="$1"
if [[ ! -d "${benchmarks_path}" ]]; then
return 0
fi
find "${benchmarks_path}" -name '*.py' -type f \
! -name 'utils.py' \
! -name 'run_benchmarks.py' \
! -name 'device_side_benchmark.py' \
! -name '__init__.py' \
! -path '*/__pycache__/*' \
-printf '%P\n' \
| sort -u
}
python_target_matches_filters() {
local target="$1"
local filter=""
for filter in "${PYTHON_FILTERS[@]}"; do
if grep -Eq -- "${filter}" <<< "${target}"; then
return 0
fi
done
return 1
}
select_python_targets() {
local base_bench_path="$1"
local test_bench_path="$2"
local -n selected_py_targets_ref="$3"
local -a base_py_targets
local -a test_py_targets
local -a common_py_targets
local target=""
mapfile -t base_py_targets < <(list_all_python_benchmarks "${base_bench_path}")
mapfile -t test_py_targets < <(list_all_python_benchmarks "${test_bench_path}")
if [[ "${#base_py_targets[@]}" -eq 0 ]]; then
die "No Python benchmark scripts were found in base tree: ${base_bench_path}" 1
fi
if [[ "${#test_py_targets[@]}" -eq 0 ]]; then
die "No Python benchmark scripts were found in test tree: ${test_bench_path}" 1
fi
mapfile -t common_py_targets < <(
comm -12 \
<(printf "%s\n" "${base_py_targets[@]}" | sort -u) \
<(printf "%s\n" "${test_py_targets[@]}" | sort -u)
)
selected_py_targets_ref=()
for target in "${common_py_targets[@]}"; do
[[ -n "${target}" ]] || continue
if python_target_matches_filters "${target}"; then
selected_py_targets_ref+=("${target}")
fi
done
if [[ "${#selected_py_targets_ref[@]}" -eq 0 ]]; then
die "No Python benchmark scripts matched the supplied --python-filter patterns." 1
fi
}
setup_python_venv() {
local venv_path="$1"
local src_path="$2"
local side="$3"
local log_path="$4"
local cuda_major="$5"
local cuda_cccl_dir="${src_path}/python/cuda_cccl"
if [[ ! -d "${cuda_cccl_dir}" ]]; then
die "cuda_cccl source directory not found: ${cuda_cccl_dir}"
fi
local -a setup_cmds
setup_cmds=(
bash -c "
set -euo pipefail
python3 -m venv '${venv_path}'
'${venv_path}/bin/pip' install --upgrade pip
'${venv_path}/bin/pip' install -e '${cuda_cccl_dir}[bench-cu${cuda_major}]'
# nvbench-compare runtime deps (until cuda-bench declares them):
'${venv_path}/bin/pip' install colorama jsondiff tabulate
"
)
run_grouped_logged_command \
"[py-venv:${side}]" \
"${log_path}" \
"${setup_cmds[@]}"
}
run_python_target_for_side() {
local side="$1"
local venv_path="$2"
local script_path="$3"
local json_path="$4"
local md_path="$5"
local run_log="$6"
local -a bench_cmd
if [[ ! -f "${script_path}" ]]; then
echo "Python benchmark script missing: ${script_path}" >&2
return 127
fi
bench_cmd=(
"${venv_path}/bin/python"
"${script_path}"
-d 0
"${NVBENCH_RUN_ARGS[@]}"
--json "${json_path}"
--md "${md_path}"
)
run_grouped_logged_command \
"[py-run:${side}] ${script_path##*/benchmarks/}" \
"${run_log}" \
"${bench_cmd[@]}"
}
run_python_compare_target() {
local target_name="$1"
local venv_path="$2"
local base_json="$3"
local test_json="$4"
local compare_out="$5"
local compare_log="$6"
local label="[py-compare] ${target_name}"
local started_at=0
local elapsed_s=0
local rc=0
local -a compare_cmd
compare_cmd=("${venv_path}/bin/nvbench-compare" --no-color "${NVBENCH_COMPARE_ARGS[@]}" "${base_json}" "${test_json}")
: > "${compare_log}"
echo "::group::${label}"
print_shell_command "${compare_cmd[@]}"
started_at="${SECONDS}"
if "${compare_cmd[@]}" \
> >(tee "${compare_out}" | tee -a "${compare_log}") \
2> >(tee -a "${compare_log}" >&2); then
rc=0
else
rc=$?
fi
elapsed_s=$((SECONDS - started_at))
echo "::endgroup::"
if [[ "${rc}" -eq 0 ]]; then
echo "${label} completed in ${elapsed_s}s"
else
echo "${label} failed in ${elapsed_s}s (rc=${rc})"
fi
return "${rc}"
}
# ============================================================================
# Common helpers
# ============================================================================
run_grouped_logged_command() {
local label="$1"
local log_path="$2"
shift 2
local started_at=0
local elapsed_s=0
local rc=0
local -a pipe_statuses
echo "::group::${label}"
print_shell_command "$@"
started_at="${SECONDS}"
set +o pipefail
"$@" 2>&1 | tee "${log_path}"
pipe_statuses=("${PIPESTATUS[@]}")
set -o pipefail
if [[ "${pipe_statuses[0]}" -ne 0 ]]; then
rc="${pipe_statuses[0]}"
elif [[ "${pipe_statuses[1]}" -ne 0 ]]; then
rc="${pipe_statuses[1]}"
fi
elapsed_s=$((SECONDS - started_at))
echo "::endgroup::"
if [[ "${rc}" -eq 0 ]]; then
echo "${label} completed in ${elapsed_s}s"
else
echo "${label} failed in ${elapsed_s}s (rc=${rc})"
fi
return "${rc}"
}
run_compare_target() {
local target="$1"
local compare_script="$2"
local compare_script_dir="$3"
local base_json="$4"
local test_json="$5"
local compare_out="$6"
local compare_log="$7"
local label="[compare] ${target}"
local started_at=0
local elapsed_s=0
local rc=0
local compare_pythonpath="${compare_script_dir}${PYTHONPATH:+:${PYTHONPATH}}"
local -a compare_cmd
compare_cmd=(python3 "${compare_script}" --no-color "${NVBENCH_COMPARE_ARGS[@]}" "${base_json}" "${test_json}")
: > "${compare_log}"
echo "::group::${label}"
print_shell_command --env "PYTHONPATH=${compare_pythonpath}" "${compare_cmd[@]}"
started_at="${SECONDS}"
if PYTHONPATH="${compare_pythonpath}" \
"${compare_cmd[@]}" \
> >(tee "${compare_out}" | tee -a "${compare_log}") \
2> >(tee -a "${compare_log}" >&2); then
rc=0
else
rc=$?
fi
elapsed_s=$((SECONDS - started_at))
echo "::endgroup::"
if [[ "${rc}" -eq 0 ]]; then
echo "${label} completed in ${elapsed_s}s"
else
echo "${label} failed in ${elapsed_s}s (rc=${rc})"
fi
return "${rc}"
}
parse_quoted_args_to_nul_file() {
local quoted_args="$1"
local output_file="$2"
local option_name="$3"
QUOTED_ARGS="${quoted_args}" OPTION_NAME="${option_name}" python3 - "${output_file}" <<'PY'
import os
import shlex
import sys
quoted = os.environ.get("QUOTED_ARGS", "")
option_name = os.environ.get("OPTION_NAME", "--args")
try:
parsed = shlex.split(quoted)
except ValueError as exc:
print(f"Invalid {option_name}: {exc}", file=sys.stderr)
raise SystemExit(2)
with open(sys.argv[1], "wb") as out_file:
for arg in parsed:
out_file.write(arg.encode("utf-8"))
out_file.write(b"\0")
PY
}
parse_quoted_args_to_array() {
local -n _target_array_ref="$1"
local quoted_args="$2"
local option_label="$3"
local parsed_args_file=""
_target_array_ref=()
[[ -n "${quoted_args}" ]] || return 0
parsed_args_file="$(mktemp "/tmp/cccl-parsed-args-XXXXXX")"
if ! parse_quoted_args_to_nul_file "${quoted_args}" "${parsed_args_file}" "${option_label}"; then
rm -f "${parsed_args_file}"
return 2
fi
mapfile -d '' -t _target_array_ref < "${parsed_args_file}"
rm -f "${parsed_args_file}"
}
# ============================================================================
# Summary
# ============================================================================
write_summary() {
local summary_file="$1"
local target=""
local compare_report_file=""
local reports_emitted=0
{
echo "# Benchmark Comparison Summary"
echo
echo "- Timestamp (UTC): ${timestamp}"
echo "- GPU name: ${CCCL_BENCH_GPU_NAME:-not specified}"
echo "- Base label: ${base_label_raw}"
echo "- Test label: ${test_label_raw}"
echo "- Base source path: \`${BASE_PATH}\`"
echo "- Test source path: \`${TEST_PATH}\`"
if [[ "${#FILTERS[@]}" -gt 0 ]]; then
echo "- Base build dir: \`${base_build_dir}\`"
echo "- Test build dir: \`${test_build_dir}\`"
fi
echo "- CUB targets selected: ${#selected_targets[@]}"
echo "- CUB comparisons attempted: ${compares_attempted}"
echo "- CUB comparisons succeeded: ${compares_succeeded}"
echo "- Python targets selected: ${#selected_py_targets[@]}"
echo "- Python comparisons attempted: ${py_compares_attempted}"
echo "- Python comparisons succeeded: ${py_compares_succeeded}"
echo "- Target arch: ${TARGET_ARCH:-preset-default}"
echo "- Artifact directory: \`${artifact_dir}\`"
echo
if [[ "${#FILTERS[@]}" -gt 0 ]]; then
echo "## CUB Filters"
for filter in "${FILTERS[@]}"; do
echo "- \`${filter}\`"
done
echo
fi
if [[ "${#PYTHON_FILTERS[@]}" -gt 0 ]]; then
echo "## Python Filters"
for filter in "${PYTHON_FILTERS[@]}"; do
echo "- \`${filter}\`"
done
echo
fi
if [[ "${#selected_targets[@]}" -gt 0 ]]; then
echo "## CUB Compare Reports"
for target in "${selected_targets[@]}"; do
compare_report_file="${artifact_dir}/compare/${target}.md"
if [[ ! -f "${compare_report_file}" ]]; then
continue
fi
reports_emitted=$((reports_emitted + 1))
echo
echo "### \`${target}\`"
echo
echo "<details><summary>Expand full compare output for \`${target}\`</summary>"
echo
cat "${compare_report_file}"
echo
echo "</details>"
done
fi
if [[ "${#selected_py_targets[@]}" -gt 0 ]]; then
echo
echo "## Python Compare Reports"
local py_target_path=""
local py_target_name=""
for py_target_path in "${selected_py_targets[@]}"; do
py_target_name="$(python_path_to_target_name "${py_target_path}")"
compare_report_file="${artifact_dir}/compare/${py_target_name}.md"
if [[ ! -f "${compare_report_file}" ]]; then
continue
fi
reports_emitted=$((reports_emitted + 1))
echo
echo "### \`${py_target_name}\` (\`${py_target_path}\`)"
echo
echo "<details><summary>Expand full compare output for \`${py_target_name}\`</summary>"
echo
cat "${compare_report_file}"
echo
echo "</details>"
done
fi
if [[ "${reports_emitted}" -eq 0 ]]; then
echo
echo "_No per-target compare reports were produced._"
fi
} > "${summary_file}"
}
# ============================================================================
# CLI parsing
# ============================================================================
parse_cli_args() {
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
if [[ "$#" -lt 2 ]]; then
usage
exit 2
fi
BASE_PATH="$(realpath "$1")"
TEST_PATH="$(realpath "$2")"
shift 2
NVBENCH_ARGS_STRING=""
NVBENCH_COMPARE_ARGS_STRING=""
TARGET_ARCH=""
FILTERS=()
PYTHON_FILTERS=()
while [[ "$#" -gt 0 ]]; do
case "$1" in
--arch)
if [[ "$#" -lt 2 ]]; then
die "Missing value for --arch"
fi
TARGET_ARCH="$2"
shift 2
;;
--nvbench-args)
if [[ "$#" -lt 2 ]]; then
die "Missing value for --nvbench-args"
fi
NVBENCH_ARGS_STRING="$2"
shift 2
;;
--nvbench-compare-args)
if [[ "$#" -lt 2 ]]; then
die "Missing value for --nvbench-compare-args"
fi
NVBENCH_COMPARE_ARGS_STRING="$2"
shift 2
;;
--cub-filter)
if [[ "$#" -lt 2 ]]; then
die "Missing value for --cub-filter"
fi
FILTERS+=("$2")
shift 2
;;
--python-filter)
if [[ "$#" -lt 2 ]]; then
die "Missing value for --python-filter"
fi
PYTHON_FILTERS+=("$2")
shift 2
;;
--)
shift
break
;;
*)
die "Unknown option: $1"
;;
esac
done
}
parse_cli_args "$@"
declare -a NVBENCH_RUN_ARGS
declare -a NVBENCH_COMPARE_ARGS
parse_quoted_args_to_array NVBENCH_RUN_ARGS "${NVBENCH_ARGS_STRING}" "--nvbench-args" \
|| die "Failed to parse --nvbench-args."
parse_quoted_args_to_array NVBENCH_COMPARE_ARGS "${NVBENCH_COMPARE_ARGS_STRING}" "--nvbench-compare-args" \
|| die "Failed to parse --nvbench-compare-args."
validate_repo_path "${BASE_PATH}"
validate_repo_path "${TEST_PATH}"
validate_filter_array FILTERS "CUB"
validate_filter_array PYTHON_FILTERS "Python"
# ============================================================================
# Common setup
# ============================================================================
timestamp="$(date -u +'%Y%m%dT%H%M%SZ')"
base_label_raw="${CCCL_BENCH_BASE_LABEL:-$(resolve_repo_label "${BASE_PATH}")}"
test_label_raw="${CCCL_BENCH_TEST_LABEL:-$(resolve_repo_label "${TEST_PATH}")}"
base_label="$(sanitize_label "${base_label_raw}")"
test_label="$(sanitize_label "${test_label_raw}")"
artifact_root="${CCCL_BENCH_ARTIFACT_ROOT:-$(pwd)/bench-artifacts}"
gpu_tag="${CCCL_BENCH_GPU_NAME:+$(sanitize_label "${CCCL_BENCH_GPU_NAME}")-}"
artifact_tag="${CCCL_BENCH_ARTIFACT_TAG:-bench-${gpu_tag}${test_label}-${timestamp}-${base_label}}"
artifact_tag="$(sanitize_label "${artifact_tag}")"
artifact_dir="${artifact_root}/${artifact_tag}"
build_root="${CCCL_BENCH_BUILD_ROOT:-/tmp/cccl-bench-builds}"
build_token="$(sanitize_label "${test_label}-${timestamp}-${base_label}")"
base_build_dir="${build_root}/base-${build_token}"
test_build_dir="${build_root}/test-${build_token}"
for subdir in base compare logs meta test; do
mkdir -p "${artifact_dir}/${subdir}"
done
mkdir -p "${build_root}"
echo "Artifact directory: ${artifact_dir}"
if [[ -n "${CCCL_BENCH_GPU_NAME:-}" ]]; then
echo "GPU name: ${CCCL_BENCH_GPU_NAME}"
fi
echo "Base source: ${BASE_PATH}"
echo "Test source: ${TEST_PATH}"
if [[ "${#FILTERS[@]}" -gt 0 ]]; then
echo "CUB filters:"
for filter in "${FILTERS[@]}"; do
echo " - ${filter}"
done
else
echo "CUB filters: (none)"
fi
if [[ "${#PYTHON_FILTERS[@]}" -gt 0 ]]; then
echo "Python filters:"
for filter in "${PYTHON_FILTERS[@]}"; do
echo " - ${filter}"
done
else
echo "Python filters: (none)"
fi
if [[ -n "${TARGET_ARCH}" ]]; then
echo "Target arch: ${TARGET_ARCH}"
fi
if [[ "${#NVBENCH_RUN_ARGS[@]}" -gt 0 ]]; then
echo "Extra run args:"
for arg in "${NVBENCH_RUN_ARGS[@]}"; do
echo " - ${arg}"
done
fi
if [[ "${#NVBENCH_COMPARE_ARGS[@]}" -gt 0 ]]; then
echo "Extra compare args:"
for arg in "${NVBENCH_COMPARE_ARGS[@]}"; do
echo " - ${arg}"
done
fi
any_failures=0
compares_attempted=0
compares_succeeded=0
declare -a selected_targets=()
py_compares_attempted=0
py_compares_succeeded=0
declare -a selected_py_targets=()
# ============================================================================
# CUB benchmark pipeline
# ============================================================================
if [[ "${#FILTERS[@]}" -gt 0 ]]; then
echo
echo "=== CUB Benchmark Pipeline ==="
echo
external_base_build_dir="${CCCL_BENCH_BASE_BUILD_DIR:-}"
external_test_build_dir="${CCCL_BENCH_TEST_BUILD_DIR:-}"
if [[ -n "${external_base_build_dir}" || -n "${external_test_build_dir}" ]]; then
if [[ -z "${external_base_build_dir}" || -z "${external_test_build_dir}" ]]; then
die "Both CCCL_BENCH_BASE_BUILD_DIR and CCCL_BENCH_TEST_BUILD_DIR must be set together."
fi
base_build_dir="$(realpath "${external_base_build_dir}")"
test_build_dir="$(realpath "${external_test_build_dir}")"
validate_build_dir "${base_build_dir}" "base"
validate_build_dir "${test_build_dir}" "test"
if [[ -n "${TARGET_ARCH}" ]]; then
echo "Warning: --arch is ignored when using preconfigured build directories." >&2
fi
fi
if [[ -z "${external_base_build_dir:-}" ]]; then
configure_build_tree "${BASE_PATH}" "${base_build_dir}" "base" "${artifact_dir}/logs/configure.base.log" "${TARGET_ARCH}"
configure_build_tree "${TEST_PATH}" "${test_build_dir}" "test" "${artifact_dir}/logs/configure.test.log" "${TARGET_ARCH}"
else
echo "[configure:base] skipped (using existing build tree)"
echo "[configure:test] skipped (using existing build tree)"
fi
select_targets "${base_build_dir}" "${test_build_dir}" selected_targets
printf "%s\n" "${selected_targets[@]}" > "${artifact_dir}/meta/selected_targets.txt"
compare_script="$(resolve_compare_script "${test_build_dir}" || true)"
if [[ -z "${compare_script}" ]]; then
compare_script="$(resolve_compare_script "${base_build_dir}" || true)"
fi
if [[ -z "${compare_script}" ]]; then
die "Unable to locate nvbench_compare.py in build dependencies." 1
fi
compare_script_dir="$(dirname "${compare_script}")"
base_build_all_rc=0
test_build_all_rc=0
if run_grouped_logged_command \
"[build:base]" \
"${artifact_dir}/logs/build.base.log" \
ninja -C "${base_build_dir}" "${selected_targets[@]}"; then
base_build_all_rc=0
else
base_build_all_rc=$?
any_failures=1
fi
if run_grouped_logged_command \
"[build:test]" \
"${artifact_dir}/logs/build.test.log" \
ninja -C "${test_build_dir}" "${selected_targets[@]}"; then
test_build_all_rc=0
else
test_build_all_rc=$?
any_failures=1
fi
for target in "${selected_targets[@]}"; do
base_target_run_rc=125
test_target_run_rc=125
base_run_log="${artifact_dir}/logs/run.base.${target}.log"
test_run_log="${artifact_dir}/logs/run.test.${target}.log"
compare_report_md="${artifact_dir}/compare/${target}.md"
compare_report_log="${artifact_dir}/logs/compare.${target}.log"
base_json="${artifact_dir}/base/${target}.json"
base_md="${artifact_dir}/base/${target}.md"
test_json="${artifact_dir}/test/${target}.json"
test_md="${artifact_dir}/test/${target}.md"
if [[ "${base_build_all_rc}" -eq 0 ]]; then
if run_target_for_side \
"base" \
"${base_build_dir}" \
"${target}" \
"${base_json}" \
"${base_md}" \
"${base_run_log}"; then
base_target_run_rc=0
else
base_target_run_rc=$?
any_failures=1
fi
fi
if [[ "${test_build_all_rc}" -eq 0 ]]; then
if run_target_for_side \
"test" \
"${test_build_dir}" \
"${target}" \
"${test_json}" \
"${test_md}" \
"${test_run_log}"; then
test_target_run_rc=0
else
test_target_run_rc=$?
any_failures=1
fi
fi
if [[ "${base_target_run_rc}" -eq 0 && "${test_target_run_rc}" -eq 0 ]]; then
compares_attempted=$((compares_attempted + 1))
if run_compare_target \
"${target}" \
"${compare_script}" \
"${compare_script_dir}" \
"${base_json}" \
"${test_json}" \
"${compare_report_md}" \
"${compare_report_log}"; then
compares_succeeded=$((compares_succeeded + 1))
else
any_failures=1
fi
fi
done
fi
# ============================================================================
# Python benchmark pipeline
# ============================================================================
if [[ "${#PYTHON_FILTERS[@]}" -gt 0 ]]; then
echo
echo "=== Python Benchmark Pipeline ==="
echo
py_benchmarks_subdir="python/cuda_cccl/benchmarks"
base_py_bench_dir="${BASE_PATH}/${py_benchmarks_subdir}"
test_py_bench_dir="${TEST_PATH}/${py_benchmarks_subdir}"
if [[ ! -d "${base_py_bench_dir}" ]]; then
die "Python benchmarks directory not found in base tree: ${base_py_bench_dir}"
fi
if [[ ! -d "${test_py_bench_dir}" ]]; then
die "Python benchmarks directory not found in test tree: ${test_py_bench_dir}"
fi
cuda_major="$(detect_cuda_major_version)"
echo "Detected CUDA major version: ${cuda_major}"
base_py_venv="${build_root}/py-base-${build_token}"
test_py_venv="${build_root}/py-test-${build_token}"
setup_python_venv "${base_py_venv}" "${BASE_PATH}" "base" "${artifact_dir}/logs/py.venv.base.log" "${cuda_major}"
setup_python_venv "${test_py_venv}" "${TEST_PATH}" "test" "${artifact_dir}/logs/py.venv.test.log" "${cuda_major}"
select_python_targets "${base_py_bench_dir}" "${test_py_bench_dir}" selected_py_targets
# Append Python targets to the selected targets metadata file.
for py_target_path in "${selected_py_targets[@]}"; do
python_path_to_target_name "${py_target_path}" >> "${artifact_dir}/meta/selected_targets.txt"
done
for py_target_path in "${selected_py_targets[@]}"; do
py_target_name="$(python_path_to_target_name "${py_target_path}")"
base_py_target_run_rc=125
test_py_target_run_rc=125
base_py_json="${artifact_dir}/base/${py_target_name}.json"
base_py_md="${artifact_dir}/base/${py_target_name}.md"
test_py_json="${artifact_dir}/test/${py_target_name}.json"
test_py_md="${artifact_dir}/test/${py_target_name}.md"
base_py_run_log="${artifact_dir}/logs/run.base.${py_target_name}.log"
test_py_run_log="${artifact_dir}/logs/run.test.${py_target_name}.log"
compare_py_report_md="${artifact_dir}/compare/${py_target_name}.md"
compare_py_report_log="${artifact_dir}/logs/compare.${py_target_name}.log"
if run_python_target_for_side \
"base" \
"${base_py_venv}" \
"${base_py_bench_dir}/${py_target_path}" \
"${base_py_json}" \
"${base_py_md}" \
"${base_py_run_log}"; then
base_py_target_run_rc=0
else
base_py_target_run_rc=$?
any_failures=1
fi
if run_python_target_for_side \
"test" \
"${test_py_venv}" \
"${test_py_bench_dir}/${py_target_path}" \
"${test_py_json}" \
"${test_py_md}" \
"${test_py_run_log}"; then
test_py_target_run_rc=0
else
test_py_target_run_rc=$?
any_failures=1
fi
if [[ "${base_py_target_run_rc}" -eq 0 && "${test_py_target_run_rc}" -eq 0 ]]; then
py_compares_attempted=$((py_compares_attempted + 1))
if run_python_compare_target \
"${py_target_name}" \
"${test_py_venv}" \
"${base_py_json}" \
"${test_py_json}" \
"${compare_py_report_md}" \
"${compare_py_report_log}"; then
py_compares_succeeded=$((py_compares_succeeded + 1))
else
any_failures=1
fi
fi
done
fi
# ============================================================================
# Summary and exit
# ============================================================================
summary_file="${artifact_dir}/summary.md"
write_summary "${summary_file}"
echo "Wrote summary: ${summary_file}"
echo "Benchmark artifacts: ${artifact_dir}"
echo
echo "Main summary:"
cat "${summary_file}"
echo
if [[ "${any_failures}" -ne 0 ]]; then
exit 1
fi
exit 0