Files
TheRock/rocm-systems/projects/rccl/tools/ci/lib/gin.sbatch
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

228 lines
9.5 KiB
Bash
Executable File

#!/usr/bin/env bash
#SBATCH --job-name=rccl-gin
#SBATCH --nodes=1
#SBATCH --ntasks=8
#SBATCH --partition=interactive
#SBATCH --exclusive
#SBATCH --time=04:00:00
#SBATCH --output=%x-%j.out
#SBATCH --error=%x-%j.err
#
# Compute-node orchestrator for the RCCL/rocSHMEM GIN CI job (baremetal
# translation of docker_build_test.bash + dockerfile-gin-fast-path), invoked
# from .github/workflows/rccl-suite.yml (suite=gin) via submit_slurm_job.py.
# On one 8-GPU node:
# 1. consume ROCm (provisioned on the head node) via .ci-out/rocm.env
# 2. lib/build-ompi.sh build OpenMPI (--with-rocm) -> ompi.env
# 3. lib/build-rocshmem.sh build rocSHMEM (IPC+SDMA) -> rocshmem.env
# 4. projects/rccl install.sh build RCCL (GIN/rocSHMEM) -> rccl.env
# 5. projects/rccl-tests build rccl-tests (DEVICE_API + ROCSHMEM)
# 6. run-gin-ci.sh run the GIN/rocSHMEM tests (non-gating)
#
# Required env (from the workflow via `sbatch --export=ALL,...`):
# RCCL_DEVICE_API_WORKDIR GHA workspace (rocm-systems root)
# Optional env:
# RCCL_AMDGPU_TARGETS GPU arch (default: gfx950)
# RCCL_DEVICE_API_CACHE Cache root, shared with head node (default: /apps/rccl-ci)
set -euo pipefail
echo "=== sbatch gin on $(hostname) ==="
date -Is
env | grep -E '^SLURM_' | sort || true
echo
workdir="${RCCL_DEVICE_API_WORKDIR:?RCCL_DEVICE_API_WORKDIR must be exported from the workflow}"
cd "${workdir}"
ci_dir="${workdir}/projects/rccl/tools/ci"
gpu_targets="${RCCL_AMDGPU_TARGETS:-gfx950}"
# Phase-split logs into build-%j and test-%j (SLURM_SUBMIT_DIR = the *-logs dir).
logs_dir="${SLURM_SUBMIT_DIR:-$(pwd)}"
jid="${SLURM_JOB_ID:-local}"
build_out="${logs_dir}/build-${jid}.out"; build_err="${logs_dir}/build-${jid}.err"
test_out="${logs_dir}/test-${jid}.out"; test_err="${logs_dir}/test-${jid}.err"
# Cap parallelism (nodes report nproc=384; -j384 thrashes the C++ compile).
if [[ -z "${RCCL_BUILD_JOBS:-}" ]]; then
nproc_count="$(nproc)"
RCCL_BUILD_JOBS=$(( nproc_count < 32 ? nproc_count : 32 ))
fi
export RCCL_BUILD_JOBS
build_jobs="${RCCL_BUILD_JOBS}"
# --- build phase -> build-%j.{out,err}. Runs in a subshell so any failure (a
# hard `exit` or a set -e abort) is caught below and skips the test phase. ---
build_rc=0
(
# 1) ROCm: provisioned on the head node; consume its shared tree via rocm.env.
echo "==> [1/6] use ROCm (provisioned on head node)"
rocm_env="${workdir}/.ci-out/rocm.env"
if [[ ! -f "${rocm_env}" ]]; then
echo "ERROR: ${rocm_env} not found. The workflow must run 'Provision ROCm'" >&2
echo " (lib/fetch-rocm.sh on the head node) before sbatch." >&2
exit 1
fi
set -a
# shellcheck source=/dev/null
source "${rocm_env}"
set +a
if [[ ! -x "${ROCM_PATH:-}/bin/hipcc" ]]; then
echo "ERROR: ROCM_PATH='${ROCM_PATH:-}' (from rocm.env) has no bin/hipcc" >&2
exit 1
fi
echo " ROCM_PATH=${ROCM_PATH} ROCM_RELEASE=${ROCM_RELEASE:-<unset>}"
rocm_cache="${RCCL_DEVICE_API_CACHE:-/apps/rccl-ci}/rocm"
case "${ROCM_PATH}" in
"${rocm_cache}"/*) : ;;
*)
if [[ -z "${ROCM_PATH_OVERRIDE:-}" ]]; then
echo "ERROR: ROCM_PATH=${ROCM_PATH} is not under ${rocm_cache}" >&2
exit 1
fi ;;
esac
echo " hipcc: $("${ROCM_PATH}/bin/hipcc" --version 2>/dev/null | head -n1)"
export HIP_PATH="${ROCM_PATH}"
export PATH="${ROCM_PATH}/bin:${ROCM_PATH}/llvm/bin:${PATH}"
export LD_LIBRARY_PATH="${ROCM_PATH}/lib:${LD_LIBRARY_PATH:-}"
export CMAKE_PREFIX_PATH="${ROCM_PATH}/lib/cmake:${ROCM_PATH}${CMAKE_PREFIX_PATH:+:${CMAKE_PREFIX_PATH}}"
# 2) OpenMPI: prebuilt by the OpenMPI provision job; this is a cache hit that
# just writes ompi.env (MPI_HOME). Falls back to building if run standalone.
echo "==> [2/6] use OpenMPI"
"${ci_dir}/lib/build-ompi.sh"
set -a
# shellcheck source=/dev/null
source "${workdir}/.ci-out/ompi.env"
set +a
export PATH="${MPI_HOME}/bin:${PATH}"
# Ensure `import yaml` works for the build; must be sourced so the venv persists.
# shellcheck source=/dev/null
source "${ci_dir}/lib/ensure-python-yaml.sh"
# 3) rocSHMEM (in-tree, IPC+SDMA) -> rocshmem.env (ROCSHMEM_INSTALL_DIR)
echo "==> [3/6] build rocSHMEM"
GPU_TARGETS="${gpu_targets}" "${ci_dir}/lib/build-rocshmem.sh"
set -a
# shellcheck source=/dev/null
source "${workdir}/.ci-out/rocshmem.env"
set +a
# 4) RCCL with GIN / rocSHMEM. Mirrors the Dockerfile: patch the rocSHMEM
# threshold guard, then install.sh with ENABLE_ROCSHMEM_GIN.
echo "==> [4/6] build RCCL (GIN, targets=${gpu_targets})"
rccl_src="${workdir}/projects/rccl"
rccl_prefix="${workdir}/.ci-out/rccl-install"
rm -rf "${rccl_prefix}"
wrap="${rccl_src}/src/rccl_wrap.cc"
if grep -q 'comm->enableRocshmem && comm->nNodes > 1' "${wrap}" 2>/dev/null; then
sed -i \
's/if (comm->enableRocshmem && comm->nNodes > 1 && (comm->nRanks\/comm->nNodes == 8) && comm->rocshmemThreshold <= 1048576)/if (comm->enableRocshmem)/' \
"${wrap}"
echo " patched rccl_wrap.cc rocSHMEM guard"
else
echo " (rccl_wrap.cc guard pattern not found; assuming already patched)"
fi
(
cd "${rccl_src}"
MPI_PATH="${MPI_HOME}" ./install.sh -j "${build_jobs}" \
--amdgpu_targets="${gpu_targets}" \
--prefix="${rccl_prefix}" \
--no-device-linker --no_clean \
--cmake-options "-DENABLE_ROCSHMEM_GIN=ON -DROCSHMEM_INSTALL_DIR=${ROCSHMEM_INSTALL_DIR}"
)
# Workaround: hipify renames core.h->core_tmp.h but gin headers still include ../core.h
rccl_build="${rccl_src}/build/release"
ln -sf core_tmp.h "${rccl_build}/include/nccl_device/core.h"
ln -sf ../hipify/src/include/nccl_tuner.h "${rccl_build}/include/nccl_tuner.h"
cp -r "${rccl_build}/include/." "${rccl_prefix}/include/"
if [[ ! -f "${rccl_prefix}/lib/librccl.so" && ! -f "${rccl_prefix}/lib/librccl.so.1" ]]; then
echo "ERROR: no librccl.so under ${rccl_prefix}/lib" >&2
ls -la "${rccl_prefix}/lib" 2>/dev/null >&2 || true
exit 1
fi
# 5) rccl-tests (DEVICE_API + ROCSHMEM). allreduce fails to build; -k keeps going
# so alltoall_perf still lands (matches the Dockerfile's `make -k || true`).
echo "==> [5/6] build rccl-tests (ENABLE_DEVICE_API + ENABLE_ROCSHMEM)"
rccl_tests_src="${workdir}/projects/rccl-tests"
rccl_tests_build="${workdir}/.ci-out/rccl-tests-build"
# --whole-archive rocSHMEM into alltoall_perf so -rdynamic exports its host symbols
# (e.g. rocshmem::envvar::log_flags) for librccl.so, which is built ENABLE_ROCSHMEM_GIN
# and leaves them undefined. CI-time patch to avoid editing tracked rccl-tests files.
tlib="${rccl_tests_src}/src/CMakeLists.txt"
if grep -q 'target_link_libraries(alltoall_perf PRIVATE ${ROCSHMEM_LIBRARY}' "${tlib}" 2>/dev/null; then
sed -i \
's|target_link_libraries(alltoall_perf PRIVATE ${ROCSHMEM_LIBRARY}|target_link_libraries(alltoall_perf PRIVATE -Wl,--whole-archive ${ROCSHMEM_LIBRARY} -Wl,--no-whole-archive|' \
"${tlib}"
echo " patched alltoall_perf link: --whole-archive rocSHMEM"
else
echo " (alltoall_perf whole-archive pattern not found; skipping)"
fi
rm -rf "${rccl_tests_build}"; mkdir -p "${rccl_tests_build}"
(
cd "${rccl_tests_build}"
cmake -DCMAKE_BUILD_TYPE=Release -DUSE_MPI=ON \
-DENABLE_DEVICE_API=ON -DENABLE_ROCSHMEM=ON \
-DROCSHMEM_INSTALL_DIR="${ROCSHMEM_INSTALL_DIR}" \
-DCMAKE_PREFIX_PATH="${rccl_prefix};${MPI_HOME}" \
-DGPU_TARGETS="${gpu_targets}" \
"${rccl_tests_src}"
make -j"${build_jobs}" -k || true
)
if [[ ! -x "${rccl_tests_build}/alltoall_perf" ]]; then
echo "ERROR: alltoall_perf not built under ${rccl_tests_build}" >&2
ls -la "${rccl_tests_build}" 2>/dev/null >&2 || true
exit 1
fi
# Diag: confirm alltoall_perf exports the rocSHMEM host symbol librccl.so resolves
# at runtime (log_flags); a miss here => runtime 'undefined symbol'.
sym='rocshmem.*envvar.*log_flags'
rocshmem_a="${ROCSHMEM_INSTALL_DIR}/lib/librocshmem.a"
librccl="$(ls "${rccl_prefix}"/lib/librccl.so* 2>/dev/null | head -n1)"
echo "== log_flags symbol diag =="
nm -C "${rocshmem_a}" 2>/dev/null | grep -E " [TDBRtdbr] .*${sym}" | head || echo " not defined in librocshmem.a"
nm -CD "${rccl_tests_build}/alltoall_perf" 2>/dev/null | grep -E "${sym}" || echo " NOT exported by alltoall_perf"
nm -CD "${librccl}" 2>/dev/null | grep -E " U .*${sym}" || echo " not undefined in librccl.so"
# Hand the install/build locations to the run stage.
{
printf 'export RCCL_INSTALL_PREFIX=%q\n' "${rccl_prefix}"
printf 'export RCCL_TESTS_BIN_DIR=%q\n' "${rccl_tests_build}"
} > "${workdir}/.ci-out/rccl.env"
) > "${build_out}" 2> "${build_err}" || build_rc=$?
echo "==> build phase logs: ${build_out} / ${build_err}"
# Skip the test phase if the build failed (testing a broken build is pointless).
if [[ "${build_rc}" -ne 0 ]]; then
echo "ERROR: build phase failed (rc=${build_rc}); skipping GIN tests." >&2
echo "----- last 60 lines of ${build_err} -----" >&2
tail -n 60 "${build_err}" >&2 || true
exit "${build_rc}"
fi
# --- test phase -> test-%j.{out,err}. Don't fail the sbatch on test failures
# (that would look like a build/infra failure); drop a `tests-failed` sentinel
# instead, which the workflow turns into a red (non-gating for gin) result. ---
test_rc=0
{
echo "==> [6/6] run GIN tests"
"${ci_dir}/run-gin-ci.sh"
} > "${test_out}" 2> "${test_err}" || test_rc=$?
echo "==> test phase logs: ${test_out} / ${test_err}"
if [[ "${test_rc}" -ne 0 ]]; then
echo "GIN tests failed (rc=${test_rc}); non-gating (sentinel written)." >&2
# Capture the "FAILED / SKIPPED RUNS" summary into the sentinel so the workflow
# can show exactly which tests failed, not just a red check.
sed -n '/=== FAILED \/ SKIPPED RUNS/,$p' "${test_out}" > "${logs_dir}/tests-failed" 2>/dev/null || true
[[ -s "${logs_dir}/tests-failed" ]] \
|| echo "GIN tests failed (rc=${test_rc}); see the test log group." > "${logs_dir}/tests-failed"
fi