Skip to content

[GPU] Add initial acceleration for AMD (ROCm 7.2.4, MiGraphX)#752

Open
Schaka wants to merge 2 commits into
NeptuneHub:mainfrom
Schaka:main
Open

[GPU] Add initial acceleration for AMD (ROCm 7.2.4, MiGraphX)#752
Schaka wants to merge 2 commits into
NeptuneHub:mainfrom
Schaka:main

Conversation

@Schaka

@Schaka Schaka commented Jul 12, 2026

Copy link
Copy Markdown

AS-IS

AMD GPUs fall back to CPU acceleration for inference

TO-BE

Not everything is GPU-accelerated on AMD:

  • musicnn — GPU (MIGraphX)
  • CLAP audio (DCLAP) — CPU only; its Resize op with keep_aspect_ratio_policy isn't parseable by MIGraphX as far as I can tell
  • Whisper — GPU, but via faster-whisper/CTranslate2 instead of ONNX, since MIGraphX can't parse the decoder's dynamic If/KV-cache subgraphs => hopefully this change isn't too invasive
  • Clustering (RAPIDS cuML) — CPU only, no ROCm port exists => only things I found for cuML on AMD were experimental and need building ourselves

Essentially, this means some acceleration is achieved, but there's unfortunately still some CPU bottlenecks. I'm not sure they're solveable this point in time.

Test

I ran the deployment YAML I added locally to start a worker. The image builds, the worker is relatively fast on my RX 9070 XT and I confirmed via rocm-smi that it's absolutely using the GPU.

Definitely need some people to test this on different architectures.

Other useful information

I only have the RNDA4 card atm and I'm building with the latest ROCm version. I cannot test older hardware or use an older ROCm base version. So there's a chance this PR isn't good enough to be accepted.

I'm also unsure about building the image. I'm fairly certain building via GitHub CI will fail due to the sheer size of the image. I wasn't able to figure out how you currently build and push your images.

Checklist

Type of change:

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Breaking change

Tested on architecture:

  • Intel
  • ARM
  • -noavx2
  • NVIDIA image
  • ROCm image 😄

Tested on media server:

  • Navidrome
  • Jellyfin
  • Emby
  • Lyrion

Updated:

  • Documentation
  • Unit test
  • Integration test

Other:

  • CONTRIBUTING.md read and accepted
  • Checked performance on a big library (> 150k songs) works without issues => it works pretty much the same as before, I'm not sure what I could've tested different, so I'm leaving this one unchecked.

Related ISSUE: Closes #xx

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
D Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces experimental support for AMD GPUs (ROCm / MIGraphX) by adding a dedicated Dockerfile-rocm, a docker-compose configuration, and documentation. It also implements a new faster-whisper transcription backend to bypass MIGraphX's limitations with the ONNX Whisper decoder, and updates ONNX session helpers to support the MIGraphXExecutionProvider. Feedback on these changes highlights a silent package installation failure in the Dockerfile due to a non-existent postgresql-client-18 package, a fragile hardcoded Python version tag (cp312) in the wheel installation step, and a potential unhandled exception during lazy iteration of the transcription generator in whisper_faster.py.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread Dockerfile-rocm
Comment on lines +31 to +53
RUN set -ux; \
n=0; \
until [ "$n" -ge 5 ]; do \
if DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv \
libfftw3-double3 libyaml-0-2 libsamplerate0 libsndfile1 \
libopenblas0 liblapack3 libgomp1 libpq5 \
ffmpeg wget curl supervisor procps \
git vim redis-tools strace iputils-ping \
postgresql-common ca-certificates tzdata \
&& /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y \
&& DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends postgresql-client-18; then \
break; \
fi; \
n=$((n+1)); \
echo "apt-get attempt $n failed - retrying in $((n*n))s"; \
sleep $((n*n)); \
done; \
rm -rf /var/lib/apt/lists/* && \
apt-get remove -y python3-numpy || true && \
apt-get autoremove -y || true && \
rm -f /usr/lib/python3.*/EXTERNALLY-MANAGED

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The package postgresql-client-18 does not exist (PostgreSQL 17 is the latest stable release, and version 18 is not yet released or packaged). This causes the apt-get install command to fail.

Furthermore, because of how the retry loop is structured, this failure is silently ignored:

  1. When apt-get install ... postgresql-client-18 fails, the if condition evaluates to false.
  2. The loop body proceeds to increment n, print a message, and run sleep $((n*n)).
  3. Since sleep exits with status 0, the loop continues.
  4. After 5 failed attempts, the until condition [ "$n" -ge 5 ] is met, and the loop terminates.
  5. In bash, the exit status of the loop is the exit status of the last command executed in its body, which is sleep (status 0).
  6. The rest of the RUN instruction executes, and the Docker build succeeds without actually installing postgresql-client!

Suggestions:

  1. Change postgresql-client-18 to a valid version. Since the docker-compose file uses postgres:15-alpine, postgresql-client-15 (or postgresql-client-16 / postgresql-client-17) should be used.
  2. Refactor the retry loop to explicitly fail the build if all attempts fail.
RUN set -ux; \
    n=0; \
    success=false; \
    until [ "$n" -ge 5 ]; do \
        if DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
            python3 python3-pip python3-venv \
            libfftw3-double3 libyaml-0-2 libsamplerate0 libsndfile1 \
            libopenblas0 liblapack3 libgomp1 libpq5 \
            ffmpeg wget curl supervisor procps \
            git vim redis-tools strace iputils-ping \
            postgresql-common ca-certificates tzdata \
            && /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y \
            && DEBIAN_FRONTEND=noninteractive apt-get update \
            && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends postgresql-client-15; then \
            success=true; \
            break; \
        fi; \
        n=$((n+1)); \
        echo "apt-get attempt $n failed - retrying in $((n*n))s"; \
        sleep $((n*n)); \
    done; \
    if [ "$success" = false ]; then \
        echo "Failed to install packages after 5 attempts"; \
        exit 1; \
    fi; \
    rm -rf /var/lib/apt/lists/* && \
    apt-get remove -y python3-numpy || true && \
    apt-get autoremove -y || true && \
    rm -f /usr/lib/python3.*/EXTERNALLY-MANAGED

Comment thread Dockerfile-rocm
Comment on lines +81 to +82
whl="$(find ct2whl -name "ctranslate2-${CT2_VERSION}-cp312-cp312-*.whl" | head -1)"; \
test -n "$whl"; \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding the Python version as cp312 in the wheel filename pattern makes the build fragile if the base image's Python version changes in the future (e.g., if they bump the base image to a newer ROCm/Ubuntu version with Python 3.13). We can dynamically detect the Python version from the virtual environment to make this more robust.

    py_ver="$("$VIRTUAL_ENV/bin/python3" -c 'import sys; print(f"cp{sys.version_info.major}{sys.version_info.minor}")')"; \
    whl="$(find ct2whl -name "ctranslate2-${CT2_VERSION}-${py_ver}-${py_ver}-*.whl" | head -1)"; \
    test -n "$whl";

Comment thread lyrics/whisper_faster.py
Comment on lines +114 to +120
for seg in segments: # generator - consuming it runs the decode
t = (seg.text or "").strip()
if t:
texts.append(t)
lp = getattr(seg, "avg_logprob", None)
if lp is not None:
logprobs.append(float(lp))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since segments returned by model.transcribe is a generator, the actual transcription decoding happens lazily during iteration. Any runtime exception (such as a GPU out-of-memory or CTranslate2 error) will be raised inside this loop. Wrapping the loop in a try...except block allows the transcriber to degrade gracefully and return whatever text was successfully transcribed up to that point, rather than failing the entire transcription task.

Suggested change
for seg in segments: # generator - consuming it runs the decode
t = (seg.text or "").strip()
if t:
texts.append(t)
lp = getattr(seg, "avg_logprob", None)
if lp is not None:
logprobs.append(float(lp))
try:
for seg in segments: # generator - consuming it runs the decode
t = (seg.text or "").strip()
if t:
texts.append(t)
lp = getattr(seg, "avg_logprob", None)
if lp is not None:
logprobs.append(float(lp))
except Exception as exc:
logger.error("Error during faster-whisper transcription decoding: %s", exc)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant