Set Velocity and Status in G4Step information #344
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: 2026 CERN | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: Athena Performance (EL9) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| mode: | |
| description: Benchmark mode to run | |
| required: true | |
| type: choice | |
| options: | |
| - mono | |
| - split | |
| athena_gpu_repository: | |
| description: Optional override for the Athena GPU merge repository | |
| required: false | |
| type: string | |
| athena_gpu_ref: | |
| description: Optional override for the Athena GPU merge ref | |
| required: false | |
| type: string | |
| athena_base_ref: | |
| description: Optional Athena base branch/ref override | |
| required: false | |
| type: string | |
| atlasexternals_base_ref: | |
| description: Optional AtlasExternals base branch/ref override | |
| required: false | |
| type: string | |
| threads: | |
| description: Optional ATHENA_CORE_NUMBER override | |
| required: false | |
| type: string | |
| events: | |
| description: Optional maxEvents override | |
| required: false | |
| type: string | |
| repetitions: | |
| description: Optional repetition count override | |
| required: false | |
| type: string | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| concurrency: | |
| group: athena-performance-el9 | |
| cancel-in-progress: false | |
| jobs: | |
| prepare: | |
| name: Prepare | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_run: ${{ steps.prepare.outputs.should_run }} | |
| mode: ${{ steps.prepare.outputs.mode }} | |
| adept_repo: ${{ steps.prepare.outputs.adept_repo }} | |
| adept_ref: ${{ steps.prepare.outputs.adept_ref }} | |
| pr_number: ${{ steps.prepare.outputs.pr_number }} | |
| comment_back: ${{ steps.prepare.outputs.comment_back }} | |
| athena_repository: ${{ steps.prepare.outputs.athena_repository }} | |
| athena_base_ref: ${{ steps.prepare.outputs.athena_base_ref }} | |
| athena_gpu_repository: ${{ steps.prepare.outputs.athena_gpu_repository }} | |
| athena_gpu_ref: ${{ steps.prepare.outputs.athena_gpu_ref }} | |
| atlasexternals_repository: ${{ steps.prepare.outputs.atlasexternals_repository }} | |
| atlasexternals_ref: ${{ steps.prepare.outputs.atlasexternals_ref }} | |
| threads: ${{ steps.prepare.outputs.threads }} | |
| events: ${{ steps.prepare.outputs.events }} | |
| repetitions: ${{ steps.prepare.outputs.repetitions }} | |
| steps: | |
| - name: Resolve trigger inputs | |
| id: prepare | |
| uses: actions/github-script@v8 | |
| env: | |
| DEFAULT_ATHENA_REPOSITORY: https://gitlab.cern.ch/atlas/athena.git | |
| DEFAULT_ATHENA_BASE_REF: main | |
| DEFAULT_ATHENA_GPU_REPOSITORY: https://gitlab.cern.ch/elmsheus/athena.git | |
| DEFAULT_ATHENA_GPU_REF: atlassim-6635-main | |
| DEFAULT_ATLAS_EXTERNALS_REPOSITORY: https://gitlab.cern.ch/atlas/atlasexternals.git | |
| DEFAULT_ATLAS_EXTERNALS_BASE_REF: 21.1.X-simGPU | |
| DEFAULT_THREADS: "96" | |
| DEFAULT_EVENTS: "1000" | |
| DEFAULT_REPETITIONS: "5" | |
| with: | |
| script: | | |
| const defaults = { | |
| athenaRepository: process.env.DEFAULT_ATHENA_REPOSITORY, | |
| athenaBaseRef: process.env.DEFAULT_ATHENA_BASE_REF, | |
| athenaGpuRepository: process.env.DEFAULT_ATHENA_GPU_REPOSITORY || "", | |
| athenaGpuRef: process.env.DEFAULT_ATHENA_GPU_REF || "", | |
| atlasexternalsRepository: process.env.DEFAULT_ATLAS_EXTERNALS_REPOSITORY, | |
| atlasexternalsBaseRef: process.env.DEFAULT_ATLAS_EXTERNALS_BASE_REF, | |
| threads: process.env.DEFAULT_THREADS, | |
| events: process.env.DEFAULT_EVENTS, | |
| repetitions: process.env.DEFAULT_REPETITIONS, | |
| }; | |
| function output(name, value) { | |
| core.setOutput(name, value ?? ""); | |
| } | |
| async function hasWriteLikeRepoAccess(username) { | |
| try { | |
| const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| username, | |
| }); | |
| return ["write", "maintain", "admin"].includes(data.permission); | |
| } catch (error) { | |
| if (error.status === 404) { | |
| return false; | |
| } | |
| throw error; | |
| } | |
| } | |
| output("should_run", "false"); | |
| output("comment_back", "false"); | |
| output("pr_number", ""); | |
| output("athena_repository", defaults.athenaRepository); | |
| output("athena_base_ref", defaults.athenaBaseRef); | |
| output("athena_gpu_repository", defaults.athenaGpuRepository); | |
| output("athena_gpu_ref", defaults.athenaGpuRef); | |
| output("atlasexternals_repository", defaults.atlasexternalsRepository); | |
| output("atlasexternals_ref", defaults.atlasexternalsBaseRef); | |
| output("threads", defaults.threads); | |
| output("events", defaults.events); | |
| output("repetitions", defaults.repetitions); | |
| let resolvedMode = ""; | |
| let resolvedAdeptRepo = ""; | |
| let resolvedAdeptRef = ""; | |
| let resolvedAthenaRepository = defaults.athenaRepository; | |
| let resolvedAthenaBaseRef = defaults.athenaBaseRef; | |
| let resolvedAthenaGpuRepository = defaults.athenaGpuRepository; | |
| let resolvedAthenaGpuRef = defaults.athenaGpuRef; | |
| let resolvedAtlasExternalsRepository = defaults.atlasexternalsRepository; | |
| let resolvedAtlasExternalsBaseRef = defaults.atlasexternalsBaseRef; | |
| let resolvedThreads = defaults.threads; | |
| let resolvedEvents = defaults.events; | |
| let resolvedRepetitions = defaults.repetitions; | |
| if (context.eventName === "workflow_dispatch") { | |
| if (!(await hasWriteLikeRepoAccess(context.actor))) { | |
| core.setFailed(`workflow_dispatch is restricted to users with write access on ${context.repo.owner}/${context.repo.repo}`); | |
| return; | |
| } | |
| if (context.ref !== "refs/heads/master") { | |
| core.setFailed("workflow_dispatch performance runs must be launched from master"); | |
| return; | |
| } | |
| const inputs = context.payload.inputs || {}; | |
| resolvedMode = inputs.mode || ""; | |
| resolvedAdeptRepo = `${context.repo.owner}/${context.repo.repo}`; | |
| resolvedAdeptRef = context.sha; | |
| resolvedAthenaBaseRef = inputs.athena_base_ref || defaults.athenaBaseRef; | |
| resolvedAthenaGpuRepository = inputs.athena_gpu_repository || defaults.athenaGpuRepository; | |
| resolvedAthenaGpuRef = inputs.athena_gpu_ref || defaults.athenaGpuRef; | |
| resolvedAtlasExternalsBaseRef = inputs.atlasexternals_base_ref || defaults.atlasexternalsBaseRef; | |
| resolvedThreads = inputs.threads || defaults.threads; | |
| resolvedEvents = inputs.events || defaults.events; | |
| resolvedRepetitions = inputs.repetitions || defaults.repetitions; | |
| } else { | |
| if (context.payload.issue?.pull_request == null) { | |
| core.notice("Ignoring non-PR issue comment"); | |
| return; | |
| } | |
| const body = (context.payload.comment.body || "").trim(); | |
| const match = body.match(/^\/run-(?:benchmark|performance)\s+(mono|split)\s*$/); | |
| if (!match) { | |
| core.notice(`Ignoring comment '${body}'`); | |
| return; | |
| } | |
| const assoc = context.payload.comment.author_association || "NONE"; | |
| const trusted = ["OWNER", "MEMBER"].includes(assoc); | |
| if (!trusted) { | |
| core.notice(`Ignoring benchmark trigger from untrusted role ${assoc}`); | |
| return; | |
| } | |
| const prNumber = context.payload.issue.number; | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| }); | |
| output("should_run", "true"); | |
| output("comment_back", "true"); | |
| resolvedMode = match[1]; | |
| output("pr_number", String(pr.number)); | |
| resolvedAdeptRepo = pr.head.repo.full_name; | |
| resolvedAdeptRef = pr.head.sha; | |
| } | |
| if (!resolvedMode) { | |
| core.setFailed("No benchmark mode was resolved"); | |
| return; | |
| } | |
| if (!resolvedAdeptRepo || !resolvedAdeptRef) { | |
| core.setFailed("No AdePT repository/ref was resolved"); | |
| return; | |
| } | |
| if (!resolvedAthenaGpuRepository) { | |
| core.setFailed("No Athena GPU repository configured; set repo variable ADEPT_ATHENA_GPU_REPOSITORY or pass an input"); | |
| return; | |
| } | |
| if (!resolvedAthenaGpuRef) { | |
| core.setFailed("No Athena GPU ref configured; set repo variable ADEPT_ATHENA_GPU_REF or pass an input"); | |
| return; | |
| } | |
| output("should_run", "true"); | |
| output("mode", resolvedMode); | |
| output("adept_repo", resolvedAdeptRepo); | |
| output("adept_ref", resolvedAdeptRef); | |
| output("athena_repository", resolvedAthenaRepository); | |
| output("athena_base_ref", resolvedAthenaBaseRef); | |
| output("athena_gpu_repository", resolvedAthenaGpuRepository); | |
| output("athena_gpu_ref", resolvedAthenaGpuRef); | |
| output("atlasexternals_repository", resolvedAtlasExternalsRepository); | |
| output("atlasexternals_ref", resolvedAtlasExternalsBaseRef); | |
| output("threads", resolvedThreads); | |
| output("events", resolvedEvents); | |
| output("repetitions", resolvedRepetitions); | |
| benchmark: | |
| name: Athena Performance | |
| needs: prepare | |
| if: needs.prepare.outputs.should_run == 'true' | |
| runs-on: | |
| - self-hosted | |
| - linux | |
| - x64 | |
| - gpu | |
| - benchmark | |
| - adept | |
| timeout-minutes: 480 | |
| env: | |
| RESULTS_FILE: ${{ github.workspace }}/athena-performance-results.env | |
| ARTIFACTS_DIR: ${{ github.workspace }}/athena-performance-results/run-${{ github.run_id }}-${{ needs.prepare.outputs.mode }} | |
| BENCHMARK_CONTAINER_IMAGE: adept/athena-benchmark:${{ github.run_id }} | |
| steps: | |
| - name: Checkout AdePT under test | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: ${{ needs.prepare.outputs.adept_repo }} | |
| ref: ${{ needs.prepare.outputs.adept_ref }} | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| submodules: recursive | |
| - name: Show runner basics | |
| run: | | |
| set -o pipefail | |
| cat /etc/os-release | sed -n '1,6p' || exit 1 | |
| echo "event=${GITHUB_EVENT_NAME}" | |
| echo "mode=${{ needs.prepare.outputs.mode }}" | |
| echo "adept_repo=${{ needs.prepare.outputs.adept_repo }}" | |
| echo "adept_ref=${{ needs.prepare.outputs.adept_ref }}" | |
| echo "hostname=$(hostname)" | |
| echo "user=$(whoami)" | |
| echo "pwd=$(pwd)" | |
| id | |
| - name: Pre-touch required CVMFS repos on host | |
| run: | | |
| set -o pipefail | |
| ls /cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase/user/atlasLocalSetup.sh || exit 1 | |
| g4_path=/cvmfs/atlas-nightlies.cern.ch/repo/sw/main--simGPU_AthSimulation_x86_64-el9-gcc14-opt/Geant4 | |
| release_data_root=/cvmfs/atlas.cern.ch/repo/sw/software/25.0/atlas/offline/ReleaseData/v20 | |
| ls "${g4_path}" || exit 1 | |
| ls "${g4_path}"/share/geant4*/data/G4ENSDFSTATE/ENSDFSTATE.dat || exit 1 | |
| ls "${release_data_root}/LArG4EC/HVEMECMap_v02.dat" || exit 1 | |
| if [ -d /afs ]; then | |
| ls /afs >/dev/null || exit 1 | |
| echo "/afs is present on the host and will be mounted into the container" | |
| else | |
| echo "/afs is not present on the host" | |
| fi | |
| - name: Check GPU visibility | |
| run: | | |
| set -o pipefail | |
| nvidia-smi || exit 1 | |
| - name: Check Docker basics | |
| run: | | |
| set -o pipefail | |
| docker --version || exit 1 | |
| - name: Build benchmark container image | |
| run: | | |
| set -o pipefail | |
| docker build \ | |
| --pull \ | |
| -t "${BENCHMARK_CONTAINER_IMAGE}" \ | |
| -f test/athena-performance/Dockerfile.alma9-man \ | |
| . | |
| - name: Run Athena performance benchmark in workflow-built container | |
| id: run_perf | |
| continue-on-error: true | |
| shell: bash --noprofile --norc {0} | |
| env: | |
| PERF_MODE: ${{ needs.prepare.outputs.mode }} | |
| ADEPT_REPOSITORY: ${{ needs.prepare.outputs.adept_repo }} | |
| ADEPT_REF: ${{ needs.prepare.outputs.adept_ref }} | |
| ATHENA_REPOSITORY: ${{ needs.prepare.outputs.athena_repository }} | |
| ATHENA_BASE_REF: ${{ needs.prepare.outputs.athena_base_ref }} | |
| ATHENA_GPU_REPOSITORY: ${{ needs.prepare.outputs.athena_gpu_repository }} | |
| ATHENA_GPU_REF: ${{ needs.prepare.outputs.athena_gpu_ref }} | |
| ATLAS_EXTERNALS_REPOSITORY: ${{ needs.prepare.outputs.atlasexternals_repository }} | |
| ATLAS_EXTERNALS_BASE_REF: ${{ needs.prepare.outputs.atlasexternals_ref }} | |
| PERF_THREADS: ${{ needs.prepare.outputs.threads }} | |
| PERF_EVENTS: ${{ needs.prepare.outputs.events }} | |
| PERF_REPETITIONS: ${{ needs.prepare.outputs.repetitions }} | |
| CERN_GITLAB_USERNAME: ${{ secrets.CERN_GITLAB_USERNAME }} | |
| CERN_GITLAB_TOKEN: ${{ secrets.CERN_GITLAB_TOKEN }} | |
| run: | | |
| set -o pipefail | |
| rm -f "${RESULTS_FILE}" | |
| rm -rf "${ARTIFACTS_DIR}" | |
| mkdir -p "${ARTIFACTS_DIR}" | |
| docker_group_args=() | |
| for group_name in video render; do | |
| if getent group "${group_name}" >/dev/null 2>&1; then | |
| docker_group_args+=(--group-add "$(getent group "${group_name}" | cut -d: -f3)") | |
| fi | |
| done | |
| docker_mount_args=( | |
| -v /build:/build | |
| -v /ec/conf:/ec/conf | |
| -v /cvmfs:/cvmfs:ro,rslave | |
| -v "${GITHUB_WORKSPACE}:/work/AdePT" | |
| ) | |
| if [ -d /afs ]; then | |
| docker_mount_args+=(-v /afs:/afs:ro,rslave) | |
| fi | |
| docker run --rm --pull never \ | |
| --gpus all \ | |
| --user "$(id -u):$(id -g)" \ | |
| "${docker_group_args[@]}" \ | |
| --security-opt seccomp=unconfined \ | |
| "${docker_mount_args[@]}" \ | |
| -e HOME=/tmp/adeptci-home \ | |
| -e PERF_MODE \ | |
| -e ADEPT_REPOSITORY \ | |
| -e ADEPT_REF \ | |
| -e ATHENA_REPOSITORY \ | |
| -e ATHENA_BASE_REF \ | |
| -e ATHENA_GPU_REPOSITORY \ | |
| -e ATHENA_GPU_REF \ | |
| -e ATLAS_EXTERNALS_REPOSITORY \ | |
| -e ATLAS_EXTERNALS_BASE_REF \ | |
| -e PERF_THREADS \ | |
| -e PERF_EVENTS \ | |
| -e PERF_REPETITIONS \ | |
| -e GITHUB_RUN_ID \ | |
| -e CERN_GITLAB_USERNAME \ | |
| -e CERN_GITLAB_TOKEN \ | |
| -w /work/AdePT \ | |
| "${BENCHMARK_CONTAINER_IMAGE}" \ | |
| bash -lc ' | |
| set -o pipefail | |
| mkdir -p "${HOME}" | |
| git config --global --add safe.directory /work/AdePT | |
| container_results_file="/work/AdePT/athena-performance-results.env" | |
| container_artifacts_dir="/work/AdePT/athena-performance-results/run-${GITHUB_RUN_ID}-${PERF_MODE}" | |
| bash ./test/athena-performance/run_athena_performance.sh \ | |
| --mode "${PERF_MODE}" \ | |
| --adept-repository "${ADEPT_REPOSITORY}" \ | |
| --adept-ref "${ADEPT_REF}" \ | |
| --athena-repository "${ATHENA_REPOSITORY}" \ | |
| --athena-base-ref "${ATHENA_BASE_REF}" \ | |
| --athena-gpu-repository "${ATHENA_GPU_REPOSITORY}" \ | |
| --athena-gpu-ref "${ATHENA_GPU_REF}" \ | |
| --atlasexternals-repository "${ATLAS_EXTERNALS_REPOSITORY}" \ | |
| --atlasexternals-base-ref "${ATLAS_EXTERNALS_BASE_REF}" \ | |
| --threads "${PERF_THREADS}" \ | |
| --events "${PERF_EVENTS}" \ | |
| --repetitions "${PERF_REPETITIONS}" \ | |
| --build-root "/tmp/adept-athena-performance/run-${GITHUB_RUN_ID}-${PERF_MODE}" \ | |
| --artifacts-dir "${container_artifacts_dir}" \ | |
| --results-file "${container_results_file}" | |
| ' | |
| - name: Remove benchmark container image | |
| if: always() | |
| run: | | |
| docker image rm -f "${BENCHMARK_CONTAINER_IMAGE}" || true | |
| - name: Load benchmark results | |
| if: always() && hashFiles(env.RESULTS_FILE) != '' | |
| run: | | |
| set -o pipefail | |
| # shellcheck disable=SC1091 | |
| source "${RESULTS_FILE}" || exit 1 | |
| host_summary_markdown_file="${SUMMARY_MARKDOWN_FILE:-}" | |
| case "${host_summary_markdown_file}" in | |
| /work/AdePT/*) | |
| host_summary_markdown_file="${GITHUB_WORKSPACE}/${host_summary_markdown_file#/work/AdePT/}" | |
| ;; | |
| esac | |
| { | |
| echo "PERF_STATUS=${PERF_STATUS:-1}" | |
| echo "SUMMARY_MARKDOWN_FILE=${host_summary_markdown_file}" | |
| } >> "${GITHUB_ENV}" | |
| - name: Write summary | |
| if: always() | |
| run: | | |
| if [ -n "${SUMMARY_MARKDOWN_FILE:-}" ] && [ -f "${SUMMARY_MARKDOWN_FILE}" ]; then | |
| cat "${SUMMARY_MARKDOWN_FILE}" >> "${GITHUB_STEP_SUMMARY}" | |
| else | |
| { | |
| echo "### Athena Performance Benchmark (${{ needs.prepare.outputs.mode }})" | |
| echo | |
| echo "The benchmark failed before producing a summary." | |
| } >> "${GITHUB_STEP_SUMMARY}" | |
| fi | |
| - name: Upload benchmark artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: athena-performance-${{ needs.prepare.outputs.mode }}-${{ github.run_id }} | |
| path: ${{ env.ARTIFACTS_DIR }} | |
| if-no-files-found: warn | |
| - name: Comment on PR with results | |
| if: always() && needs.prepare.outputs.comment_back == 'true' | |
| uses: actions/github-script@v8 | |
| env: | |
| SUMMARY_MARKDOWN_FILE: ${{ env.SUMMARY_MARKDOWN_FILE }} | |
| RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| with: | |
| script: | | |
| const fs = require("fs"); | |
| let body = ""; | |
| if (process.env.SUMMARY_MARKDOWN_FILE && fs.existsSync(process.env.SUMMARY_MARKDOWN_FILE)) { | |
| body = fs.readFileSync(process.env.SUMMARY_MARKDOWN_FILE, "utf8").trim(); | |
| } else { | |
| body = `### Athena Performance Benchmark (${context.payload.comment?.body || "${{ needs.prepare.outputs.mode }}"})\n\nThe benchmark failed before producing a summary.`; | |
| } | |
| body += `\n\n- Actions run: ${process.env.RUN_URL}`; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: Number("${{ needs.prepare.outputs.pr_number }}"), | |
| body, | |
| }); | |
| - name: Clean benchmark workspace outputs | |
| if: always() | |
| run: | | |
| rm -rf "${ARTIFACTS_DIR}" "${RESULTS_FILE}" | |
| - name: Fail workflow if benchmark failed | |
| if: always() | |
| run: | | |
| test "${PERF_STATUS:-1}" -eq 0 |