Files
Jungfraujoch/image_analysis/azint/AzIntEngineGPU.h
T
leonarski_fandClaude Opus 5.5 f290e40d20 GPU azimuthal profile: sum the corrected values in fixed point
The fused adaptive spot finder's azimuthal profile (and the standalone AzIntEngineGPU) summed the
corrected pixel values - pixel times a float correction - with float atomicAdd, in shared memory
and then across blocks. Float addition is not associative, so the per-ring sums depended on the
order the threads and blocks happened to arrive in, and on the grid size chosen for the GPU. The
profile, and the per-image background estimate read from it, moved in their last bits between two
runs of the same command: on a thaumatin JUNGFRAU 4M sweep one bkg value in _plot.txt differed in
the 4th digit between runs. The raw ring sums that drive detection were already exact integers;
only the reported profile was affected.

Each corrected value (and its square) is now quantised to 2^-12 on its own and summed as a signed
64-bit integer, the convention the raw ring sums and the Bragg-integration profile already use.
Integer addition is associative, so the sums are the same whatever the order or grid.

The repeat test of the fused finder now also requires the profile and its std to repeat bit for
bit (it failed on the old kernel at the first repeat); the AzIntEngineGPU test re-runs 20 times.
Fused finder benchmark: 0.313 -> 0.324 ms/frame.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SVmAWnzCmRKAXVUCdc4iNi
2026-10-08 12:33:34 +02:00

35 lines
1.4 KiB
C++

// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include "AzIntEngine.h"
#include "../indexing/CUDAMemHelpers.h"
#include "../indexing/CudaSharedTables.h"
class AzIntEngineGPU : public AzIntEngine {
std::shared_ptr<CudaStream> stream;
int threads;
int blocks;
size_t shared_needed;
size_t shared_size;
// Geometry-only tables: one copy per GPU, shared with every other engine on it (see
// CudaSharedTables.h) instead of one copy per worker thread.
std::shared_ptr<CudaDevicePtr<float>> gpu_azint_correction;
std::shared_ptr<CudaDevicePtr<uint16_t>> gpu_pixel_to_bin;
// Fixed point (AZINT_FIXED in the .cu), so the sums do not depend on the order the atomics arrive in.
CudaDevicePtr<unsigned long long> gpu_sum;
CudaDevicePtr<unsigned long long> gpu_sum2;
CudaDevicePtr<uint32_t> gpu_count;
std::vector<unsigned long long> fixed_sum;
std::vector<unsigned long long> fixed_sum2;
CudaRegisteredVector<unsigned long long> cpu_sum_reg;
CudaRegisteredVector<unsigned long long> cpu_sum2_reg;
CudaRegisteredVector<uint32_t> cpu_count_reg;
public:
AzIntEngineGPU(const AzimuthalIntegrationMapping& integration, std::shared_ptr<CudaStream> stream);
void Run(const ImagePreprocessorBuffer &image, AzimuthalIntegrationProfile &profile) override;
};