Skip to content

Commit 26b9b60

Browse files
committed
GPU: only use GPUdoubleCalc where fast math is off
Metal defaults to fast math, and fast math reassociates GPUdoubleCalcImpl's compensation terms away: measured on the full propagateTo kernel, the two-float type then costs 1.57x for exactly the accuracy of a plain float. The Metal build therefore follows what the OpenCL one already does: fast math unless GPUCA_DETERMINISTIC_MODE asks for otherwise, and GPUdoubleCalc becomes a plain float when fast math is on. This also takes back the unconditional -fno-fast-math added with the type, which is not free as claimed there: on an ALU-bound kernel it costs the plain float path a factor of two (0.055 -> 0.108 ns/propagate on an M1 Max), and that would have been paid by every Metal kernel, including the clusterer and the sector tracker, which have no double-precision intermediates at all. -fmetal-math-mode=safe would keep the compensation intact for 20% less, but it makes division and square root approximate and the GPU result then stops matching the CPU bit-for-bit (79% of covariance elements instead of 100%), which is the opposite of what deterministic mode is for. -fno-fast-math is also the exact analogue of the CUDA flags used there, --prec-div --prec-sqrt --fmad false.
1 parent 88e8ebc commit 26b9b60

3 files changed

Lines changed: 9 additions & 1 deletion

File tree

‎GPU/Common/GPUCommonDouble.h‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,11 @@ GPUdi() GPUdoubleCalcImpl operator/(float a, GPUdoubleCalcImpl b) { return GPUdo
180180

181181
// GPUCA_FORCE_DOUBLECALC lets a host test exercise the Metal representation and
182182
// compare it against the double one.
183-
#if defined(__METAL__) || defined(GPUCA_FORCE_DOUBLECALC)
183+
#if defined(__METAL__) && defined(__FAST_MATH__)
184+
// Fast math reassociates the compensation terms away: the two-float type would
185+
// then cost 1.5x for the accuracy of a plain float.
186+
typedef float GPUdoubleCalc;
187+
#elif defined(__METAL__) || defined(GPUCA_FORCE_DOUBLECALC)
184188
typedef GPUdoubleCalcImpl GPUdoubleCalc;
185189
#elif defined(GPUCA_FORCE_FLOATCALC) // for the host test only, to show what plain float would cost
186190
typedef float GPUdoubleCalc;

‎GPU/GPUTracking/Base/metal/CMakeLists.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ set(METAL_BIN ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionMetalCode)
2727
# reject an unannotated pointer or `this` outright, which GPUCommonDefAPI.h
2828
# relies on for GPUgeneric() and GPUdDefault().
2929
set(METAL_FLAGS -std=metal4.1 ${GPUCA_METAL_DENORMALS_FLAGS})
30+
if(GPUCA_DETERMINISTIC_MODE GREATER_EQUAL ${GPUCA_DETERMINISTIC_MODE_MAP_NO_FAST_MATH})
31+
set(METAL_FLAGS ${METAL_FLAGS} ${GPUCA_METAL_NO_FAST_MATH_FLAGS})
32+
endif()
3033
set(METAL_DEFINES "-D$<JOIN:$<TARGET_PROPERTY:O2::GPUTracking,COMPILE_DEFINITIONS>,$<SEMICOLON>-D>"
3134
"-I$<JOIN:$<FILTER:$<TARGET_PROPERTY:O2::GPUTracking,INCLUDE_DIRECTORIES>,EXCLUDE,^/usr/include/?>,$<SEMICOLON>-I>"
3235
-I${CMAKE_SOURCE_DIR}/Detectors/TRD/base/src

‎dependencies/FindO2GPU.cmake‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ endif()
177177
set(GPUCA_CXX_NO_FAST_MATH_FLAGS "-fno-fast-math -ffp-contract=off")
178178
set(GPUCA_CUDA_NO_FAST_MATH_FLAGS "--prec-div=true --prec-sqrt=true --fmad false -Xcompiler -fno-fast-math -Xcompiler -ffp-contract=off")
179179
set(GPUCA_OCL_NO_FAST_MATH_FLAGS -cl-fp32-correctly-rounded-divide-sqrt )
180+
set(GPUCA_METAL_NO_FAST_MATH_FLAGS "-fno-fast-math")
180181
if(GPUCA_DETERMINISTIC_MODE GREATER_EQUAL ${GPUCA_DETERMINISTIC_MODE_MAP_WHOLEO2})
181182
add_definitions(-DGPUCA_DETERMINISTIC_MODE)
182183
string(APPEND CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER} " ${GPUCA_CXX_NO_FAST_MATH_FLAGS}")

0 commit comments

Comments
 (0)