Villar-PSO light curve fitting on GPU in ZTF enrichment workers#448
Villar-PSO light curve fitting on GPU in ZTF enrichment workers#448frenbox wants to merge 18 commits into
Conversation
|
Throughput results (
|
|
Throughput results (
|
|
I curated the following dataset of longest classified source on fritz for the LC fitting test
The memory alone used for computation is quite small
however nvidia-smi reports ~312 MB for the process likely due to CUDA context over heads |
There was a problem hiding this comment.
My immediate thought, looking at this, was: why are we having one GPU algorithm behind a rust feature flag (villar fits) and not the other (ml with ort). Obviously the difference is that there is no villar fits happening on CPU (unlike the ml work), but still it feels cumbersome to have one env var to enable GPU for ML but one rust feature flag to enable GPU for lc fitting. My suggestion for this PR would be to undertake the following work: put both villar fits and GPU-enabled ML behind a feature flag(s). The "s" is in parenthesis because if we think as soon as we have GPUs all work should be done there then we should couple ml and villar fits, but if not we could have a feature flag for each. I'd suggest coupling them for now. BUT, if what you are looking for is:
- feature flag to specify if something is included in the compiled binary
- and separately, config to tell the code if it should run the GPU stuff
then I'm fine keeping both! But even then, I wonder if we should apply the same logic to the ML stuff? (feature flag to use ort with dynamic loading but then at runtime we can go back to CPU inference is the config says its disabled, and that way the CPU only version - when no feature flag is set - doesn't need to bother with the ort dynamic loading).
Another comment, related to this: I see that in your implementation "Workers are round-robin-assigned a device from config.gpu.device_ids at init time.". May I ask why this choice? Also, how does that play w/ the GPU session pool we use for ML? Instinctively I'd think a shared pool + context would make more sense for throughput (because here your enrichment worker might want to use GPU that was assigned to it at init rather than whichever is actually available now at runtime)? If you think there is no advantage to this and round robin makes more sense, then may I ask why we would not apply the same treatment to the ML stuff? I guess I just want to make sure we don't add complexity and different GPU-handling systems unless we have a specific reason for it.
Last but not least (related to the round-robin villar vs gpu ml pool stuff), let's say one GPU is running an inference task: can that GPU also run the villar fits in parallel? My understanding is that GPUs do have the ability to parallelize over different compute (which varies between cards of course, some more capable than others, and it seems server cards are best at this). If so, then maybe that answer the round-robin question. Also what happens when 2 workers try to use the same GPU context (which seems to be analogous to a device in this implementation) at the same time? Can CUDA decide to parallelize which means higher mem usage? If so, that could be an issue.
I think my understanding of parallel compute on GPUs is a little poor here, so these are all genuine questions I am asking myself as well!
|
Hi Theo, Thanks for the comments! I am also new to figuring out this GPU stuff so we are on similar boat. Here are my thoughts on your comments
I feel like it is forced by the dependencies:
So the Cargo feature exists because villar-pso requires it, not because we wanted asymmetry. The runtime semantics are unified such that both ORT and villar honor
The current existing pattern introduced by Michael's PR is: However, this has its implication to your third concern
This is how the current CUDA streams work:
From NVIDIA CUDA docs, it seems NULL stream has legacy "sync everything" semantics: any work on it implicitly waits for all other streams, and other streams wait for it. So ORT and villar serialize in practice, even though they're on different streams. So in practice:
To actually unlock parallelism on a single GPU, villar-pso would need to use a non-default stream per |
|
Throughput results (
|
|
Throughput results (
|
Additional Changes Summary
|
|
@Theodlz this is now ready for you to test Theo. |
|
Throughput results (
|
|
Throughput results (
|
|
Throughput results (
|
|
It seems after a combined cuda stream, in local testing my GPU is hitting OOM. |
|
Throughput results (
|
|
Throughput results (
|
|
Throughput results (
|
|
Final round of changes: Configurable batch size (config.yaml, src/conf.rs): workers.ztf.enrichment.batch_size is now a single config variable (default 750) controlling both the RPOP cap and the fixed ONNX inference shape. Zero-padding keeps the GPU memory arena stable across partial batches. I tested multiple benchmark for this and the GPU memory usage remained stable. @Theodlz Finally this is done! |
|
Throughput results (
|
1 similar comment
|
Throughput results (
|
|
Throughput results (
|
|
Throughput results (
Baseline run: |
Summary
Adds Villar light curve fitting on GPU (via frenbox/villar-pso) to the ZTF enrichment pipeline, on top of
gpu-setup-fix. Each enrichment worker runsbatch_pso_multi_seedon its batch's fittable alerts after ONNX classification and writesvillar_fit.*fields to the ZTF alerts collection.New
gpufeature gatesdep:villar-pso(its CUDA build needs nvcc at build time, unlike ORT which isload-dynamic). ORT GPU is unchanged.Two-gate runtime: villar runs only when both the build and the config allow it.
Build flag (
--features gpu): controls whether villar code is included in the binary.Config (
config.gpu.enabled): controls whether that code runs when the binary starts.Villar is GPU-only — there's no CPU fallback. When the config disables it, workers skip the villar step entirely while the rest of enrichment runs normally.
GpuContext, round-robin overconfig.gpu.device_ids(mirrors upstreamSharedModelPool).villar_fit.*docs so downstream consumers always see the expected keys.Files
Cargo.toml/Cargo.lockgpufeature + optional villar-pso dep (Linux)Dockerfile.gpu--features gputo the finalcargo buildsrc/enrichment/ztf.rsprocess_alertsAdditional Change 1
Share a single CUDA stream between ONNX Runtime sessions (ACAI/btsbot) and
villar-pso PSO for each enrichment device, replacing the previous mix of
the default (NULL) stream + 6 ORT-internal streams. Eliminates the
default-stream's implicit cross-stream fences and makes H2D/D2H transfers
actually async.
SharedModels now owns the per-device Stream and the villar-pso
GpuContext. Field declaration order ensures the stream is destroyed
after every session and context that uses it. SharedModelPool already
fans this out per device.
Drops the parallel round-robin (GPU_DEVICE_COUNTER) in
ZtfEnrichmentWorker, which was assigning Villar to a different device
than ACAI/btsbot for the same worker. Both now come from the same
Arc, so device alignment is guaranteed.
Removes the dead boom/src/gpu/ module (GpuPool was never wired up).
Additional Change 2