From 13aa20a5287a14b7f5b4cf4de7d7bdeea63eec8b Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 3 Aug 2026 01:02:07 +0200 Subject: [PATCH] bragg_integration: grow the GPU reflection arrays with slack EnsureCapacity resized its 13 device arrays to exactly the current image's predicted-reflection count, so every image that set a new record freed and reallocated all of them. cudaMalloc and cudaFree take a device-wide lock in the CUDA driver, so those images stalled every other worker: sampling the worker threads during the per-image loop found 21-24 of 32 parked in cuMemAlloc_v2 or cuMemFree_v2, all called from this one function, and the running maximum makes 32 workers do far more allocator work than one does. Grow by half again instead. All transfers and kernel launches are sized by the per-image reflection count rather than by the capacity, and the member is already documented as holding at least that many, so over-allocating changes no result. On an 18 Mpx rotation set the integration stage drops from 1.37 to 1.25 ms per image at 32 workers; merged statistics, error model and adopted space group are unchanged. Co-Authored-By: Claude Opus 5 (1M context) --- .../BraggIntegrationEngineGPU.cu | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/image_analysis/bragg_integration/BraggIntegrationEngineGPU.cu b/image_analysis/bragg_integration/BraggIntegrationEngineGPU.cu index b0a3d6a5..2de35c7d 100644 --- a/image_analysis/bragg_integration/BraggIntegrationEngineGPU.cu +++ b/image_analysis/bragg_integration/BraggIntegrationEngineGPU.cu @@ -434,25 +434,29 @@ BraggIntegrationEngineGPU::BraggIntegrationEngineGPU(const DiffractionExperiment void BraggIntegrationEngineGPU::EnsureCapacity(size_t n) { if (n <= capacity) return; - d_px_x = CudaDevicePtr(n); - d_px_y = CudaDevicePtr(n); - d_d = CudaDevicePtr(n); - d_cx = CudaDevicePtr(n); - d_cy = CudaDevicePtr(n); - d_I = CudaDevicePtr(n); - d_sigma = CudaDevicePtr(n); - d_bkg = CudaDevicePtr(n); - d_obs_x = CudaDevicePtr(n); - d_obs_y = CudaDevicePtr(n); - d_ok = CudaDevicePtr(n); - d_strong = CudaDevicePtr(n); - d_has_obs = CudaDevicePtr(n); + // Grow with slack. cudaMalloc/cudaFree take a device-wide lock in the driver, so growing to + // exactly n makes every image that sets a new reflection-count record stall all other workers. + const size_t new_capacity = std::max(n, capacity + capacity / 2); - h_px_x.resize(n); h_px_y.resize(n); h_d.resize(n); - h_I.resize(n); h_sigma.resize(n); h_bkg.resize(n); - h_obs_x.resize(n); h_obs_y.resize(n); - h_ok.resize(n); h_has_obs.resize(n); - capacity = n; + d_px_x = CudaDevicePtr(new_capacity); + d_px_y = CudaDevicePtr(new_capacity); + d_d = CudaDevicePtr(new_capacity); + d_cx = CudaDevicePtr(new_capacity); + d_cy = CudaDevicePtr(new_capacity); + d_I = CudaDevicePtr(new_capacity); + d_sigma = CudaDevicePtr(new_capacity); + d_bkg = CudaDevicePtr(new_capacity); + d_obs_x = CudaDevicePtr(new_capacity); + d_obs_y = CudaDevicePtr(new_capacity); + d_ok = CudaDevicePtr(new_capacity); + d_strong = CudaDevicePtr(new_capacity); + d_has_obs = CudaDevicePtr(new_capacity); + + h_px_x.resize(new_capacity); h_px_y.resize(new_capacity); h_d.resize(new_capacity); + h_I.resize(new_capacity); h_sigma.resize(new_capacity); h_bkg.resize(new_capacity); + h_obs_x.resize(new_capacity); h_obs_y.resize(new_capacity); + h_ok.resize(new_capacity); h_has_obs.resize(new_capacity); + capacity = new_capacity; } std::vector BraggIntegrationEngineGPU::Run(const ImagePreprocessorBuffer &image,