From 70ee58cce04e850ed4a037d1bce3cc9307704f68 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sat, 26 Sep 2026 02:48:27 +0000 Subject: [PATCH 1/2] perf-analyzer: Add version 2.60.0 Build the Triton Performance Analyzer CLI from the r26.04 head of triton-inference-server/perf_analyzer (the commit the PyPI 2.60.0 binary reports) without CUDA, and package it with the repo's own pyproject.toml as a py3-none-manylinux_2_39_riscv64 wheel. --- .github/workflows/build-perf-analyzer.yml | 222 ++++++++++++++++++ docs/packages/perf-analyzer.yaml | 10 + .../0001-forward-third-party-repo-tag.patch | 44 ++++ 3 files changed, 276 insertions(+) create mode 100644 .github/workflows/build-perf-analyzer.yml create mode 100644 docs/packages/perf-analyzer.yaml create mode 100644 patches/perf-analyzer/2.60.0/0001-forward-third-party-repo-tag.patch diff --git a/.github/workflows/build-perf-analyzer.yml b/.github/workflows/build-perf-analyzer.yml new file mode 100644 index 00000000000..53274abbf76 --- /dev/null +++ b/.github/workflows/build-perf-analyzer.yml @@ -0,0 +1,222 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# perf_analyzer tags no release; PyPI's 2.60.0 wheels were built from the r26.04 +# head, the commit their binary reports in `perf_analyzer --version`. Upstream's +# wheel build runs in NVIDIA's internal CI, so this follows the documented +# source build (docs/install.md) and packages the binary with the repo's own +# pyproject.toml. +name: Build perf-analyzer wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/perf-analyzer.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-perf-analyzer.yml' + - 'docs/packages/perf-analyzer.yaml' + - 'patches/perf-analyzer/**' + push: + branches: [main] + paths: + - '.github/workflows/build-perf-analyzer.yml' + - 'docs/packages/perf-analyzer.yaml' + - 'patches/perf-analyzer/**' + +run-name: build-perf-analyzer ${{ inputs.version && format('- {0}', inputs.version) || '' }} + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + # Move together with the version in docs/packages/perf-analyzer.yaml. + PERF_ANALYZER_REF: 3a49a23abf06242ed170ee479b5e2e080e4250e6 + TRITON_RELEASE_BRANCH: r26.04 + # Rocky 10 packages no RapidJSON, and its 1.1.0 release does not compile with GCC 14. + RAPIDJSON_REF: 24b5e7a8b27f42fa16b96fc70aade9106cf7102f + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: perf-analyzer + version: ${{ inputs.version }} + + build_wheels: + name: Build perf-analyzer ${{ matrix.version }} py3-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 720 + needs: [setup] + if: needs.setup.outputs.versions != '[]' + + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + + env: + PERF_ANALYZER_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout triton-inference-server/perf_analyzer @ ${{ env.PERF_ANALYZER_REF }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: triton-inference-server/perf_analyzer + ref: ${{ env.PERF_ANALYZER_REF }} + path: perf_analyzer + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Apply patches + working-directory: perf_analyzer + run: | + for p in ../python-wheels/patches/perf-analyzer/${PERF_ANALYZER_VERSION}/*.patch; do + git apply --verbose "$p" + done + + - name: Pull manylinux_riscv64 image + run: docker pull "${MANYLINUX_RISCV64_IMAGE}" + + - name: Build wheel + run: | + mkdir -p wheelhouse + set -o pipefail + docker run --rm -i --network=host \ + -v "${GITHUB_WORKSPACE}:/work" \ + -w /work \ + -e PERF_ANALYZER_VERSION \ + -e TRITON_RELEASE_BRANCH \ + -e RAPIDJSON_REF \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash <<'SCRIPT' 2>&1 | tee build.log + set -eux + + dnf install -y --disablerepo=extras --setopt=install_weak_deps=False openssl-devel zlib-ng-compat-devel + export PATH="/opt/python/cp312-cp312/bin:${PATH}" + git config --global --add safe.directory '*' + + # c-ares, as pinned by gRPC v1.54.3, declares a cmake_minimum_required below CMake 4's floor. + export CMAKE_POLICY_VERSION_MINIMUM=3.5 + # abseil lts_20230125 reads the cycle counter with rdcycle, which Linux 6.6+ traps as SIGILL + # on riscv64; abseil dropped that path for RISC-V upstream (abseil/abseil-cpp#1644). + export CXXFLAGS="-DABSL_USE_UNSCALED_CYCLECLOCK=0" + + git clone -q https://github.com/Tencent/rapidjson /tmp/rapidjson + git -C /tmp/rapidjson checkout -q "${RAPIDJSON_REF}" + cmake -S /tmp/rapidjson -B /tmp/rapidjson/build \ + -DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD_TESTS=OFF + cmake --install /tmp/rapidjson/build + + cd /work/perf_analyzer + PA_VERSION="$(python -c 'import runpy; print(runpy.run_path("src/perf_analyzer/__init__.py")["__version__"])')" + test "${PA_VERSION}" = "${PERF_ANALYZER_VERSION}" + + cmake -B /tmp/build -S . \ + -DTRITON_ENABLE_GPU=OFF \ + -DTRITON_CLIENT_REPO_TAG="${TRITON_RELEASE_BRANCH}" \ + -DTRITON_COMMON_REPO_TAG="${TRITON_RELEASE_BRANCH}" \ + -DTRITON_CORE_REPO_TAG="${TRITON_RELEASE_BRANCH}" \ + -DTRITON_THIRD_PARTY_REPO_TAG="${TRITON_RELEASE_BRANCH}" + cmake --build /tmp/build --parallel "$(nproc)" + + mkdir /tmp/unit-tests + (cd /tmp/unit-tests && /usr/local/bin/perf_analyzer_unit_tests) + + CC=/tmp/build/cc_clients/src/cc-clients + TP=/tmp/build/cc_clients/src/cc-clients-build/_deps/repo-third-party-build + PA=/tmp/build/perf_analyzer/src/perf-analyzer-build/_deps + install -m 0644 "${CC}/LICENSE" LICENSE.triton-client + install -m 0644 "${PA}/repo-common-src/LICENSE" LICENSE.triton-common + install -m 0644 "${TP}/grpc-repo/src/grpc/LICENSE" LICENSE.grpc + install -m 0644 "${TP}/grpc-repo/src/grpc/third_party/abseil-cpp/LICENSE" LICENSE.abseil-cpp + install -m 0644 "${TP}/grpc-repo/src/grpc/third_party/protobuf/LICENSE" LICENSE.protobuf + install -m 0644 "${TP}/grpc-repo/src/grpc/third_party/re2/LICENSE" LICENSE.re2 + install -m 0644 "${TP}/grpc-repo/src/grpc/third_party/cares/cares/LICENSE.md" LICENSE.c-ares + install -m 0644 "${TP}/curl/src/curl/COPYING" LICENSE.curl + install -m 0644 "${PA}/libb64-src/LICENSE.md" LICENSE.libb64 + install -m 0644 /tmp/rapidjson/license.txt LICENSE.rapidjson + install -m 0644 "$(rpm -qL openssl-libs | grep -m1 '/LICENSE')" LICENSE.openssl + + install -D -m 0755 /usr/local/bin/perf_analyzer src/perf_analyzer/bin/perf_analyzer + python -m pip install -q -U pip wheel + python -m pip wheel --no-deps -w dist . + # The payload is a CLI the pure-Python wrapper execs, which is why upstream's wheels are py3-none. + python -m wheel tags --platform-tag linux_riscv64 --remove dist/*.whl + auditwheel repair --plat manylinux_2_39_riscv64 -w /work/wheelhouse dist/*.whl + SCRIPT + + - name: Upload build log + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: perf-analyzer-${{ env.PERF_ANALYZER_VERSION }}-build-log + path: build.log + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: perf-analyzer-${{ env.PERF_ANALYZER_VERSION }}-py3-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + - name: Test wheel + run: | + docker run --rm -i --network=host \ + -v "${GITHUB_WORKSPACE}:/work" \ + -e PERF_ANALYZER_REF \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash <<'SCRIPT' + set -eux + + for py in cp312-cp312 cp313-cp313 cp314-cp314 cp314-cp314t; do + venv="/tmp/venv-${py}" + "/opt/python/${py}/bin/python" -m venv "${venv}" + "${venv}/bin/python" -m pip install -q --only-binary=:all: /work/wheelhouse/*.whl + + cd /tmp + "${venv}/bin/perf_analyzer" --version | tee /tmp/version.txt + grep -F "(commit ${PERF_ANALYZER_REF:0:7}" /tmp/version.txt + "${venv}/bin/python" - <<'PY' + import importlib.metadata as md + + names = {p.name for p in md.files("perf-analyzer") if "licenses" in p.parts} + expected = { + "LICENSE", "LICENSE.triton-client", "LICENSE.triton-common", "LICENSE.grpc", + "LICENSE.abseil-cpp", "LICENSE.protobuf", "LICENSE.re2", "LICENSE.c-ares", + "LICENSE.curl", "LICENSE.libb64", "LICENSE.rapidjson", "LICENSE.openssl", + } + assert expected <= names, sorted(expected - names) + PY + done + SCRIPT + + publish: + name: Publish perf-analyzer ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + artifact-pattern: perf-analyzer-${{ matrix.version }}-py3-manylinux_riscv64 diff --git a/docs/packages/perf-analyzer.yaml b/docs/packages/perf-analyzer.yaml new file mode 100644 index 00000000000..189da2dfbfd --- /dev/null +++ b/docs/packages/perf-analyzer.yaml @@ -0,0 +1,10 @@ +package-name: perf-analyzer +source-code: https://github.com/triton-inference-server/perf_analyzer +license: BSD-3-Clause +versions: +- version: 2.60.0 + patched: true + comment: >- + Built without CUDA shared-memory support (TRITON_ENABLE_GPU=OFF). The patch pins the + Triton third_party repository to the release branch so gRPC matches the upstream wheels, + and the wheel ships the licences of the statically linked dependencies and bundled OpenSSL. diff --git a/patches/perf-analyzer/2.60.0/0001-forward-third-party-repo-tag.patch b/patches/perf-analyzer/2.60.0/0001-forward-third-party-repo-tag.patch new file mode 100644 index 00000000000..6b498c2c8b0 --- /dev/null +++ b/patches/perf-analyzer/2.60.0/0001-forward-third-party-repo-tag.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sat, 26 Sep 2026 00:00:00 +0000 +Subject: [PATCH] Forward TRITON_THIRD_PARTY_REPO_TAG to the cc-clients build + +The top-level superbuild exposes TRITON_CLIENT_REPO_TAG, +TRITON_COMMON_REPO_TAG and TRITON_CORE_REPO_TAG, but not the tag of +triton-inference-server/third_party, which the client repo fetches with +its own "main" default. A release-branch build therefore compiles +whatever gRPC/protobuf/abseil third_party main pins at build time: r26.04 +pins gRPC v1.54.3 (abseil lts_20230125, the one linked into the 2.60.0 +wheels on PyPI), while third_party main has since moved to gRPC v1.81.1. + +Add the cache variable, defaulting to "main" like its siblings, and pass +it through so the release branch can be built reproducibly. + +Upstream-Status: To upstream [not submitted from this automated port run; needs a pull request against triton-inference-server/perf_analyzer] +--- + CMakeLists.txt | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index b533316..5adaf96 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -61,6 +61,7 @@ set(TRITON_REPO_ORGANIZATION "https://github.com/triton-inference-server" CACHE + set(TRITON_COMMON_REPO_TAG "main" CACHE STRING "Tag for triton-inference-server/common repo") + set(TRITON_CORE_REPO_TAG "main" CACHE STRING "Tag for triton-inference-server/core repo") + set(TRITON_CLIENT_REPO_TAG "main" CACHE STRING "Tag for triton-inference-server/core repo") ++set(TRITON_THIRD_PARTY_REPO_TAG "main" CACHE STRING "Tag for triton-inference-server/third_party repo") + + # + # Install locations +@@ -97,6 +98,7 @@ ExternalProject_Add( + -DTRITON_REPO_ORGANIZATION:STRING=${TRITON_REPO_ORGANIZATION} + -DTRITON_CORE_REPO_TAG:STRING=${TRITON_CORE_REPO_TAG} + -DTRITON_COMMON_REPO_TAG:STRING=${TRITON_COMMON_REPO_TAG} ++ -DTRITON_THIRD_PARTY_REPO_TAG:STRING=${TRITON_THIRD_PARTY_REPO_TAG} + -DTRITON_ENABLE_CC_HTTP:BOOL=${TRITON_ENABLE_CC_HTTP} + -DTRITON_ENABLE_CC_GRPC:BOOL=${TRITON_ENABLE_CC_GRPC} + -DTRITON_ENABLE_PYTHON_HTTP:BOOL=OFF +-- +2.43.0 + From 3758e73facece5609f09ad3883ea3cdaeb3e21e3 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sat, 26 Sep 2026 06:18:16 +0000 Subject: [PATCH 2/2] perf-analyzer: Skip the RapidJSON-version-specific error-code subcase The unit test asserts RapidJSON 1.1.0's error code for an unescaped control character (kParseErrorStringEscapeInvalid, 10); RapidJSON after Tencent/rapidjson@85500e8 reports kParseErrorStringInvalidEncoding (12), and 1.1.0 itself does not compile with GCC 14. --- .github/workflows/build-perf-analyzer.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-perf-analyzer.yml b/.github/workflows/build-perf-analyzer.yml index 53274abbf76..1db20ca6c83 100644 --- a/.github/workflows/build-perf-analyzer.yml +++ b/.github/workflows/build-perf-analyzer.yml @@ -135,7 +135,8 @@ jobs: cmake --build /tmp/build --parallel "$(nproc)" mkdir /tmp/unit-tests - (cd /tmp/unit-tests && /usr/local/bin/perf_analyzer_unit_tests) + # Asserts RapidJSON 1.1.0's error code 10, which Tencent/rapidjson@85500e8 changed to 12. + (cd /tmp/unit-tests && /usr/local/bin/perf_analyzer_unit_tests --subcase-exclude="invalid json") CC=/tmp/build/cc_clients/src/cc-clients TP=/tmp/build/cc_clients/src/cc-clients-build/_deps/repo-third-party-build