GPU offloading preconditioner#4953
Conversation
First pass at firedrake-configure for GPU-enabled PETSc builds, as well as a github actions test. OffloadPC is more or less unchanged from olender's work, but with a few extra checks for whether GPU offload is available. No dedicated GPU tests yet. CUDA only so far.
Have offload preconditioner warn instead of crash if a GPU could not be initialised for whatever reason. Remove --with-cuda-arch flag from PETSc config in firedrake-configure.
| y_cu = PETSc.Vec() # begin | ||
| y_cu.createCUDAWithArrays(y) |
There was a problem hiding this comment.
| y_cu = PETSc.Vec() # begin | |
| y_cu.createCUDAWithArrays(y) | |
| y_cu = PETSc.Vec().createCUDAWithArrays(y) |
| x_cu = PETSc.Vec() | ||
| # Passing a vec into another vec doesnt work because original is locked | ||
| x_cu.createCUDAWithArrays(x.array_r) |
There was a problem hiding this comment.
| x_cu = PETSc.Vec() | |
| # Passing a vec into another vec doesnt work because original is locked | |
| x_cu.createCUDAWithArrays(x.array_r) | |
| # Passing a vec into another vec doesnt work because original is locked | |
| x_cu = PETSc.Vec().createCUDAWithArrays(x.array_r) |
| with PETSc.Log.Event("Event: solve"): | ||
| self.pc.apply(x_cu, y_cu) | ||
| # Calling data to synchronize vector | ||
| tmp = y_cu.array_r # noqa: F841 |
There was a problem hiding this comment.
| tmp = y_cu.array_r # noqa: F841 | |
| y_cu.array_r |
|
|
||
| def initialize(self, pc): | ||
| # Check if our PETSc installation is GPU enabled | ||
| super().initialize(pc) |
There was a problem hiding this comment.
Should we be calling AssembledPC.initialize()? It seems to me that this will trigger reassembly and we are not really making use of it.
There was a problem hiding this comment.
I'm working my way through understanding how PETSc manages GPU devices, and it does appear that when I drop super().initialize(pc) I get:
[0] MatConvert() at /g/data/fp50/admin/gpu-testing/apps/petsc/3.24.4/src/mat/interface/matrix.c:4390
[0] Object is in wrong state
[0] Not for unassembled matrix
So the matrix does have to be assembled, but the previous PC might have performed assembly, so it wouldn't be necessary in that case. I think this might be an argument in favour of having device offload as an optional step in AssembledPC?
| return mat_type | ||
|
|
||
|
|
||
| class OffloadPC(AssembledPC): |
There was a problem hiding this comment.
Should the name be more precise and refer to "GPU"?
| P_cu.setNearNullSpace(P.getNearNullSpace()) | ||
|
|
||
| # Update preconditioner with GPU matrix | ||
| self.pc.setOperators(A, P_cu) |
There was a problem hiding this comment.
P lives in the GPU but A still lives in the CPU. When pc_type = ksp, the more sensible thing would be to have both A and P in the GPU, don't you agree?
There was a problem hiding this comment.
Is this because A cannot live in the GPU if it is matfree? Do we benefit much from A being matfree if P is not matfree?
There was a problem hiding this comment.
I genuinely don't know. On the surface it makes sense, but are there cases in which A or P can't be offloaded? Do we need do an if not isinstance(A,ImplicitMatrix): before attempting to offload?
There was a problem hiding this comment.
We certainly want PETSc options to give the user finer control over what to offload.
If the original A and P point to the same Mat instance then it is reasonable to offload them both (as the same instance). This could be the default.
| nested_parameters = { | ||
| "pc_type": "ksp", | ||
| "ksp": { | ||
| "ksp_type": ksp_type, |
There was a problem hiding this comment.
ksp_max_it should be capped, to ensure that we are not taking an insane number of iterations.
| L = inner(grad(u), grad(v)) * dx | ||
| R = inner(v, f) * dx | ||
|
|
||
| # Dirichlet boundary on all sides to 0 |
There was a problem hiding this comment.
To catch potential issues with BCs, it'd be good to prescribe non-homogeneous BCs
|
Thanks for this, it looks really promising. I have some suggestions about the interface. It seems to me that the current implementation might be abusing from Once |
firedrake/preconditioners/offload.py
Outdated
| for device, mat_type in device_mat_type_map.items(): | ||
| if device in petsctools.get_external_packages(): | ||
| break |
There was a problem hiding this comment.
This feels like it should be a utility function. It's very opaque.
.github/workflows/core.yml
Outdated
| run: | | ||
| curl -fsSL $( python3 ./firedrake-repo/scripts/firedrake-configure --gpu-arch cuda --show-extra-repo-key ) | apt-key add - | ||
| python3 ./firedrake-repo/scripts/firedrake-configure --gpu-arch cuda --show-extra-repo-file-contents > \ | ||
| /etc/apt/sources.list.d/$( python3 ./firedrake-repo/scripts/firedrake-configure --gpu-arch cuda --show-extra-repo-file-name ) |
There was a problem hiding this comment.
It's kinda horrid that we have to invoke firedrake-configure 3 times. Is there a nicer way to do this?
There was a problem hiding this comment.
Agreed, its disgusting. I was following the procedure nvidia uses to build their Docker containers. I'll see if I can adapt the procedure they document for end-users, and hope its similar enough to AMD's procedure for HIP.
scripts/firedrake-configure
Outdated
| CUDA: ( | ||
| "cuda.list", | ||
| f"deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/{CUDA_ARCH_MAP.get(platform.machine(), platform.machine())} /", | ||
| f"https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/{CUDA_ARCH_MAP.get(platform.machine(), platform.machine())}/3bf863cc.pub", |
There was a problem hiding this comment.
This includes the magic string '3bf863cc'. Can there be a comment saying how we can update this?
There was a problem hiding this comment.
I am very uncomfortable with this. Unless I have misunderstood what is going on this is potentially introducing a security risk for both us and our users.
Should we really be telling our users (many of whom will blindly follow installation guide) to add random authentication keys to their package managers? And even if we are happy to do that, should we be doing it through a deprecated utility that will be removed after Ubuntu24.04 (according to the apt-key man page)?
Even if we can make sure that this specific repo at this specific time is safe, are we confident we can reliably keep the links up to date, and if we allow one then inevitably it's only a matter of time before someone adds another. Can we keep up to date with that too?
AFAIK we currently only make users install packages that are already available through their package manager (so the security responsibility is predominantly with them), or through PETSc, which is a much bigger library with more developer and maintainer resources.
I think we need to discuss this at the meeting and work out a) what size of risk it really is, and b) if it is a risk, the best way around this if offloading really does need libraries that can't be installed via package manager or PETSc.
There was a problem hiding this comment.
As above, will rework this part.
There was a problem hiding this comment.
I think we shouldn't be silently adding software sources behind users' backs. This seems bad. Potentially we should just fail and tell users to go do it themselves.
There was a problem hiding this comment.
firedrake-configure doesn't really 'do' anything here. Like the rest of the firedrake-configure steps It just prints a string to give to another command. A user still has to run curl and then dpkg to install it. Once this is in, I figured it would be added as an optional step to the installation instructions. If a user skips that step and then attempts to install system packages with --gpu-arch cuda, that would fail as apt would not be able to find any packages from the Nvidia repository.
| (LINUX_APT_AARCH64, ARCH_COMPLEX): LINUX_APT_PACKAGES, | ||
| (MACOS_HOMEBREW_ARM64, ARCH_DEFAULT): MACOS_HOMEBREW_PACKAGES, | ||
| (MACOS_HOMEBREW_ARM64, ARCH_COMPLEX): MACOS_HOMEBREW_PACKAGES, | ||
| (LINUX_APT_X86_64, ARCH_DEFAULT, NO_GPU): LINUX_APT_PACKAGES, |
There was a problem hiding this comment.
I think I'd prefer it if packages were purely additive. So add libsuperlu-dev etc here instead of filtering them out above.
scripts/firedrake-configure
Outdated
| PetscSpecsDeltaDictType = dict[GPUArch, PetscSpecsDictType] | ||
|
|
||
| # Suitesparse and SuperLU-DIST must be built from source to enable GPU features | ||
| PETSC_EXTERNAL_PACKAGE_SPECS_DELTA_GPU: PetscSpecsDeltaDictType = { |
There was a problem hiding this comment.
Same comment about an additive approach. I want firedrake-configure to be as dumb as possible.
.github/workflows/core.yml
Outdated
| # Use a different mirror to fetch apt packages from to get around | ||
| # temporary outage. | ||
| # (https://askubuntu.com/questions/1549622/problem-with-archive-ubuntu-com-most-of-the-servers-are-not-responding) | ||
| # The mirror was chosen from https://launchpad.net/ubuntu/+archivemirrors. | ||
| - name: Configure apt | ||
| run: | | ||
| sed -i 's|http://archive.ubuntu.com/ubuntu|http://www.mirrorservice.org/sites/archive.ubuntu.com/ubuntu/|g' /etc/apt/sources.list.d/ubuntu.sources | ||
| apt-get update |
|
I should have marked this as a draft from the start, apologies for missing that. @pbrubeck thanks for the detailed feedback. I like your suggested approach of having Where |
Addresses review comments on firedrake-configure, core.yml and test_poisson_offloading_pc.py. Updates to firedrake-check and skipnogpu marker still to come.
Move device matrix type selection to utils.py. Introduce skipnogpu test marker.
|
The issue in the CI seems to indicate that the host that the GPU tests attempted to run on did not have Nvidia drivers installed. Is it possible there are runners with the |
I've passed this on so it should get investigated and fixed shortly. |
Have you tried setting up PETSc inside the Nvidia-provided Cuda Docker image locally and running the tests, instead of on your local PC installation, just to check? I attempted this before (at the time it was PETSc installed inside a clean cuda:12.9 Ubuntu Docker image from Nvidia, though I can check my Dockerfile to confirm if needed), and I couldn’t get PETSc to work properly in that setup. When passing the --download-mpi flag to PETSc and then checking ompi_info, I saw the following MPI extensions: affinity, cuda, ftmpi, rocm, shortfloat. It seemed odd that both rocm and cuda appeared, especially since this was inside the CUDA container with no other prior installations. Could you check whether you’re seeing the same extensions on your side? Note: this was on a older PETSc version |
|
Thanks for the info @Olender. My testing setup is a little more complicated by necessity as I don't have access to a system that I can both run Docker on and that has Nvidia hardware. I'm testing the build set-up from the base Ubuntu image in Docker as in the longer term we're trying to augment the current installation instructions to include GPU builds. For that reason, I'm cross-compiling, so I download the full CUDA software stack into the container (stuck at 12.9 until this PETSc issue is resolved) and build PETSc, though I can't run |
013d6f0 to
4e8ca5b
Compare
connorjward
left a comment
There was a problem hiding this comment.
I think this is getting better. The CI error now appears to be a more mundane missing apt-get update.
…runner label to pass linting
47c4cdc to
f2af668
Compare
f2cee66 to
3f3a96a
Compare
33bf483 to
04d12d9
Compare
Yes, its looking good now. After a few iterations we have a working build. Now I can work on integrating a GPU test into |
69c58e7 to
decca0a
Compare
decca0a to
9ea7a8a
Compare
connorjward
left a comment
There was a problem hiding this comment.
Looking pretty good. Please let us know when you think this is ready for a detailed review.
| mat_type = device_matrix_type() | ||
| if mat_type is None: | ||
| if pc_comm_rank == 0: | ||
| warnings.warn( |
There was a problem hiding this comment.
For Python warnings there is a difference between warnings.warn and logger.warning. The former indicates a genuine problem and the latter indicates a likely problem. I wonder if the first warning here should be a warnings.warn and the second a logger.warning.
There was a problem hiding this comment.
Thanks, I wasn't aware of the distinction. Ironic given that if you set logging.captureWarnings = True, the function called by warnings.warn to show the message is monkey-patched to a function that calls logging.warning. Will update accordingly
| def test_poisson_offload(ksp_type, pc_type): | ||
|
|
||
| # Different tests for poisson: cg and pctype sor, --ksp_type=cg --pc_type=gamg | ||
| print(f"Using ksp_type = {ksp_type}, and pc_type = {pc_type}.", flush=True) |
There was a problem hiding this comment.
Shouldn't leave print statements in tests (but fine for debugging)
| def test_poisson_offload(ksp_type, pc_type): | ||
|
|
||
| # Different tests for poisson: cg and pctype sor, --ksp_type=cg --pc_type=gamg | ||
| print(f"Using ksp_type = {ksp_type}, and pc_type = {pc_type}.", flush=True) |
There was a problem hiding this comment.
Shouldn't leave print statements in tests (but fine for debugging)
|
|
||
| - name: Add Nvidia CUDA deb repositories | ||
| run: | | ||
| deburl=$( python3 ./firedrake-repo/scripts/firedrake-configure --show-extra-repo-pkg-url --gpu-arch cuda ) |
There was a problem hiding this comment.
This process definitely seems better.
| run: | | ||
| . venv/bin/activate | ||
| export PETSC_OPTIONS="${PETSC_OPTIONS} -log_view_gpu_time -log_view" | ||
| python3 ./firedrake-repo/tests/firedrake/offload/test_poisson_offloading_pc.py |
There was a problem hiding this comment.
This is a final sanity check on my end, I wanted to confirm that the GPU was being used as expected. The final column of the profiling output (https://github.com/firedrakeproject/firedrake/actions/runs/23132801814/job/67189609416) shows the fraction of work done on the GPU, I wanted to make sure that the low-level vector and matrix operations were actually happening on the GPU, which they are. Besides, this change is in a 'DROP BEFORE MERGE' commit, it will be going away.
| @@ -0,0 +1,3 @@ | |||
| self-hosted-runner: | |||
| labels: | |||
| - gpu No newline at end of file | |||
There was a problem hiding this comment.
That's to address this linting error: https://github.com/firedrakeproject/firedrake/actions/runs/22885137596/job/66433010084. I will add a comment to the file to that effect.
Thanks @connorjward. Before I dive into the implementation, I'd like some feedback on @pbrubeck's suggestion of performing the offload step as part of |
The I'm starting to think that this Regarding the automatic selection of the |
|
@pbrubeck thanks for the feedback. I've been experimenting with implementations over the last couple of days and I think I'm close to the same conclusion. If offloading is integrated into The reason I suggested a As for multiple different offload types being supported by the same device, both HIP and CUDA are vendor-specific languages, AMD devices only support HIP, Nvidia devices only support CUDA. That being said, I believe it is possible to build PETSc with support for multiple devices. Perhaps the |
|
An update on where I'm at with this. I have a reworked version subclassing |
|
I think it's fine if you want to break this apart. |
Description
First pass at
firedrake-configurefor GPU-enabled PETSc builds, as well as a corresponding github actions workflow. An optional--gpu-archflag has been added which adds all the necessary configuration to go from a fresh Ubuntu installation to a working Firedrake build (mostly) following existing build instructions.OffloadPCitself is more or less unchanged from @Olender's work for now, but with a few extra checks for whether GPU offload is available. I expect it will take a few iterations to get this going under CI, so once that's up and running reliably we can look at further development to theOffloadPCfunctionality and testing.I've tried to put this together in a way that can be expanded upon fairly easily. We're interested in ROCm/HIP as well as Cuda, so there are a couple of dictionaries that just have a
cudakey for now that will be expanded upon in time. I also thought thatOffloadPCshould be a no-op with a warning if there are any issues around GPUs, rather than crashing out. I think this is the right choice for workflow portability and heterogeneous systems, but it would be just as easy to raise exceptions there.