/* * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ************************************************************************ * Modified from pytorch_scatter * Copyright (c) 2020 Matthias Fey * See https://github.com/rusty1s/pytorch_scatter/blob/master/LICENSE for details * ************************************************************************ */ #ifndef TRT_SCATTER_ELEMENTS_ATOMICS_H #define TRT_SCATTER_ELEMENTS_ATOMICS_H #include #include #include #include #define ATOMIC(NAME) \ template \ struct Atomic##NAME##IntegerImpl; \ \ template \ struct Atomic##NAME##IntegerImpl \ { \ inline __device__ void operator()(TScalar* address, TScalar val) \ { \ std::uint32_t* addressAsUI = reinterpret_cast(address); \ std::uint32_t old = *addressAsUI; \ std::uint32_t assumed; \ \ do \ { \ assumed = old; \ old = atomicCAS(addressAsUI, assumed, OP(val, static_cast(old))); \ } while (assumed != old); \ } \ }; \ \ template \ struct Atomic##NAME##IntegerImpl \ { \ inline __device__ void operator()(TScalar* address, TScalar val) \ { \ unsigned long long* addressAsULL = reinterpret_cast(address); \ unsigned long long old = *addressAsULL; \ unsigned long long assumed; \ \ do \ { \ assumed = old; \ old = atomicCAS(addressAsULL, assumed, OP(val, static_cast(old))); \ } while (assumed != old); \ } \ }; \ \ template \ struct Atomic##NAME##DecimalImpl; \ \ template \ struct Atomic##NAME##DecimalImpl \ { \ inline __device__ void operator()(TScalar* address, TScalar val) \ { \ std::int32_t* addressAsI = reinterpret_cast(address); \ std::int32_t old = *addressAsI; \ std::int32_t assumed; \ \ do \ { \ assumed = old; \ old = atomicCAS(addressAsI, assumed, __float_as_int(OP(val, __int_as_float(assumed)))); \ } while (assumed != old); \ } \ }; \ template \ struct Atomic##NAME##DecimalImpl \ { \ inline __device__ void operator()(TScalar* address, TScalar val) \ { \ uint32_t* addressAsUI = reinterpret_cast((char*) address - ((std::size_t) address & 2)); \ std::uint32_t old = *addressAsUI; \ std::uint32_t assumed; \ \ do \ { \ assumed = old; \ std::uint16_t hsum_old; \ hsum_old = reinterpret_cast(address) & 2 ? (old >> 16) : (old & 0xffff); \ auto hsum = OP(*reinterpret_cast(&hsum_old), val); \ old = (size_t) address & 2 ? (old & 0xffff) | ((*reinterpret_cast(&hsum)) << 16) \ : (old & 0xffff0000) | *reinterpret_cast(&hsum); \ old = atomicCAS(addressAsUI, assumed, old); \ } while (assumed != old); \ } \ }; #define OP(X, Y) ((Y) + (X)) ATOMIC(Add) #undef OP static inline __device__ void atomAdd(float* address, float val) { atomicAdd(address, val); } static inline __device__ void atomAdd(__half* address, __half val) { #if defined(USE_ROCM) || (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 700 || CUDA_VERSION < 10000)) AtomicAddDecimalImpl<__half, sizeof(__half)>()(address, val); #else atomicAdd(address, val); #endif } static inline __device__ void atomAdd(__nv_bfloat16* address, __nv_bfloat16 val) { #if (defined(__CUDACC__) && (!defined(__CUDA_ARCH__) || (__CUDA_ARCH__ >= 800))) || defined(_NVHPC_CUDA) atomicAdd(address, val); #else AtomicAddDecimalImpl<__nv_bfloat16, sizeof(__nv_bfloat16)>()(address, val); #endif } static inline __device__ void atomAdd(std::int32_t* address, std::int32_t val) { atomicAdd(address, val); } static inline __device__ void atomAdd(std::int64_t* address, std::int64_t val) { AtomicAddIntegerImpl()(address, val); } #define OP(X, Y) ((Y) * (X)) ATOMIC(Mul) #undef OP static inline __device__ void atomMul(std::int32_t* address, std::int32_t val) { AtomicMulIntegerImpl()(address, val); } static inline __device__ void atomMul(std::int64_t* address, std::int64_t val) { AtomicMulIntegerImpl()(address, val); } static inline __device__ void atomMul(float* address, float val) { AtomicMulDecimalImpl()(address, val); } static inline __device__ void atomMul(__half* address, __half val) { AtomicMulDecimalImpl<__half, sizeof(__half)>()(address, val); } static inline __device__ void atomMul(__nv_bfloat16* address, __nv_bfloat16 val) { AtomicMulDecimalImpl<__nv_bfloat16, sizeof(__nv_bfloat16)>()(address, val); } #define OP(X, Y) ((X) < (Y)) ? (Y) : (X) ATOMIC(Max) #undef OP static inline __device__ void atomMax(std::int32_t* address, std::int32_t val) { atomicMax(address, val); } static inline __device__ void atomMax(float* address, float val) { AtomicMaxDecimalImpl()(address, val); } static inline __device__ void atomMax(std::int64_t* address, std::int64_t val) { AtomicMaxIntegerImpl()(address, val); } static inline __device__ void atomMax(__half* address, __half val) { AtomicMaxDecimalImpl<__half, sizeof(__half)>()(address, val); } static inline __device__ void atomMax(__nv_bfloat16* address, __nv_bfloat16 val) { AtomicMaxDecimalImpl<__nv_bfloat16, sizeof(__nv_bfloat16)>()(address, val); } #define OP(X, Y) ((X) > (Y)) ? (Y) : (X) ATOMIC(Min) #undef OP static inline __device__ void atomMin(std::int32_t* address, std::int32_t val) { atomicMin(address, val); } static inline __device__ void atomMin(std::int64_t* address, std::int64_t val) { AtomicMinIntegerImpl()(address, val); } static inline __device__ void atomMin(float* address, float val) { AtomicMinDecimalImpl()(address, val); } static inline __device__ void atomMin(__half* address, __half val) { AtomicMinDecimalImpl<__half, sizeof(__half)>()(address, val); } static inline __device__ void atomMin(__nv_bfloat16* address, __nv_bfloat16 val) { AtomicMinDecimalImpl<__nv_bfloat16, sizeof(__nv_bfloat16)>()(address, val); } #endif