Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-26 09:34:33

0001 //------------------------------ -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file corecel/data/Ldg.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <type_traits>
0010 
0011 #include "corecel/Macros.hh"
0012 #if CELER_DEVICE_COMPILE
0013 // Make sure __ldg is available for HIP
0014 #    include "corecel/DeviceRuntimeApi.hh"
0015 #endif
0016 
0017 namespace celeritas
0018 {
0019 //---------------------------------------------------------------------------//
0020 /*!
0021  * \page ldg Cached device loading
0022  *
0023  * On GPUs, reading from global memory through the L1/texture cache can improve
0024  * throughput when many threads access the same address.
0025  * The \c __ldg intrinsic (CUDA/HIP) performs such a cached load, using
0026  * L1/texture memory rather than the ordinary data cache.
0027  * The hardware contract is that the pointed-to memory must be \em read-only
0028  * for the lifetime of the kernel; this is generally true for \em Params data
0029  * (physics tables, geometry) but not for \em State data.
0030  * On the host the \c ldg family of functions falls back to a plain
0031  * dereference, so no special-casing is needed in caller code.
0032  * Because \c ldg is only for \em read-only addresses in \em global memory, all
0033  * arguments \em must match only \c const_pointer types.
0034  *
0035  * \par Scalar values
0036  *
0037  * Pass a const pointer to any supported type to the one-argument \c ldg:
0038  * \code
0039  *   real_type energy = ldg(&record.energy);
0040  *   MaterialId mat   = ldg(&record.material);  // OpaqueId supported
0041  * \endcode
0042  *
0043  * Built-in implementations cover:
0044  *
0045  * - arithmetic types (identity, the default),
0046  * - enum types (reinterpret-cast to the underlying integer),
0047  * - \c OpaqueId (pointer to the underlying index \c T),
0048  * - \c Quantity (pointer to the underlying value \c T),
0049  * - \c Array (successive loads to each element).
0050  *
0051  * \par Spans and collections
0052  *
0053  * \c LdgSpan<T const> (from \c corecel/cont/LdgSpan.hh) is an alias for
0054  * \c Span whose iterator triggers \c __ldg on every element access.
0055  * Use it as you would any ordinary span:
0056  * \code
0057  *   LdgSpan<real_type const> energies = params.get_energies();
0058  *   for (real_type e : energies)   // each read uses __ldg
0059  *       process(e);
0060  * \endcode
0061  *
0062  * \c Collection<T, Ownership::const_reference, MemSpace::device> returns
0063  * \c LdgSpan automatically when the element type supports \c ldg, so View
0064  * classes built on device \c const_reference collections benefit without any
0065  * extra work.
0066  *
0067  * \par Extending ldg to a new type
0068  *
0069  * The \c ldg function is found by argument-dependent lookup (ADL).
0070  * Implement a \c friend function \c ldg that takes a const pointer and returns
0071  * a value:
0072  * \code
0073  *   template<class T>
0074  *   class MyClass
0075  *   {
0076  *     public:
0077  *       CELER_CONSTEXPR_FUNCTION friend MyClass ldg(MyClass const* m)
0078  *       {
0079  *         return MyClass{ldg(&m->value_)};
0080  *       }
0081  *
0082  *     private:
0083  *       T value_;
0084  *   };
0085  * \endcode
0086  * See \c OpaqueId, \c Quantity and \c Array for examples.
0087  *
0088  * \internal
0089  *
0090  * \par Implementation details
0091  *
0092  * \c detail::LdgWrapper<T const> is a thin proxy (similar to
0093  * \c std::reference_wrapper) that stores a \c const pointer and implicitly
0094  * converts to the value type by calling \c ldg. The result is always a
0095  * \em value, not a reference, and the load goes through \c __ldg on device.
0096  *
0097  * \c detail::LdgIterator<T const> is a random-access iterator whose
0098  * \c operator* returns an \c LdgWrapper.
0099  * Wrapping it in \c Span yields \c LdgSpan: range-for loops and standard
0100  * algorithms transparently trigger \c __ldg on every element access without
0101  * requiring any change at the call site.
0102  */
0103 
0104 //---------------------------------------------------------------------------//
0105 /*!
0106  * Wrap the low-level CUDA/HIP "load read-only global memory" function.
0107  *
0108  * On CUDA the load is cached in L1/texture memory, improving performance when
0109  * data is repeatedly read by many threads in a kernel.
0110  *
0111  * \warning The target must be \c global memory (or else the kernel may crash)
0112  * and \c read-only (or else the result may be wrong) for the lifetime of the
0113  * kernel.
0114  * This is generally true for Params data but not State data.
0115  */
0116 template<class T>
0117 CELER_CONSTEXPR_FUNCTION std::enable_if_t<std::is_arithmetic_v<T>, T> ldg(
0118     T const* ptr) noexcept
0119 {
0120 #if CELER_DEVICE_COMPILE
0121     return __ldg(ptr);
0122 #else
0123     return *ptr;
0124 #endif
0125 }
0126 
0127 //---------------------------------------------------------------------------//
0128 //! \cond (CELERITAS_DOC_DEV)
0129 /*!
0130  * Get a pointer to the underlying integer for an enum type.
0131  */
0132 template<class T>
0133 CELER_CONSTEXPR_FUNCTION std::enable_if_t<std::is_enum_v<T>, T> ldg(
0134     T const* ptr) noexcept
0135 {
0136     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
0137     auto const* data = reinterpret_cast<std::underlying_type_t<T> const*>(ptr);
0138     return static_cast<T>(ldg(data));
0139 }
0140 
0141 //---------------------------------------------------------------------------//
0142 /*!
0143  * Load a struct member via \c ldg using a pointer-to-member.
0144  *
0145  * Convenience overload for when the member is known at the call site.
0146  * \code
0147  * BvhNodeId parent = ldg(node, &BvhLeafNode::parent);
0148  * \endcode
0149  */
0150 template<class Class, class T>
0151 CELER_CONSTEXPR_FUNCTION T ldg(Class const& obj, T Class::* mp) noexcept
0152 {
0153     using ::celeritas::ldg;
0154     return ldg(&(obj.*mp));
0155 }
0156 
0157 //! \endcond
0158 //---------------------------------------------------------------------------//
0159 /*!
0160  * Storable projector that loads a struct member via \c ldg .
0161  *
0162  * Stores a pointer-to-member and, when called with an object, returns the
0163  * member value loaded via \c __ldg . Use this when the load must be captured
0164  * as a callable; for immediate use prefer the two-argument \c ldg overload.
0165  *
0166  * \code
0167  * auto load_parent = LdgMember{&BvhLeafNode::parent};
0168  * BvhNodeId parent = load_parent(node);
0169  * \endcode
0170  */
0171 template<class Class, class T>
0172 struct LdgMember
0173 {
0174     //// TYPES ////
0175 
0176     using MemberPtr = T Class::*;
0177 
0178     //// DATA ////
0179 
0180     MemberPtr mp;
0181 
0182     //// FUNCTIONS ////
0183 
0184     CELER_CONSTEXPR_FUNCTION T operator()(Class const& obj) const
0185     {
0186         using ::celeritas::ldg;
0187         return ldg(&(obj.*mp));
0188     }
0189 };
0190 
0191 //! Deduction guide: \c LdgMember{&Foo::bar} deduces \c LdgMember<Foo,Bar>
0192 template<class Class, class T>
0193 LdgMember(T Class::*) -> LdgMember<Class, T>;
0194 
0195 //---------------------------------------------------------------------------//
0196 }  // namespace celeritas