From 7c7c5afb2ec81796ebbbf99c0b6d2c629d7a69d7 Mon Sep 17 00:00:00 2001 From: Attila Krasznahorkay Date: Wed, 25 Sep 2024 16:09:56 +0200 Subject: [PATCH 1/5] Introduced a concatenation constructor for vecmem::tuple. Did not try to make a fully functional vecmem::tuple_cat function, as that was not technically needed so far. --- core/include/vecmem/utils/tuple.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/include/vecmem/utils/tuple.hpp b/core/include/vecmem/utils/tuple.hpp index 1f176b1f..6f41d927 100644 --- a/core/include/vecmem/utils/tuple.hpp +++ b/core/include/vecmem/utils/tuple.hpp @@ -74,6 +74,22 @@ struct tuple { VECMEM_HOST_AND_DEVICE constexpr tuple(U &&head, Us &&... tail) : m_head(std::forward(head)), m_tail(std::forward(tail)...) {} + /// "Concatenation" constructor + /// + /// It is used in the @c vecmem::edm code while constructing some of the + /// internal tuples of the objects. + /// + /// @param head The first element to be stored in the tuple + /// @param tail The rest of the elements to be stored in the tuple + /// + template , + std::is_constructible...>::value, + bool> = true> + VECMEM_HOST_AND_DEVICE constexpr tuple(U &&head, tuple &&tail) + : m_head(std::forward(head)), m_tail(std::move(tail)) {} + /// The first/head element of the tuple T m_head; /// The rest of the tuple elements From 233b5322f6de50dff6802d2aa696695ec00ecd15 Mon Sep 17 00:00:00 2001 From: Attila Krasznahorkay Date: Wed, 25 Sep 2024 16:15:54 +0200 Subject: [PATCH 2/5] Introduced vecmem::edm::proxy for host containers. Proxies can be used to provide an "AoS view" over an SoA container. A view that would make use of the same interface that the container itself also uses. Code for device container support was also added, but is probably not functional at the moment. --- core/include/vecmem/edm/container.hpp | 7 +- .../vecmem/edm/details/proxy_traits.hpp | 551 ++++++++++++++++++ core/include/vecmem/edm/host.hpp | 64 +- core/include/vecmem/edm/impl/host.ipp | 121 ++-- core/include/vecmem/edm/impl/proxy.ipp | 53 ++ core/include/vecmem/edm/proxy.hpp | 97 +++ core/include/vecmem/utils/copy.hpp | 10 +- core/include/vecmem/utils/impl/copy.ipp | 10 +- tests/core/test_core_edm_host.cpp | 92 ++- 9 files changed, 935 insertions(+), 70 deletions(-) create mode 100644 core/include/vecmem/edm/details/proxy_traits.hpp create mode 100644 core/include/vecmem/edm/impl/proxy.ipp create mode 100644 core/include/vecmem/edm/proxy.hpp diff --git a/core/include/vecmem/edm/container.hpp b/core/include/vecmem/edm/container.hpp index 2577f117..a487b42b 100644 --- a/core/include/vecmem/edm/container.hpp +++ b/core/include/vecmem/edm/container.hpp @@ -40,7 +40,7 @@ struct container { #if __cplusplus >= 201700L /// Host container type - using host = interface_type >; + using host = interface_type>; /// (Non-const) Data type using data = vecmem::edm::data; @@ -52,10 +52,9 @@ struct container { #endif // __cplusplus >= 201700L /// (Non-const) Device container type - using device = interface_type >; + using device = interface_type>; /// (Const) Device container type - using const_device = - interface_type >; + using const_device = interface_type>; /// (Non-const) View type using view = vecmem::edm::view; diff --git a/core/include/vecmem/edm/details/proxy_traits.hpp b/core/include/vecmem/edm/details/proxy_traits.hpp new file mode 100644 index 00000000..75ccd29e --- /dev/null +++ b/core/include/vecmem/edm/details/proxy_traits.hpp @@ -0,0 +1,551 @@ +/* VecMem project, part of the ACTS project (R&D line) + * + * (c) 2024 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ +#pragma once + +// Local include(s). +#include "vecmem/containers/device_vector.hpp" +#include "vecmem/containers/vector.hpp" +#include "vecmem/edm/details/device_traits.hpp" +#include "vecmem/edm/details/host_traits.hpp" +#include "vecmem/edm/schema.hpp" +#include "vecmem/utils/tuple.hpp" + +// System include(s). +#include +#include + +namespace vecmem { +namespace edm { +namespace details { + +/// @brief The type of the proxy to use for a given container variable +enum class proxy_type { + /// Proxy for a host container element + host, + /// Proxy for a device container element + device +}; + +/// @brief The "access type" of the proxy to use for a given container variable +enum class proxy_access { + /// Proxy for a non-const container element + non_constant, + /// Proxy for a const container element + constant +}; + +/// @name Traits for the proxied variable types +/// @{ + +/// Technical base class for the @c proxy_var_type traits +template +struct proxy_var_type; + +/// Constant access to a scalar variable (both host and device) +template +struct proxy_var_type, PTYPE, proxy_access::constant> { + + /// The scalar is kept by value in the proxy + using type = VTYPE; + /// It is returned as a const reference even on non-const access + using return_type = std::add_lvalue_reference_t>; + /// It is returned as a const reference on const access + using const_return_type = return_type; + + /// Helper function constructing a scalar proxy variable + template + static return_type make(ITYPE, return_type variable) { + return variable; + } +}; + +/// Non-const access to a scalar variable (both host and device) +template +struct proxy_var_type, PTYPE, proxy_access::non_constant> { + + /// The scalar is kept by lvalue reference in the proxy + using type = std::add_lvalue_reference_t; + /// It is returned as a non-const lvalue reference on non-const access + using return_type = type; + /// It is returned as a const reference on const access + using const_return_type = + std::add_lvalue_reference_t>; + + /// Helper function constructing a scalar proxy variable + template + static return_type make(ITYPE, return_type variable) { + return variable; + } +}; + +/// Constant access to a vector variable from a host container +template +struct proxy_var_type, proxy_type::host, + proxy_access::constant> { + + /// Vector elements are kept by value in the proxy + using type = VTYPE; + /// They are returned as a const reference even on non-const access + using return_type = std::add_lvalue_reference_t>; + /// They are returned as a const reference on const access + using const_return_type = return_type; + + /// Helper function constructing a vector proxy variable + static return_type make(typename vector::size_type i, + const vector& vec) { + return vec.at(i); + } +}; + +/// Non-const access to a vector variable from a host container +template +struct proxy_var_type, proxy_type::host, + proxy_access::non_constant> { + + /// Vector elements are kept by lvalue reference in the proxy + using type = std::add_lvalue_reference_t; + /// They are returned as a non-const lvalue reference on non-const access + using return_type = type; + /// They are returned as a const reference on const access + using const_return_type = + std::add_lvalue_reference_t>; + + /// Helper function constructing a vector proxy variable + static return_type make(typename vector::size_type i, + vector& vec) { + return vec.at(i); + } +}; + +/// Constant access to a vector variable from a device container +template +struct proxy_var_type, proxy_type::device, + proxy_access::constant> { + + /// Vector elements are kept by value in the proxy + using type = typename proxy_var_type::type; + /// They are returned as a const reference even on non-const access + using return_type = + typename proxy_var_type::return_type; + /// They are returned as a const reference on const access + using const_return_type = + typename proxy_var_type::const_return_type; + + /// Helper function constructing a vector proxy variable + static return_type make( + typename device_vector>::size_type i, + const device_vector>& vec) { + + return vec.at(i); + } +}; + +/// Non-const access to a vector variable from a device container +template +struct proxy_var_type, proxy_type::device, + proxy_access::non_constant> { + + /// Vector elements are kept by lvalue reference in the proxy + using type = typename proxy_var_type::type; + /// They are returned as a non-const lvalue reference on non-const access + using return_type = + typename proxy_var_type::return_type; + /// They are returned as a const reference on const access + using const_return_type = + typename proxy_var_type::const_return_type; + + /// Helper function constructing a vector proxy variable + static return_type make(typename device_vector::size_type i, + device_vector& vec) { + + return vec.at(i); + } +}; + +/// Constant access to a jagged vector variable from a host container +template +struct proxy_var_type, proxy_type::host, + proxy_access::constant> { + + /// Jagged vector elements are kept by constant reference in the proxy + using type = std::add_lvalue_reference_t>>; + /// They are returned as a const reference even on non-const access + using return_type = type; + /// They are returned as a const reference on const access + using const_return_type = type; + + /// Helper function constructing a vector proxy variable + static return_type make(typename jagged_vector::size_type i, + const jagged_vector& vec) { + + return vec.at(i); + } +}; + +/// Non-const access to a jagged vector variable from a host container +template +struct proxy_var_type, proxy_type::host, + proxy_access::non_constant> { + + /// Jagged vector elements are kept by non-const lvalue reference in the + /// proxy + using type = std::add_lvalue_reference_t>; + /// They are returned as a non-const lvalue reference on non-const access + using return_type = type; + /// They are returned as a const reference on const access + using const_return_type = + std::add_lvalue_reference_t>>; + + /// Helper function constructing a vector proxy variable + static return_type make(typename jagged_vector::size_type i, + jagged_vector& vec) { + + return vec.at(i); + } +}; + +/// Constant access to a jagged vector variable from a device container +template +struct proxy_var_type, proxy_type::device, + proxy_access::constant> { + + /// Jagged vector elements are kept by constant device vectors in the proxy + using type = device_vector>; + /// They are returned as a const reference to the device vector even in + /// non-const access + using return_type = std::add_lvalue_reference_t>; + /// They are returned as a const reference to the device vector on const + /// access + using const_return_type = return_type; + + /// Helper function constructing a vector proxy variable + static return_type make( + typename jagged_device_vector>::size_type i, + const jagged_device_vector>& vec) { + + return vec.at(i); + } +}; + +/// Non-const access to a jagged vector variable from a device container +template +struct proxy_var_type, proxy_type::device, + proxy_access::non_constant> { + + /// Jagged vector elements are kept by non-const device vectors in the proxy + using type = device_vector; + /// They are returned as non-const lvalue references to the non-const device + /// vector in non-const access + using return_type = std::add_lvalue_reference_t; + /// They are returned as const references to the non-const device vector in + /// const access + using const_return_type = + std::add_lvalue_reference_t>; + + /// Helper function constructing a vector proxy variable + static type make(typename jagged_device_vector::size_type i, + jagged_device_vector& vec) { + + return vec.at(i); + } +}; + +/// Proxy types for one element of a type pack +template +struct proxy_var_type_at { + /// Type of the variable held by the proxy + using type = + typename proxy_var_type>, + PTYPE, CTYPE>::type; + /// Return type on non-const access to the proxy + using return_type = + typename proxy_var_type>, + PTYPE, CTYPE>::return_type; + /// Return type on const access to the proxy + using const_return_type = + typename proxy_var_type>, + PTYPE, CTYPE>::const_return_type; +}; + +/// @} + +/// @name Traits for creating the proxy variables +/// @{ + +/// Technical base class for the @c proxy_data_creator traits +template +struct proxy_data_creator; + +template +struct proxy_data_creator, proxy_type::host, + proxy_access::constant> { + + /// Make all other instantiations of the struct friends + template + friend struct proxy_data_creator; + + /// Proxy tuple type created by the helper + using proxy_tuple_type = + tuple::type>; + + /// Construct the tuple used by the proxy + template + static proxy_tuple_type make(std::size_t i, const CONTAINER& c) { + return make_impl<0>(i, c); + } + +private: + template + static proxy_tuple_type make_impl(std::size_t i, const CONTAINER& c) { + + return { + proxy_var_type:: + make(i, c.template get())}; + } +}; + +template +struct proxy_data_creator, proxy_type::host, + proxy_access::non_constant> { + + /// Make all other instantiations of the struct friends + template + friend struct proxy_data_creator; + + /// Proxy tuple type created by the helper + using proxy_tuple_type = + tuple::type>; + + /// Construct the tuple used by the proxy + template + static proxy_tuple_type make(std::size_t i, CONTAINER& c) { + return make_impl<0>(i, c); + } + +private: + template + static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { + + return {proxy_var_type< + VARTYPE, proxy_type::host, + proxy_access::non_constant>::make(i, c.template get())}; + } +}; + +template +struct proxy_data_creator, proxy_type::device, + proxy_access::constant> { + + /// Make all other instantiations of the struct friends + template + friend struct proxy_data_creator; + + /// Proxy tuple type created by the helper + using proxy_tuple_type = + tuple::type>; + + /// Construct the tuple used by the proxy + template + static proxy_tuple_type make(unsigned int i, const CONTAINER& c) { + return make_impl<0>(i, c); + } + +private: + template + static proxy_tuple_type make_impl(unsigned int i, const CONTAINER& c) { + + return {proxy_var_type< + VARTYPE, proxy_type::device, + proxy_access::constant>::make(i, c.template get())}; + } +}; + +template +struct proxy_data_creator, proxy_type::device, + proxy_access::non_constant> { + + /// Make all other instantiations of the struct friends + template + friend struct proxy_data_creator; + + /// Proxy tuple type created by the helper + using proxy_tuple_type = + tuple::type>; + + /// Construct the tuple used by the proxy + template + static proxy_tuple_type make(unsigned int i, CONTAINER& c) { + return make_impl<0>(i, c); + } + +private: + template + static proxy_tuple_type make_impl(unsigned int i, CONTAINER& c) { + + return {proxy_var_type< + VARTYPE, proxy_type::device, + proxy_access::non_constant>::make(i, c.template get())}; + } +}; + +template +struct proxy_data_creator, proxy_type::host, + proxy_access::constant> { + + /// Make all other instantiations of the struct friends + template + friend struct proxy_data_creator; + + /// Proxy tuple type created by the helper + using proxy_tuple_type = + tuple::type, + typename proxy_var_type::type...>; + + /// Construct the tuple used by the proxy + template + static proxy_tuple_type make(std::size_t i, const CONTAINER& c) { + return make_impl<0>(i, c); + } + +private: + template + static proxy_tuple_type make_impl(std::size_t i, const CONTAINER& c) { + + return proxy_tuple_type( + proxy_var_type:: + make(i, c.template get()), + proxy_data_creator< + schema, proxy_type::host, + proxy_access::constant>::template make_impl(i, c)); + } +}; + +template +struct proxy_data_creator, proxy_type::host, + proxy_access::non_constant> { + + /// Make all other instantiations of the struct friends + template + friend struct proxy_data_creator; + + /// Proxy tuple type created by the helper + using proxy_tuple_type = + tuple::type, + typename proxy_var_type::type...>; + + /// Construct the tuple used by the proxy + template + static proxy_tuple_type make(std::size_t i, CONTAINER& c) { + return make_impl<0>(i, c); + } + +private: + template + static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { + + return proxy_tuple_type( + proxy_var_type< + VARTYPE, proxy_type::host, + proxy_access::non_constant>::make(i, c.template get()), + proxy_data_creator< + schema, proxy_type::host, + proxy_access::non_constant>::template make_impl(i, + c)); + } +}; + +template +struct proxy_data_creator, proxy_type::device, + proxy_access::constant> { + + /// Make all other instantiations of the struct friends + template + friend struct proxy_data_creator; + + /// Proxy tuple type created by the helper + using proxy_tuple_type = + tuple::type, + typename proxy_var_type::type...>; + + /// Construct the tuple used by the proxy + template + static proxy_tuple_type make(unsigned int i, const CONTAINER& c) { + return make_impl<0>(i, c); + } + +private: + template + static proxy_tuple_type make_impl(unsigned int i, const CONTAINER& c) { + + return proxy_tuple_type( + proxy_var_type< + VARTYPE, proxy_type::device, + proxy_access::constant>::make(i, c.template get()), + proxy_data_creator< + schema, proxy_type::device, + proxy_access::constant>::template make_impl(i, c)); + } +}; + +template +struct proxy_data_creator, proxy_type::device, + proxy_access::non_constant> { + + /// Make all other instantiations of the struct friends + template + friend struct proxy_data_creator; + + /// Proxy tuple type created by the helper + using proxy_tuple_type = + tuple::type, + typename proxy_var_type::type...>; + + /// Construct the tuple used by the proxy + template + static proxy_tuple_type make(unsigned int i, CONTAINER& c) { + return make_impl<0>(i, c); + } + +private: + template + static proxy_tuple_type make_impl(unsigned int i, CONTAINER& c) { + + return proxy_tuple_type( + proxy_var_type< + VARTYPE, proxy_type::device, + proxy_access::non_constant>::make(i, c.template get()), + proxy_data_creator< + schema, proxy_type::device, + proxy_access::non_constant>::template make_impl(i, + c)); + } +}; + +/// @} + +} // namespace details +} // namespace edm +} // namespace vecmem diff --git a/core/include/vecmem/edm/host.hpp b/core/include/vecmem/edm/host.hpp index 44ab1e47..6648a250 100644 --- a/core/include/vecmem/edm/host.hpp +++ b/core/include/vecmem/edm/host.hpp @@ -1,6 +1,6 @@ /* VecMem project, part of the ACTS project (R&D line) * - * (c) 2023 CERN for the benefit of the ACTS project + * (c) 2023-2024 CERN for the benefit of the ACTS project * * Mozilla Public License Version 2.0 */ @@ -10,6 +10,7 @@ #include "vecmem/edm/data.hpp" #include "vecmem/edm/details/host_traits.hpp" #include "vecmem/edm/details/schema_traits.hpp" +#include "vecmem/edm/proxy.hpp" #include "vecmem/edm/schema.hpp" #include "vecmem/memory/memory_resource.hpp" #include "vecmem/utils/types.hpp" @@ -22,8 +23,8 @@ namespace vecmem { namespace edm { -/// Technical base type for @c host> -template +/// Technical base type for @c host,INTERFACE> +template class I> class host; /// Structure-of-Arrays host container @@ -34,8 +35,8 @@ class host; /// /// @tparam ...VARTYPES The variable types to store in the host container /// -template -class host> { +template class INTERFACE> +class host, INTERFACE> { public: /// The schema describing the host's payload @@ -45,6 +46,17 @@ class host> { /// The tuple type holding all of the the individual variable vectors using tuple_type = std::tuple::type...>; + /// The type of the interface used for the proxy objects + template + using interface_type = INTERFACE; + /// The type of the (non-const) proxy objects for the container elements + using proxy_type = + interface_type>; + /// The type of the (const) proxy objects for the container elements + using const_proxy_type = + interface_type>; /// @name Constructors and assignment operators /// @{ @@ -78,6 +90,40 @@ class host> { typename details::host_type_at::const_return_type get() const; + /// Get a proxy object for a specific variable (non-const) + /// + /// While also checking that the index is within bounds. + /// + /// @param index The index of the element to proxy + /// @return A proxy object for the element at the given index + /// + VECMEM_HOST + proxy_type at(size_type index); + /// Get a proxy object for a specific variable (const) + /// + /// While also checking that the index is within bounds. + /// + /// @param index The index of the element to proxy + /// @return A proxy object for the element at the given index + /// + VECMEM_HOST + const_proxy_type at(size_type index) const; + + /// Get a proxy object for a specific variable (non-const) + /// + /// @param index The index of the element to proxy + /// @return A proxy object for the element at the given index + /// + VECMEM_HOST + proxy_type operator[](size_type index); + /// Get a proxy object for a specific variable (const) + /// + /// @param index The index of the element to proxy + /// @return A proxy object for the element at the given index + /// + VECMEM_HOST + const_proxy_type operator[](size_type index) const; + /// @} /// @name Function(s) meant for internal use by other VecMem types @@ -113,9 +159,9 @@ class host> { /// @param resource The memory resource to use for any allocation(s) /// @return A (non-const) data object describing the host container /// -template +template class INTERFACE> VECMEM_HOST edm::data> get_data( - edm::host>& host, + edm::host, INTERFACE>& host, memory_resource* resource = nullptr); /// Helper function for getting a (const) data object for a host container @@ -125,9 +171,9 @@ VECMEM_HOST edm::data> get_data( /// @param resource The memory resource to use for any allocation(s) /// @return A (const) data object describing the host container /// -template +template class INTERFACE> VECMEM_HOST edm::data>> -get_data(const edm::host>& host, +get_data(const edm::host, INTERFACE>& host, memory_resource* resource = nullptr); } // namespace vecmem diff --git a/core/include/vecmem/edm/impl/host.ipp b/core/include/vecmem/edm/impl/host.ipp index e3c38e97..881bbeae 100644 --- a/core/include/vecmem/edm/impl/host.ipp +++ b/core/include/vecmem/edm/impl/host.ipp @@ -17,13 +17,14 @@ namespace vecmem { namespace edm { -template -VECMEM_HOST host>::host(memory_resource& resource) +template class INTERFACE> +VECMEM_HOST host, INTERFACE>::host( + memory_resource& resource) : m_data{details::host_alloc::make(resource)...}, m_resource{resource} {} -template -VECMEM_HOST std::size_t host>::size() const { +template class INTERFACE> +VECMEM_HOST std::size_t host, INTERFACE>::size() const { // Make sure that there are some (jagged) vector types in the container. static_assert( @@ -35,8 +36,9 @@ VECMEM_HOST std::size_t host>::size() const { m_data, std::index_sequence_for{}); } -template -VECMEM_HOST void host>::resize(std::size_t size) { +template class INTERFACE> +VECMEM_HOST void host, INTERFACE>::resize( + std::size_t size) { // Make sure that there are some (jagged) vector types in the container. static_assert( @@ -48,8 +50,9 @@ VECMEM_HOST void host>::resize(std::size_t size) { std::index_sequence_for{}); } -template -VECMEM_HOST void host>::reserve(std::size_t size) { +template class INTERFACE> +VECMEM_HOST void host, INTERFACE>::reserve( + std::size_t size) { // Make sure that there are some (jagged) vector types in the container. static_assert( @@ -61,10 +64,10 @@ VECMEM_HOST void host>::reserve(std::size_t size) { std::index_sequence_for{}); } -template +template class INTERFACE> template VECMEM_HOST typename details::host_type_at::return_type -host>::get() { +host, INTERFACE>::get() { // For scalar types we don't return the array, but rather a // reference to the single scalar held by the array. @@ -76,11 +79,11 @@ host>::get() { } } -template +template class INTERFACE> template VECMEM_HOST typename details::host_type_at::const_return_type - host>::get() const { + host, INTERFACE>::get() const { // For scalar types we don't return the array, but rather a // reference to the single scalar held by the array. @@ -92,22 +95,67 @@ VECMEM_HOST } } -template -VECMEM_HOST typename host>::tuple_type& -host>::variables() { +template class INTERFACE> +VECMEM_HOST typename host, INTERFACE>::proxy_type +host, INTERFACE>::at(size_type index) { + + // Make sure that the index is within bounds. + if (index >= size()) { + throw std::out_of_range("index (" + std::to_string(index) + + ") >= size (" + std::to_string(size()) + ")"); + } + + // Create the proxy. + return proxy_type{*this, index}; +} + +template class INTERFACE> +VECMEM_HOST typename host, INTERFACE>::const_proxy_type +host, INTERFACE>::at(size_type index) const { + + // Make sure that the index is within bounds. + if (index >= size()) { + throw std::out_of_range("index (" + std::to_string(index) + + ") >= size (" + std::to_string(size()) + ")"); + } + + // Create the proxy. + return const_proxy_type{*this, index}; +} + +template class INTERFACE> +VECMEM_HOST typename host, INTERFACE>::proxy_type +host, INTERFACE>::operator[](size_type index) { + + // Create the proxy. + return proxy_type{*this, index}; +} + +template class INTERFACE> +VECMEM_HOST typename host, INTERFACE>::const_proxy_type +host, INTERFACE>::operator[](size_type index) const { + + // Create the proxy. + return const_proxy_type{*this, index}; +} + +template class INTERFACE> +VECMEM_HOST typename host, INTERFACE>::tuple_type& +host, INTERFACE>::variables() { return m_data; } -template -VECMEM_HOST const typename host>::tuple_type& -host>::variables() const { +template class INTERFACE> +VECMEM_HOST const typename host, INTERFACE>::tuple_type& +host, INTERFACE>::variables() const { return m_data; } -template -VECMEM_HOST memory_resource& host>::resource() const { +template class INTERFACE> +VECMEM_HOST memory_resource& host, INTERFACE>::resource() + const { return m_resource; } @@ -115,17 +163,18 @@ VECMEM_HOST memory_resource& host>::resource() const { } // namespace edm /// Helper function terminal node -template -VECMEM_HOST void get_data_impl(edm::host>&, +template class INTERFACE> +VECMEM_HOST void get_data_impl(edm::host, INTERFACE>&, edm::data>&, memory_resource&, std::index_sequence<>) {} /// Helper function recursive node -template -VECMEM_HOST void get_data_impl(edm::host>& host, - edm::data>& data, - memory_resource& mr, - std::index_sequence) { +template class INTERFACE, + std::size_t I, std::size_t... Is> +VECMEM_HOST void get_data_impl( + edm::host, INTERFACE>& host, + edm::data>& data, memory_resource& mr, + std::index_sequence) { if constexpr (edm::type::details::is_jagged_vector_v< typename std::tuple_element< @@ -152,9 +201,10 @@ VECMEM_HOST void get_data_impl(edm::host>& host, get_data_impl(host, data, mr, std::index_sequence{}); } -template +template class INTERFACE> VECMEM_HOST edm::data> get_data( - edm::host>& host, memory_resource* resource) { + edm::host, INTERFACE>& host, + memory_resource* resource) { // Create the result object. edm::data> result; @@ -178,16 +228,17 @@ VECMEM_HOST edm::data> get_data( } /// Helper function terminal node -template +template class INTERFACE> VECMEM_HOST void get_data_impl( - const edm::host>&, + const edm::host, INTERFACE>&, edm::data>>&, memory_resource&, std::index_sequence<>) {} /// Helper function recursive node -template +template class INTERFACE, + std::size_t I, std::size_t... Is> VECMEM_HOST void get_data_impl( - const edm::host>& host, + const edm::host, INTERFACE>& host, edm::data>>& data, memory_resource& mr, std::index_sequence) { @@ -216,9 +267,9 @@ VECMEM_HOST void get_data_impl( get_data_impl(host, data, mr, std::index_sequence{}); } -template +template class INTERFACE> VECMEM_HOST edm::data>> -get_data(const edm::host>& host, +get_data(const edm::host, INTERFACE>& host, memory_resource* resource) { // Create the result object. diff --git a/core/include/vecmem/edm/impl/proxy.ipp b/core/include/vecmem/edm/impl/proxy.ipp new file mode 100644 index 00000000..41415568 --- /dev/null +++ b/core/include/vecmem/edm/impl/proxy.ipp @@ -0,0 +1,53 @@ +/* VecMem project, part of the ACTS project (R&D line) + * + * (c) 2024 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ +#pragma once + +namespace vecmem { +namespace edm { + +template +template +VECMEM_HOST_AND_DEVICE proxy, PTYPE, CTYPE>::proxy( + PARENT& parent, typename PARENT::size_type index) + : m_data{ + details::proxy_data_creator, PTYPE, CTYPE>::make( + index, parent)} {} + +template +template +VECMEM_HOST_AND_DEVICE proxy, PTYPE, CTYPE>::proxy( + const PARENT& parent, typename PARENT::size_type index) + : m_data{ + details::proxy_data_creator, PTYPE, CTYPE>::make( + index, parent)} {} + +template +template +VECMEM_HOST_AND_DEVICE + typename details::proxy_var_type_at::return_type + proxy, PTYPE, CTYPE>::get() { + + return vecmem::get(m_data); +} + +template +template +VECMEM_HOST_AND_DEVICE + typename details::proxy_var_type_at::const_return_type + proxy, PTYPE, CTYPE>::get() const { + + return vecmem::get(m_data); +} + +} // namespace edm +} // namespace vecmem diff --git a/core/include/vecmem/edm/proxy.hpp b/core/include/vecmem/edm/proxy.hpp new file mode 100644 index 00000000..bfbe7166 --- /dev/null +++ b/core/include/vecmem/edm/proxy.hpp @@ -0,0 +1,97 @@ +/* VecMem project, part of the ACTS project (R&D line) + * + * (c) 2024 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ +#pragma once + +// Local include(s). +#include "vecmem/edm/details/proxy_traits.hpp" +#include "vecmem/edm/schema.hpp" +#include "vecmem/utils/tuple.hpp" +#include "vecmem/utils/types.hpp" + +namespace vecmem { +namespace edm { + +/// Technical base type for @c proxy,PTYPE,CTYPE> +template +class proxy; + +/// Structure-of-Arrays element proxy +/// +/// This class implements a "view" of a single element in an SoA container. +/// +/// @tparam ...VARTYPES The variable types to store in the proxy object +/// +template +class proxy, PTYPE, CTYPE> { + +public: + /// The schema describing the host's payload + using schema_type = schema; + /// The type of the proxy (host or device) + static constexpr details::proxy_type proxy_type = PTYPE; + /// The access mode of the proxy (const or non-const) + static constexpr details::proxy_access access_type = CTYPE; + /// The tuple type holding all of the the proxied variables + using tuple_type = + tuple::type...>; + + /// @name Constructors and assignment operators + /// @{ + + /// Constructor of a non-const proxy on top of a parent container + /// + /// @tparam PARENT The type of the parent container + /// @param parent The parent container to create a proxy for + /// @param index The index of the element to proxy + /// + template + VECMEM_HOST_AND_DEVICE proxy(PARENT& parent, + typename PARENT::size_type index); + + /// Constructor of a const proxy on top of a parent container + /// + /// @tparam PARENT The type of the parent container + /// @param parent The parent container to create a proxy for + /// @param index The index of the element to proxy + /// + template + VECMEM_HOST_AND_DEVICE proxy(const PARENT& parent, + typename PARENT::size_type index); + + /// @} + + /// @name Variable accessor functions + /// @{ + + /// Get a specific variable (non-const) + template + VECMEM_HOST_AND_DEVICE + typename details::proxy_var_type_at::return_type + get(); + /// Get a specific variable (const) + template + VECMEM_HOST_AND_DEVICE + typename details::proxy_var_type_at::const_return_type + get() const; + + /// @} + +private: + /// The tuple holding all of the individual proxy objects + tuple_type m_data; + +}; // class proxy + +} // namespace edm +} // namespace vecmem + +// Include the implementation. +#include "vecmem/edm/impl/proxy.ipp" diff --git a/core/include/vecmem/utils/copy.hpp b/core/include/vecmem/utils/copy.hpp index 3ec76656..e8a6f983 100644 --- a/core/include/vecmem/utils/copy.hpp +++ b/core/include/vecmem/utils/copy.hpp @@ -172,11 +172,11 @@ class VECMEM_CORE_EXPORT copy { type::copy_type cptype = type::unknown) const; /// Copy from a view, into a host container - template + template class INTERFACE> event_type operator()( const edm::view>>& from, - edm::host>& to, + edm::host, INTERFACE>& to, type::copy_type cptype = type::unknown) const; /// Get the (outer) size of a resizable SoA container @@ -230,11 +230,13 @@ class VECMEM_CORE_EXPORT copy { template void memset_impl(edm::view> data, int value) const; /// Implementation for setting the sizes of an SoA container - template + template class INTERFACE> void resize_impl( const edm::view>>& from, - edm::host>& to, type::copy_type cptype) const; + edm::host, INTERFACE>& to, + type::copy_type cptype) const; /// Implementation for the variadic @c copy function (for the sizes) template void copy_sizes_impl( diff --git a/core/include/vecmem/utils/impl/copy.ipp b/core/include/vecmem/utils/impl/copy.ipp index 5986cdf9..d73a789b 100644 --- a/core/include/vecmem/utils/impl/copy.ipp +++ b/core/include/vecmem/utils/impl/copy.ipp @@ -431,11 +431,12 @@ copy::event_type copy::operator()( return create_event(); } -template +template class INTERFACE> copy::event_type copy::operator()( const edm::view>>& from_view, - edm::host>& to_vec, type::copy_type cptype) const { + edm::host, INTERFACE>& to_vec, + type::copy_type cptype) const { // Resize the output object to the correct size(s). resize_impl<0>(from_view, to_vec, cptype); @@ -761,11 +762,12 @@ void copy::memset_impl(edm::view> data, } } -template +template class INTERFACE> void copy::resize_impl( const edm::view>>& from_view, - edm::host>& to_vec, + edm::host, INTERFACE>& to_vec, [[maybe_unused]] type::copy_type cptype) const { // The target is a host container, so the copy type can't be anything diff --git a/tests/core/test_core_edm_host.cpp b/tests/core/test_core_edm_host.cpp index 1da56471..2d6f3f23 100644 --- a/tests/core/test_core_edm_host.cpp +++ b/tests/core/test_core_edm_host.cpp @@ -1,6 +1,6 @@ /* VecMem project, part of the ACTS project (R&D line) * - * (c) 2023 CERN for the benefit of the ACTS project + * (c) 2023-2024 CERN for the benefit of the ACTS project * * Mozilla Public License Version 2.0 */ @@ -24,12 +24,25 @@ class core_edm_host_test : public testing::Test { vecmem::edm::type::jagged_vector>; /// Constant schema used for the test. using const_schema = vecmem::edm::details::add_const_t; + /// Interface for the test container. + template + struct interface : public BASE { + using BASE::BASE; + auto& scalar() { return BASE::template get<0>(); } + const auto& scalar() const { return BASE::template get<0>(); } + auto& vector() { return BASE::template get<1>(); } + const auto& vector() const { return BASE::template get<1>(); } + auto& jagged_vector() { return BASE::template get<2>(); } + const auto& jagged_vector() const { return BASE::template get<2>(); } + }; + /// Host container type used in the test. + using host_type = interface>; /// Create a host container object with some non-trivial content. - vecmem::edm::host create() { + host_type create() { // Create a host container, and fill it. - vecmem::edm::host host{m_resource}; + host_type host{m_resource}; static constexpr std::size_t SIZE = 5; host.resize(SIZE); host.get<0>() = 1; @@ -52,13 +65,12 @@ class core_edm_host_test : public testing::Test { TEST_F(core_edm_host_test, construct_assign) { // Construct a host container, and make sure that it looks okay. - vecmem::edm::host host1 = create(); + host_type host1 = create(); EXPECT_EQ(host1.size(), host1.get<1>().size()); EXPECT_EQ(host1.size(), host1.get<2>().size()); // Lambda comparing two host containers. - auto compare = [](const vecmem::edm::host& h1, - const vecmem::edm::host& h2) { + auto compare = [](const host_type& h1, const host_type& h2) { EXPECT_EQ(h1.size(), h2.size()); EXPECT_EQ(h1.get<0>(), h2.get<0>()); EXPECT_EQ(h1.get<1>(), h2.get<1>()); @@ -66,12 +78,12 @@ TEST_F(core_edm_host_test, construct_assign) { }; // Construct a copy of it, and check that it is the same. - vecmem::edm::host host2{host1}; + host_type host2{host1}; compare(host1, host2); // Create a new host container, and assign the first one to it. Check that // this also works as it should. - vecmem::edm::host host3{m_resource}; + host_type host3{m_resource}; host3 = host1; compare(host1, host3); } @@ -79,10 +91,11 @@ TEST_F(core_edm_host_test, construct_assign) { TEST_F(core_edm_host_test, get_data) { // Construct a host container. - vecmem::edm::host host1 = create(); + host_type host1 = create(); // Lambda checking a data object against a host container. - auto compare = [](const auto& data, const vecmem::edm::host& host) { + auto compare = [](const auto& data, + const vecmem::edm::host& host) { EXPECT_EQ(data.capacity(), host.size()); EXPECT_EQ(data.template get<0>(), &(host.get<0>())); EXPECT_EQ(data.template get<1>().size(), host.size()); @@ -108,7 +121,7 @@ TEST_F(core_edm_host_test, get_data) { // Get a const data object for it, and check its contents. vecmem::edm::data data2 = - [](const vecmem::edm::host& host) { + [](const vecmem::edm::host& host) { return vecmem::get_data(host); }(host1); compare(data2, host1); @@ -117,11 +130,11 @@ TEST_F(core_edm_host_test, get_data) { TEST_F(core_edm_host_test, device) { // Construct a host container. - vecmem::edm::host host1 = create(); + host_type host1 = create(); // Lambda comparing the contents of a host and a device container. auto compare = [](const auto& device, - const vecmem::edm::host& host) { + const vecmem::edm::host& host) { ASSERT_EQ(device.size(), host.size()); EXPECT_EQ(device.template get<0>(), host.get<0>()); for (vecmem::edm::details::size_type i = 0; i < device.size(); ++i) { @@ -141,7 +154,7 @@ TEST_F(core_edm_host_test, device) { compare(device1, host1); // Create constant device objects for it, and check their contents. - auto data2 = [](const vecmem::edm::host& host) { + auto data2 = [](const vecmem::edm::host& host) { return vecmem::get_data(host); }(host1); vecmem::edm::device device2{data2}; @@ -149,3 +162,54 @@ TEST_F(core_edm_host_test, device) { vecmem::edm::device device3{data1}; compare(device3, host1); } + +TEST_F(core_edm_host_test, proxy) { + + // Construct a host container. + host_type host = create(); + + // Compare the contents retrieved through the proxy interface, with the + // contents retrieved through its container interface. + for (std::size_t i = 0; i < host.size(); ++i) { + EXPECT_EQ(host.at(i).scalar(), host.scalar()); + EXPECT_EQ(host[i].scalar(), host.scalar()); + EXPECT_FLOAT_EQ(host.at(i).vector(), host.vector().at(i)); + EXPECT_FLOAT_EQ(host[i].vector(), host.vector()[i]); + ASSERT_EQ(host.at(i).jagged_vector().size(), + host.jagged_vector().at(i).size()); + ASSERT_EQ(host[i].jagged_vector().size(), + host.jagged_vector()[i].size()); + for (std::size_t j = 0; j < host.jagged_vector().at(i).size(); ++j) { + EXPECT_DOUBLE_EQ(host.at(i).jagged_vector().at(j), + host.jagged_vector().at(i).at(j)); + EXPECT_DOUBLE_EQ(host[i].jagged_vector()[j], + host.jagged_vector()[i][j]); + } + } +} + +TEST_F(core_edm_host_test, const_proxy) { + + // Construct a host container. + host_type nchost = create(); + const host_type& host = nchost; + + // Compare the contents retrieved through the proxy interface, with the + // contents retrieved through its container interface. + for (std::size_t i = 0; i < host.size(); ++i) { + EXPECT_EQ(host.at(i).scalar(), host.scalar()); + EXPECT_EQ(host[i].scalar(), host.scalar()); + EXPECT_FLOAT_EQ(host.at(i).vector(), host.vector().at(i)); + EXPECT_FLOAT_EQ(host[i].vector(), host.vector()[i]); + ASSERT_EQ(host.at(i).jagged_vector().size(), + host.jagged_vector().at(i).size()); + ASSERT_EQ(host[i].jagged_vector().size(), + host.jagged_vector()[i].size()); + for (std::size_t j = 0; j < host.jagged_vector().at(i).size(); ++j) { + EXPECT_DOUBLE_EQ(host.at(i).jagged_vector().at(j), + host.jagged_vector().at(i).at(j)); + EXPECT_DOUBLE_EQ(host[i].jagged_vector()[j], + host.jagged_vector()[i][j]); + } + } +} From 95f8a9d57ae12c82e3a51333d1de4d4aba344376 Mon Sep 17 00:00:00 2001 From: Attila Krasznahorkay Date: Wed, 25 Sep 2024 17:34:51 +0200 Subject: [PATCH 3/5] Added "proxy functionality" to vecmem::edm::device as well. While also ensuring that vecmem::edm::proxy would be usable in device code, and also with C++14. --- .../vecmem/containers/device_vector.hpp | 4 +- core/include/vecmem/edm/container.hpp | 6 +- .../vecmem/edm/details/proxy_traits.hpp | 103 ++++++++++++------ core/include/vecmem/edm/device.hpp | 56 +++++++++- core/include/vecmem/edm/host.hpp | 1 + core/include/vecmem/edm/impl/device.ipp | 101 ++++++++++++----- core/include/vecmem/edm/impl/host.ipp | 8 +- core/include/vecmem/edm/proxy.hpp | 2 + tests/common/simple_soa_container_helpers.hpp | 4 +- tests/core/test_core_edm_buffer.cpp | 12 +- tests/core/test_core_edm_device.cpp | 20 +++- tests/core/test_core_edm_host.cpp | 6 +- 12 files changed, 231 insertions(+), 92 deletions(-) diff --git a/core/include/vecmem/containers/device_vector.hpp b/core/include/vecmem/containers/device_vector.hpp index b59ded56..1ee29b71 100644 --- a/core/include/vecmem/containers/device_vector.hpp +++ b/core/include/vecmem/containers/device_vector.hpp @@ -21,7 +21,7 @@ namespace vecmem { // Forward declaration(s). namespace edm { -template +template class I> class device; } @@ -36,7 +36,7 @@ template class device_vector { // Make @c vecmem::edm::device a friend of this class. - template + template class I> friend class edm::device; public: diff --git a/core/include/vecmem/edm/container.hpp b/core/include/vecmem/edm/container.hpp index a487b42b..1d8ec69c 100644 --- a/core/include/vecmem/edm/container.hpp +++ b/core/include/vecmem/edm/container.hpp @@ -52,9 +52,11 @@ struct container { #endif // __cplusplus >= 201700L /// (Non-const) Device container type - using device = interface_type>; + using device = + interface_type>; /// (Const) Device container type - using const_device = interface_type>; + using const_device = + interface_type>; /// (Non-const) View type using view = vecmem::edm::view; diff --git a/core/include/vecmem/edm/details/proxy_traits.hpp b/core/include/vecmem/edm/details/proxy_traits.hpp index 75ccd29e..d04ebc63 100644 --- a/core/include/vecmem/edm/details/proxy_traits.hpp +++ b/core/include/vecmem/edm/details/proxy_traits.hpp @@ -8,11 +8,13 @@ // Local include(s). #include "vecmem/containers/device_vector.hpp" -#include "vecmem/containers/vector.hpp" -#include "vecmem/edm/details/device_traits.hpp" -#include "vecmem/edm/details/host_traits.hpp" +#include "vecmem/containers/jagged_device_vector.hpp" #include "vecmem/edm/schema.hpp" #include "vecmem/utils/tuple.hpp" +#if __cplusplus >= 201700L +#include "vecmem/containers/jagged_vector.hpp" +#include "vecmem/containers/vector.hpp" +#endif // __cplusplus >= 201700L // System include(s). #include @@ -58,7 +60,8 @@ struct proxy_var_type, PTYPE, proxy_access::constant> { /// Helper function constructing a scalar proxy variable template - static return_type make(ITYPE, return_type variable) { + VECMEM_HOST_AND_DEVICE static return_type make(ITYPE, + return_type variable) { return variable; } }; @@ -77,11 +80,14 @@ struct proxy_var_type, PTYPE, proxy_access::non_constant> { /// Helper function constructing a scalar proxy variable template - static return_type make(ITYPE, return_type variable) { + VECMEM_HOST_AND_DEVICE static return_type make(ITYPE, + return_type variable) { return variable; } }; +#if __cplusplus >= 201700L + /// Constant access to a vector variable from a host container template struct proxy_var_type, proxy_type::host, @@ -95,6 +101,7 @@ struct proxy_var_type, proxy_type::host, using const_return_type = return_type; /// Helper function constructing a vector proxy variable + VECMEM_HOST static return_type make(typename vector::size_type i, const vector& vec) { return vec.at(i); @@ -115,30 +122,29 @@ struct proxy_var_type, proxy_type::host, std::add_lvalue_reference_t>; /// Helper function constructing a vector proxy variable + VECMEM_HOST static return_type make(typename vector::size_type i, vector& vec) { return vec.at(i); } }; +#endif // __cplusplus >= 201700L + /// Constant access to a vector variable from a device container template struct proxy_var_type, proxy_type::device, proxy_access::constant> { /// Vector elements are kept by value in the proxy - using type = typename proxy_var_type::type; + using type = VTYPE; /// They are returned as a const reference even on non-const access - using return_type = - typename proxy_var_type::return_type; + using return_type = std::add_lvalue_reference_t>; /// They are returned as a const reference on const access - using const_return_type = - typename proxy_var_type::const_return_type; + using const_return_type = return_type; /// Helper function constructing a vector proxy variable + VECMEM_HOST_AND_DEVICE static return_type make( typename device_vector>::size_type i, const device_vector>& vec) { @@ -153,18 +159,15 @@ struct proxy_var_type, proxy_type::device, proxy_access::non_constant> { /// Vector elements are kept by lvalue reference in the proxy - using type = typename proxy_var_type::type; + using type = std::add_lvalue_reference_t; /// They are returned as a non-const lvalue reference on non-const access - using return_type = - typename proxy_var_type::return_type; + using return_type = type; /// They are returned as a const reference on const access using const_return_type = - typename proxy_var_type::const_return_type; + std::add_lvalue_reference_t>; /// Helper function constructing a vector proxy variable + VECMEM_HOST_AND_DEVICE static return_type make(typename device_vector::size_type i, device_vector& vec) { @@ -172,6 +175,8 @@ struct proxy_var_type, proxy_type::device, } }; +#if __cplusplus >= 201700L + /// Constant access to a jagged vector variable from a host container template struct proxy_var_type, proxy_type::host, @@ -185,6 +190,7 @@ struct proxy_var_type, proxy_type::host, using const_return_type = type; /// Helper function constructing a vector proxy variable + VECMEM_HOST static return_type make(typename jagged_vector::size_type i, const jagged_vector& vec) { @@ -207,6 +213,7 @@ struct proxy_var_type, proxy_type::host, std::add_lvalue_reference_t>>; /// Helper function constructing a vector proxy variable + VECMEM_HOST static return_type make(typename jagged_vector::size_type i, jagged_vector& vec) { @@ -214,6 +221,8 @@ struct proxy_var_type, proxy_type::host, } }; +#endif // __cplusplus >= 201700L + /// Constant access to a jagged vector variable from a device container template struct proxy_var_type, proxy_type::device, @@ -229,6 +238,7 @@ struct proxy_var_type, proxy_type::device, using const_return_type = return_type; /// Helper function constructing a vector proxy variable + VECMEM_HOST_AND_DEVICE static return_type make( typename jagged_device_vector>::size_type i, const jagged_device_vector>& vec) { @@ -253,6 +263,7 @@ struct proxy_var_type, proxy_type::device, std::add_lvalue_reference_t>; /// Helper function constructing a vector proxy variable + VECMEM_HOST_AND_DEVICE static type make(typename jagged_device_vector::size_type i, jagged_device_vector& vec) { @@ -287,6 +298,8 @@ struct proxy_var_type_at { template struct proxy_data_creator; +#if __cplusplus >= 201700L + template struct proxy_data_creator, proxy_type::host, proxy_access::constant> { @@ -302,13 +315,15 @@ struct proxy_data_creator, proxy_type::host, /// Construct the tuple used by the proxy template - static proxy_tuple_type make(std::size_t i, const CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make(std::size_t i, + const CONTAINER& c) { return make_impl<0>(i, c); } private: template - static proxy_tuple_type make_impl(std::size_t i, const CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, + const CONTAINER& c) { return { proxy_var_type:: @@ -331,13 +346,13 @@ struct proxy_data_creator, proxy_type::host, /// Construct the tuple used by the proxy template - static proxy_tuple_type make(std::size_t i, CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make(std::size_t i, CONTAINER& c) { return make_impl<0>(i, c); } private: template - static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { return {proxy_var_type< VARTYPE, proxy_type::host, @@ -345,6 +360,8 @@ struct proxy_data_creator, proxy_type::host, } }; +#endif // __cplusplus >= 201700L + template struct proxy_data_creator, proxy_type::device, proxy_access::constant> { @@ -360,13 +377,15 @@ struct proxy_data_creator, proxy_type::device, /// Construct the tuple used by the proxy template - static proxy_tuple_type make(unsigned int i, const CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int i, + const CONTAINER& c) { return make_impl<0>(i, c); } private: template - static proxy_tuple_type make_impl(unsigned int i, const CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl( + unsigned int i, const CONTAINER& c) { return {proxy_var_type< VARTYPE, proxy_type::device, @@ -389,13 +408,15 @@ struct proxy_data_creator, proxy_type::device, /// Construct the tuple used by the proxy template - static proxy_tuple_type make(unsigned int i, CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int i, + CONTAINER& c) { return make_impl<0>(i, c); } private: template - static proxy_tuple_type make_impl(unsigned int i, CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl(unsigned int i, + CONTAINER& c) { return {proxy_var_type< VARTYPE, proxy_type::device, @@ -403,6 +424,8 @@ struct proxy_data_creator, proxy_type::device, } }; +#if __cplusplus >= 201700L + template struct proxy_data_creator, proxy_type::host, proxy_access::constant> { @@ -420,13 +443,15 @@ struct proxy_data_creator, proxy_type::host, /// Construct the tuple used by the proxy template - static proxy_tuple_type make(std::size_t i, const CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make(std::size_t i, + const CONTAINER& c) { return make_impl<0>(i, c); } private: template - static proxy_tuple_type make_impl(std::size_t i, const CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, + const CONTAINER& c) { return proxy_tuple_type( proxy_var_type:: @@ -454,13 +479,13 @@ struct proxy_data_creator, proxy_type::host, /// Construct the tuple used by the proxy template - static proxy_tuple_type make(std::size_t i, CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make(std::size_t i, CONTAINER& c) { return make_impl<0>(i, c); } private: template - static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { return proxy_tuple_type( proxy_var_type< @@ -473,6 +498,8 @@ struct proxy_data_creator, proxy_type::host, } }; +#endif // __cplusplus >= 201700L + template struct proxy_data_creator, proxy_type::device, proxy_access::constant> { @@ -490,13 +517,15 @@ struct proxy_data_creator, proxy_type::device, /// Construct the tuple used by the proxy template - static proxy_tuple_type make(unsigned int i, const CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int i, + const CONTAINER& c) { return make_impl<0>(i, c); } private: template - static proxy_tuple_type make_impl(unsigned int i, const CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl( + unsigned int i, const CONTAINER& c) { return proxy_tuple_type( proxy_var_type< @@ -525,13 +554,15 @@ struct proxy_data_creator, proxy_type::device, /// Construct the tuple used by the proxy template - static proxy_tuple_type make(unsigned int i, CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int i, + CONTAINER& c) { return make_impl<0>(i, c); } private: template - static proxy_tuple_type make_impl(unsigned int i, CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl(unsigned int i, + CONTAINER& c) { return proxy_tuple_type( proxy_var_type< diff --git a/core/include/vecmem/edm/device.hpp b/core/include/vecmem/edm/device.hpp index 69dbb490..7e3b7f2f 100644 --- a/core/include/vecmem/edm/device.hpp +++ b/core/include/vecmem/edm/device.hpp @@ -1,6 +1,6 @@ /* VecMem project, part of the ACTS project (R&D line) * - * (c) 2023 CERN for the benefit of the ACTS project + * (c) 2023-2024 CERN for the benefit of the ACTS project * * Mozilla Public License Version 2.0 */ @@ -8,6 +8,7 @@ // Local include(s). #include "vecmem/edm/details/device_traits.hpp" +#include "vecmem/edm/proxy.hpp" #include "vecmem/edm/schema.hpp" #include "vecmem/edm/view.hpp" #include "vecmem/utils/tuple.hpp" @@ -19,8 +20,8 @@ namespace vecmem { namespace edm { -/// Technical base type for @c device> -template +/// Technical base type for @c device,INTERFACE> +template class I> class device; /// Structure-of-Arrays device container @@ -31,8 +32,8 @@ class device; /// /// @tparam ...VARTYPES The variable types stored in the container /// -template -class device> { +template class INTERFACE> +class device, INTERFACE> { // Sanity check(s). static_assert(sizeof...(VARTYPES) > 0, @@ -47,6 +48,17 @@ class device> { using size_pointer = typename view::size_pointer; /// The tuple type holding all of the the individual "device objects" using tuple_type = tuple::type...>; + /// The type of the interface used for the proxy objects + template + using interface_type = INTERFACE; + /// The type of the (non-const) proxy objects for the container elements + using proxy_type = + interface_type>; + /// The type of the (const) proxy objects for the container elements + using const_proxy_type = + interface_type>; /// @name Constructors and assignment operators /// @{ @@ -82,6 +94,40 @@ class device> { typename details::device_type_at::const_return_type get() const; + /// Get a proxy object for a specific variable (non-const) + /// + /// While also checking that the index is within bounds. + /// + /// @param index The index of the element to proxy + /// @return A proxy object for the element at the given index + /// + VECMEM_HOST_AND_DEVICE + proxy_type at(size_type index); + /// Get a proxy object for a specific variable (const) + /// + /// While also checking that the index is within bounds. + /// + /// @param index The index of the element to proxy + /// @return A proxy object for the element at the given index + /// + VECMEM_HOST_AND_DEVICE + const_proxy_type at(size_type index) const; + + /// Get a proxy object for a specific variable (non-const) + /// + /// @param index The index of the element to proxy + /// @return A proxy object for the element at the given index + /// + VECMEM_HOST_AND_DEVICE + proxy_type operator[](size_type index); + /// Get a proxy object for a specific variable (const) + /// + /// @param index The index of the element to proxy + /// @return A proxy object for the element at the given index + /// + VECMEM_HOST_AND_DEVICE + const_proxy_type operator[](size_type index) const; + /// @} /// @name Function(s) meant for internal use by other VecMem types diff --git a/core/include/vecmem/edm/host.hpp b/core/include/vecmem/edm/host.hpp index 6648a250..54fa63dd 100644 --- a/core/include/vecmem/edm/host.hpp +++ b/core/include/vecmem/edm/host.hpp @@ -34,6 +34,7 @@ class host; /// variables in the client code. /// /// @tparam ...VARTYPES The variable types to store in the host container +/// @tparam INTERFACE The interface type to use for the container('s proxies) /// template class INTERFACE> class host, INTERFACE> { diff --git a/core/include/vecmem/edm/impl/device.ipp b/core/include/vecmem/edm/impl/device.ipp index 07f2f409..983b8e9d 100644 --- a/core/include/vecmem/edm/impl/device.ipp +++ b/core/include/vecmem/edm/impl/device.ipp @@ -17,8 +17,8 @@ namespace vecmem { namespace edm { -template -VECMEM_HOST_AND_DEVICE device>::device( +template class INTERFACE> +VECMEM_HOST_AND_DEVICE device, INTERFACE>::device( const view& view) : m_capacity{view.capacity()}, m_size{details::device_size_pointer>::device( m_capacity, m_data, std::index_sequence_for{})); } -template -VECMEM_HOST_AND_DEVICE auto device>::size() const +template class INTERFACE> +VECMEM_HOST_AND_DEVICE auto device, INTERFACE>::size() const -> size_type { // Check that all variables have the correct capacities. @@ -41,9 +41,9 @@ VECMEM_HOST_AND_DEVICE auto device>::size() const return (m_size == nullptr ? m_capacity : *m_size); } -template -VECMEM_HOST_AND_DEVICE auto device>::capacity() const - -> size_type { +template class INTERFACE> +VECMEM_HOST_AND_DEVICE auto device, INTERFACE>::capacity() + const -> size_type { // Check that all variables have the correct capacities. assert(details::device_capacities_match( @@ -52,9 +52,9 @@ VECMEM_HOST_AND_DEVICE auto device>::capacity() const return m_capacity; } -template -VECMEM_HOST_AND_DEVICE auto device>::push_back_default() - -> size_type { +template class INTERFACE> +VECMEM_HOST_AND_DEVICE auto +device, INTERFACE>::push_back_default() -> size_type { // There must be no jagged vector variables for this to work. static_assert(!vecmem::details::disjunction< @@ -83,43 +83,86 @@ VECMEM_HOST_AND_DEVICE auto device>::push_back_default() return index; } -template +template class INTERFACE> template VECMEM_HOST_AND_DEVICE typename details::device_type_at::return_type - device>::get() { + device, INTERFACE>::get() { return details::device_get>>::get( vecmem::get(m_data)); } -template +template class INTERFACE> template VECMEM_HOST_AND_DEVICE typename details::device_type_at::const_return_type - device>::get() const { + device, INTERFACE>::get() const { return details::device_get>>::get( vecmem::get(m_data)); } -template -VECMEM_HOST_AND_DEVICE auto device>::variables() +template class INTERFACE> +VECMEM_HOST_AND_DEVICE + typename device, INTERFACE>::proxy_type + device, INTERFACE>::at(size_type index) { + + // Make sure that the index is within bounds. + assert(index < size()); + + // Use the unprotected function. + return this->operator[](index); +} + +template class INTERFACE> +VECMEM_HOST_AND_DEVICE + typename device, INTERFACE>::const_proxy_type + device, INTERFACE>::at(size_type index) const { + + // Make sure that the index is within bounds. + assert(index < size()); + + // Use the unprotected function. + return this->operator[](index); +} + +template class INTERFACE> +VECMEM_HOST_AND_DEVICE + typename device, INTERFACE>::proxy_type + device, INTERFACE>::operator[](size_type index) { + + // Create the proxy. + return proxy_type{*this, index}; +} + +template class INTERFACE> +VECMEM_HOST_AND_DEVICE + typename device, INTERFACE>::const_proxy_type + device, INTERFACE>::operator[](size_type index) const { + + // Create the proxy. + return const_proxy_type{*this, index}; +} + +template class INTERFACE> +VECMEM_HOST_AND_DEVICE auto device, INTERFACE>::variables() -> tuple_type& { return m_data; } -template -VECMEM_HOST_AND_DEVICE auto device>::variables() const - -> const tuple_type& { +template class INTERFACE> +VECMEM_HOST_AND_DEVICE auto device, INTERFACE>::variables() + const -> const tuple_type& { return m_data; } -template +template class INTERFACE> template -VECMEM_HOST_AND_DEVICE void device>::construct_default( +VECMEM_HOST_AND_DEVICE void +device, INTERFACE>::construct_default( size_type index, std::index_sequence) { // Construct the new element in this variable, if it's a vector. @@ -128,18 +171,20 @@ VECMEM_HOST_AND_DEVICE void device>::construct_default( construct_default(index, std::index_sequence{}); } -template -VECMEM_HOST_AND_DEVICE void device>::construct_default( +template class INTERFACE> +VECMEM_HOST_AND_DEVICE void +device, INTERFACE>::construct_default( size_type, std::index_sequence<>) {} -template +template class INTERFACE> template -VECMEM_HOST_AND_DEVICE void device>::construct_vector( - size_type, T&) {} +VECMEM_HOST_AND_DEVICE void +device, INTERFACE>::construct_vector(size_type, T&) {} -template +template class INTERFACE> template -VECMEM_HOST_AND_DEVICE void device>::construct_vector( +VECMEM_HOST_AND_DEVICE void +device, INTERFACE>::construct_vector( size_type index, device_vector& vec) { vec.construct(index, {}); diff --git a/core/include/vecmem/edm/impl/host.ipp b/core/include/vecmem/edm/impl/host.ipp index 881bbeae..fd8f566b 100644 --- a/core/include/vecmem/edm/impl/host.ipp +++ b/core/include/vecmem/edm/impl/host.ipp @@ -105,8 +105,8 @@ host, INTERFACE>::at(size_type index) { ") >= size (" + std::to_string(size()) + ")"); } - // Create the proxy. - return proxy_type{*this, index}; + // Use the unprotected function. + return this->operator[](index); } template class INTERFACE> @@ -119,8 +119,8 @@ host, INTERFACE>::at(size_type index) const { ") >= size (" + std::to_string(size()) + ")"); } - // Create the proxy. - return const_proxy_type{*this, index}; + // Use the unprotected function. + return this->operator[](index); } template class INTERFACE> diff --git a/core/include/vecmem/edm/proxy.hpp b/core/include/vecmem/edm/proxy.hpp index bfbe7166..c4b52ffb 100644 --- a/core/include/vecmem/edm/proxy.hpp +++ b/core/include/vecmem/edm/proxy.hpp @@ -24,6 +24,8 @@ class proxy; /// This class implements a "view" of a single element in an SoA container. /// /// @tparam ...VARTYPES The variable types to store in the proxy object +/// @tparam PTYPE The type of the proxy (host or device) +/// @tparam CTYPE The access mode of the proxy (const or non-const) /// template diff --git a/tests/common/simple_soa_container_helpers.hpp b/tests/common/simple_soa_container_helpers.hpp index a947fd5c..7b9c1982 100644 --- a/tests/common/simple_soa_container_helpers.hpp +++ b/tests/common/simple_soa_container_helpers.hpp @@ -28,7 +28,7 @@ inline void fill(unsigned int i, simple_soa_container::device& obj) { if (i < obj.capacity()) { unsigned int ii = obj.push_back_default(); obj.measurement()[ii] = 1.0f * static_cast(ii); - obj.index()[ii] = static_cast(ii); + obj.at(ii).index() = static_cast(ii); } } @@ -43,7 +43,7 @@ inline void modify(unsigned int i, simple_soa_container::device& obj) { } // In the rest of the threads modify the vector variables. if (i < obj.size()) { - obj.measurement()[i] *= 2.0f; + obj.at(i).measurement() *= 2.0f; obj.index()[i] += 10; } } diff --git a/tests/core/test_core_edm_buffer.cpp b/tests/core/test_core_edm_buffer.cpp index c20c1287..207d4584 100644 --- a/tests/core/test_core_edm_buffer.cpp +++ b/tests/core/test_core_edm_buffer.cpp @@ -42,6 +42,10 @@ class core_edm_buffer_test : public testing::Test { using jagged_const_schema = vecmem::edm::details::add_const_t; + /// Dummy container interface for the test + template + struct interface {}; + /// Memory resource for the test(s) vecmem::host_memory_resource m_resource; /// Copy object for the test(s) @@ -246,7 +250,7 @@ TEST_F(core_edm_buffer_test, device) { CAPACITY, m_resource, vecmem::data::buffer_type::fixed_size}; // Make a device container on top of it. - vecmem::edm::device device1{buffer1}; + vecmem::edm::device device1{buffer1}; ASSERT_EQ(device1.size(), CAPACITY); ASSERT_EQ(device1.capacity(), CAPACITY); auto check_fixed_vector = [&](const auto& v) { @@ -282,7 +286,7 @@ TEST_F(core_edm_buffer_test, device) { m_copy.memset(buffer2.size(), 0); // Make a device container on top of it. - vecmem::edm::device device2{buffer2}; + vecmem::edm::device device2{buffer2}; ASSERT_EQ(device2.size(), 0u); ASSERT_EQ(device2.capacity(), CAPACITY); auto check_resizable_vector = [&](const auto& v) { @@ -324,7 +328,7 @@ TEST_F(core_edm_buffer_test, device) { CAPACITIES, m_resource, nullptr, vecmem::data::buffer_type::fixed_size}; // Make a device container on top of it. - vecmem::edm::device device3{buffer3}; + vecmem::edm::device device3{buffer3}; ASSERT_EQ(device3.size(), CAPACITY); ASSERT_EQ(device3.capacity(), CAPACITY); auto check_fixed_jagged = [&](const auto& v) { @@ -365,7 +369,7 @@ TEST_F(core_edm_buffer_test, device) { m_copy.memset(buffer4.size(), 0); // Make a device container on top of it. - vecmem::edm::device device4{buffer4}; + vecmem::edm::device device4{buffer4}; ASSERT_EQ(device4.size(), CAPACITY); ASSERT_EQ(device4.capacity(), CAPACITY); auto check_resizable_jagged = [&](const auto& v) { diff --git a/tests/core/test_core_edm_device.cpp b/tests/core/test_core_edm_device.cpp index 7e126bd0..55477745 100644 --- a/tests/core/test_core_edm_device.cpp +++ b/tests/core/test_core_edm_device.cpp @@ -1,6 +1,6 @@ /* VecMem project, part of the ACTS project (R&D line) * - * (c) 2023 CERN for the benefit of the ACTS project + * (c) 2023-2024 CERN for the benefit of the ACTS project * * Mozilla Public License Version 2.0 */ @@ -14,6 +14,14 @@ // System include(s). #include +namespace { + +/// Dummy container interface for the test +template +struct interface {}; + +} // namespace + TEST(core_edm_device_test, construct) { using schema = @@ -25,9 +33,9 @@ TEST(core_edm_device_test, construct) { vecmem::edm::view view1{}; vecmem::edm::view view2{}; - vecmem::edm::device device1{view1}; - vecmem::edm::device device2{view1}; - vecmem::edm::device device3{view2}; + vecmem::edm::device device1{view1}; + vecmem::edm::device device2{view1}; + vecmem::edm::device device3{view2}; } TEST(core_edm_device_test, members) { @@ -44,8 +52,8 @@ TEST(core_edm_device_test, members) { view.get<0>() = &value1; view.get<1>() = {static_cast(value2.size()), value2.data()}; - vecmem::edm::device device1{view}; - vecmem::edm::device device2{view}; + vecmem::edm::device device1{view}; + vecmem::edm::device device2{view}; EXPECT_EQ(device2.get<0>(), value1); ASSERT_EQ(device2.get<1>().size(), value2.size()); diff --git a/tests/core/test_core_edm_host.cpp b/tests/core/test_core_edm_host.cpp index 2d6f3f23..cd244b8c 100644 --- a/tests/core/test_core_edm_host.cpp +++ b/tests/core/test_core_edm_host.cpp @@ -150,16 +150,16 @@ TEST_F(core_edm_host_test, device) { // Create a non-const device object for it, and check its contents. auto data1 = vecmem::get_data(host1); - vecmem::edm::device device1{data1}; + vecmem::edm::device device1{data1}; compare(device1, host1); // Create constant device objects for it, and check their contents. auto data2 = [](const vecmem::edm::host& host) { return vecmem::get_data(host); }(host1); - vecmem::edm::device device2{data2}; + vecmem::edm::device device2{data2}; compare(device2, host1); - vecmem::edm::device device3{data1}; + vecmem::edm::device device3{data1}; compare(device3, host1); } From 9095440ecd25f88bf9d646499dcc68584f1ef845 Mon Sep 17 00:00:00 2001 From: Attila Krasznahorkay Date: Wed, 25 Sep 2024 21:39:02 +0200 Subject: [PATCH 4/5] Tweaked the new code and its documentation a little. --- CMakeLists.txt | 2 + core/include/vecmem/edm/container.hpp | 2 +- .../vecmem/edm/details/proxy_traits.hpp | 325 +++++++++--------- core/include/vecmem/edm/impl/device.ipp | 2 +- core/include/vecmem/edm/impl/host.ipp | 2 +- core/include/vecmem/edm/impl/proxy.ipp | 12 +- 6 files changed, 183 insertions(+), 162 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97ac7f96..505b7f73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,8 @@ if( VECMEM_BUILD_DOCS ) "${CMAKE_CURRENT_SOURCE_DIR}/cuda/include" "${CMAKE_CURRENT_SOURCE_DIR}/hip/include" "${CMAKE_CURRENT_SOURCE_DIR}/sycl/include" ) + set( DOXYGEN_PREDEFINED + "__cplusplus=201700L" ) set( DOXYGEN_EXCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/tests" "${CMAKE_CURRENT_SOURCE_DIR}/benchmarks" diff --git a/core/include/vecmem/edm/container.hpp b/core/include/vecmem/edm/container.hpp index 1d8ec69c..7f8e494d 100644 --- a/core/include/vecmem/edm/container.hpp +++ b/core/include/vecmem/edm/container.hpp @@ -1,6 +1,6 @@ /* VecMem project, part of the ACTS project (R&D line) * - * (c) 2023 CERN for the benefit of the ACTS project + * (c) 2023-2024 CERN for the benefit of the ACTS project * * Mozilla Public License Version 2.0 */ diff --git a/core/include/vecmem/edm/details/proxy_traits.hpp b/core/include/vecmem/edm/details/proxy_traits.hpp index d04ebc63..e8376839 100644 --- a/core/include/vecmem/edm/details/proxy_traits.hpp +++ b/core/include/vecmem/edm/details/proxy_traits.hpp @@ -86,11 +86,9 @@ struct proxy_var_type, PTYPE, proxy_access::non_constant> { } }; -#if __cplusplus >= 201700L - -/// Constant access to a vector variable from a host container +/// Constant access to a vector variable from a device container template -struct proxy_var_type, proxy_type::host, +struct proxy_var_type, proxy_type::device, proxy_access::constant> { /// Vector elements are kept by value in the proxy @@ -101,16 +99,18 @@ struct proxy_var_type, proxy_type::host, using const_return_type = return_type; /// Helper function constructing a vector proxy variable - VECMEM_HOST - static return_type make(typename vector::size_type i, - const vector& vec) { + VECMEM_HOST_AND_DEVICE + static return_type make( + typename device_vector>::size_type i, + const device_vector>& vec) { + return vec.at(i); } }; -/// Non-const access to a vector variable from a host container +/// Non-const access to a vector variable from a device container template -struct proxy_var_type, proxy_type::host, +struct proxy_var_type, proxy_type::device, proxy_access::non_constant> { /// Vector elements are kept by lvalue reference in the proxy @@ -122,54 +122,57 @@ struct proxy_var_type, proxy_type::host, std::add_lvalue_reference_t>; /// Helper function constructing a vector proxy variable - VECMEM_HOST - static return_type make(typename vector::size_type i, - vector& vec) { + VECMEM_HOST_AND_DEVICE + static return_type make(typename device_vector::size_type i, + device_vector& vec) { + return vec.at(i); } }; -#endif // __cplusplus >= 201700L - -/// Constant access to a vector variable from a device container +/// Constant access to a jagged vector variable from a device container template -struct proxy_var_type, proxy_type::device, +struct proxy_var_type, proxy_type::device, proxy_access::constant> { - /// Vector elements are kept by value in the proxy - using type = VTYPE; - /// They are returned as a const reference even on non-const access + /// Jagged vector elements are kept by constant device vectors in the proxy + using type = device_vector>; + /// They are returned as a const reference to the device vector even in + /// non-const access using return_type = std::add_lvalue_reference_t>; - /// They are returned as a const reference on const access + /// They are returned as a const reference to the device vector on const + /// access using const_return_type = return_type; /// Helper function constructing a vector proxy variable VECMEM_HOST_AND_DEVICE static return_type make( - typename device_vector>::size_type i, - const device_vector>& vec) { + typename jagged_device_vector>::size_type i, + const jagged_device_vector>& vec) { return vec.at(i); } }; -/// Non-const access to a vector variable from a device container +/// Non-const access to a jagged vector variable from a device container template -struct proxy_var_type, proxy_type::device, +struct proxy_var_type, proxy_type::device, proxy_access::non_constant> { - /// Vector elements are kept by lvalue reference in the proxy - using type = std::add_lvalue_reference_t; - /// They are returned as a non-const lvalue reference on non-const access - using return_type = type; - /// They are returned as a const reference on const access - using const_return_type = - std::add_lvalue_reference_t>; + /// Jagged vector elements are kept by non-const device vectors in the proxy + using type = device_vector; + /// They are returned as non-const lvalue references to the non-const device + /// vector in non-const access + using return_type = std::add_lvalue_reference_t; + /// They are returned as const references to the non-const device vector in + /// const access + using const_return_type = std::add_lvalue_reference_t< + std::add_const_t>>>; /// Helper function constructing a vector proxy variable VECMEM_HOST_AND_DEVICE - static return_type make(typename device_vector::size_type i, - device_vector& vec) { + static return_type make(typename jagged_device_vector::size_type i, + jagged_device_vector& vec) { return vec.at(i); } @@ -177,100 +180,104 @@ struct proxy_var_type, proxy_type::device, #if __cplusplus >= 201700L -/// Constant access to a jagged vector variable from a host container +/// Constant access to a vector variable from a host container template -struct proxy_var_type, proxy_type::host, +struct proxy_var_type, proxy_type::host, proxy_access::constant> { - /// Jagged vector elements are kept by constant reference in the proxy - using type = std::add_lvalue_reference_t>>; + /// Vector elements are kept by value in the proxy + using type = + typename proxy_var_type, proxy_type::device, + proxy_access::constant>::type; /// They are returned as a const reference even on non-const access - using return_type = type; + using return_type = + typename proxy_var_type, proxy_type::device, + proxy_access::constant>::return_type; /// They are returned as a const reference on const access - using const_return_type = type; + using const_return_type = + typename proxy_var_type, proxy_type::device, + proxy_access::constant>::const_return_type; /// Helper function constructing a vector proxy variable VECMEM_HOST - static return_type make(typename jagged_vector::size_type i, - const jagged_vector& vec) { - + static return_type make(typename vector::size_type i, + const vector& vec) { return vec.at(i); } }; -/// Non-const access to a jagged vector variable from a host container +/// Non-const access to a vector variable from a host container template -struct proxy_var_type, proxy_type::host, +struct proxy_var_type, proxy_type::host, proxy_access::non_constant> { - /// Jagged vector elements are kept by non-const lvalue reference in the - /// proxy - using type = std::add_lvalue_reference_t>; + /// Vector elements are kept by lvalue reference in the proxy + using type = + typename proxy_var_type, proxy_type::device, + proxy_access::non_constant>::type; /// They are returned as a non-const lvalue reference on non-const access - using return_type = type; + using return_type = + typename proxy_var_type, proxy_type::device, + proxy_access::non_constant>::return_type; /// They are returned as a const reference on const access using const_return_type = - std::add_lvalue_reference_t>>; + typename proxy_var_type, proxy_type::device, + proxy_access::non_constant>::const_return_type; /// Helper function constructing a vector proxy variable VECMEM_HOST - static return_type make(typename jagged_vector::size_type i, - jagged_vector& vec) { - + static return_type make(typename vector::size_type i, + vector& vec) { return vec.at(i); } }; -#endif // __cplusplus >= 201700L - -/// Constant access to a jagged vector variable from a device container +/// Constant access to a jagged vector variable from a host container template -struct proxy_var_type, proxy_type::device, +struct proxy_var_type, proxy_type::host, proxy_access::constant> { - /// Jagged vector elements are kept by constant device vectors in the proxy - using type = device_vector>; - /// They are returned as a const reference to the device vector even in - /// non-const access - using return_type = std::add_lvalue_reference_t>; - /// They are returned as a const reference to the device vector on const - /// access - using const_return_type = return_type; + /// Jagged vector elements are kept by constant reference in the proxy + using type = std::add_lvalue_reference_t>>; + /// They are returned as a const reference even on non-const access + using return_type = type; + /// They are returned as a const reference on const access + using const_return_type = type; /// Helper function constructing a vector proxy variable - VECMEM_HOST_AND_DEVICE - static return_type make( - typename jagged_device_vector>::size_type i, - const jagged_device_vector>& vec) { + VECMEM_HOST + static return_type make(typename jagged_vector::size_type i, + const jagged_vector& vec) { return vec.at(i); } }; -/// Non-const access to a jagged vector variable from a device container +/// Non-const access to a jagged vector variable from a host container template -struct proxy_var_type, proxy_type::device, +struct proxy_var_type, proxy_type::host, proxy_access::non_constant> { - /// Jagged vector elements are kept by non-const device vectors in the proxy - using type = device_vector; - /// They are returned as non-const lvalue references to the non-const device - /// vector in non-const access - using return_type = std::add_lvalue_reference_t; - /// They are returned as const references to the non-const device vector in - /// const access + /// Jagged vector elements are kept by non-const lvalue reference in the + /// proxy + using type = std::add_lvalue_reference_t>; + /// They are returned as a non-const lvalue reference on non-const access + using return_type = type; + /// They are returned as a const reference on const access using const_return_type = - std::add_lvalue_reference_t>; + std::add_lvalue_reference_t>>; /// Helper function constructing a vector proxy variable - VECMEM_HOST_AND_DEVICE - static type make(typename jagged_device_vector::size_type i, - jagged_device_vector& vec) { + VECMEM_HOST + static return_type make(typename jagged_vector::size_type i, + jagged_vector& vec) { return vec.at(i); } }; +#endif // __cplusplus >= 201700L + /// Proxy types for one element of a type pack template @@ -291,17 +298,16 @@ struct proxy_var_type_at { /// @} -/// @name Traits for creating the proxy variables +/// @name Traits for creating the proxy data tuples /// @{ /// Technical base class for the @c proxy_data_creator traits template struct proxy_data_creator; -#if __cplusplus >= 201700L - +/// Helper class making the data tuple for a constant device proxy template -struct proxy_data_creator, proxy_type::host, +struct proxy_data_creator, proxy_type::device, proxy_access::constant> { /// Make all other instantiations of the struct friends @@ -310,29 +316,30 @@ struct proxy_data_creator, proxy_type::host, /// Proxy tuple type created by the helper using proxy_tuple_type = - tuple::type>; /// Construct the tuple used by the proxy template - VECMEM_HOST static proxy_tuple_type make(std::size_t i, - const CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int i, + const CONTAINER& c) { return make_impl<0>(i, c); } private: template - VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, - const CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl( + unsigned int i, const CONTAINER& c) { - return { - proxy_var_type:: - make(i, c.template get())}; + return {proxy_var_type< + VARTYPE, proxy_type::device, + proxy_access::constant>::make(i, c.template get())}; } }; +/// Helper class making the data tuple for a non-const device proxy template -struct proxy_data_creator, proxy_type::host, +struct proxy_data_creator, proxy_type::device, proxy_access::non_constant> { /// Make all other instantiations of the struct friends @@ -341,29 +348,30 @@ struct proxy_data_creator, proxy_type::host, /// Proxy tuple type created by the helper using proxy_tuple_type = - tuple::type>; /// Construct the tuple used by the proxy template - VECMEM_HOST static proxy_tuple_type make(std::size_t i, CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int i, + CONTAINER& c) { return make_impl<0>(i, c); } private: template - VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl(unsigned int i, + CONTAINER& c) { return {proxy_var_type< - VARTYPE, proxy_type::host, + VARTYPE, proxy_type::device, proxy_access::non_constant>::make(i, c.template get())}; } }; -#endif // __cplusplus >= 201700L - -template -struct proxy_data_creator, proxy_type::device, +/// Helper class making the data tuple for a constant device proxy +template +struct proxy_data_creator, proxy_type::device, proxy_access::constant> { /// Make all other instantiations of the struct friends @@ -373,7 +381,9 @@ struct proxy_data_creator, proxy_type::device, /// Proxy tuple type created by the helper using proxy_tuple_type = tuple::type>; + proxy_access::constant>::type, + typename proxy_var_type::type...>; /// Construct the tuple used by the proxy template @@ -387,14 +397,19 @@ struct proxy_data_creator, proxy_type::device, VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl( unsigned int i, const CONTAINER& c) { - return {proxy_var_type< - VARTYPE, proxy_type::device, - proxy_access::constant>::make(i, c.template get())}; + return proxy_tuple_type( + proxy_var_type< + VARTYPE, proxy_type::device, + proxy_access::constant>::make(i, c.template get()), + proxy_data_creator< + schema, proxy_type::device, + proxy_access::constant>::template make_impl(i, c)); } }; -template -struct proxy_data_creator, proxy_type::device, +/// Helper class making the data tuple for a non-const device proxy +template +struct proxy_data_creator, proxy_type::device, proxy_access::non_constant> { /// Make all other instantiations of the struct friends @@ -404,7 +419,9 @@ struct proxy_data_creator, proxy_type::device, /// Proxy tuple type created by the helper using proxy_tuple_type = tuple::type>; + proxy_access::non_constant>::type, + typename proxy_var_type::type...>; /// Construct the tuple used by the proxy template @@ -418,16 +435,22 @@ struct proxy_data_creator, proxy_type::device, VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl(unsigned int i, CONTAINER& c) { - return {proxy_var_type< - VARTYPE, proxy_type::device, - proxy_access::non_constant>::make(i, c.template get())}; + return proxy_tuple_type( + proxy_var_type< + VARTYPE, proxy_type::device, + proxy_access::non_constant>::make(i, c.template get()), + proxy_data_creator< + schema, proxy_type::device, + proxy_access::non_constant>::template make_impl(i, + c)); } }; #if __cplusplus >= 201700L -template -struct proxy_data_creator, proxy_type::host, +/// Helper class making the data tuple for a constant host proxy +template +struct proxy_data_creator, proxy_type::host, proxy_access::constant> { /// Make all other instantiations of the struct friends @@ -437,9 +460,7 @@ struct proxy_data_creator, proxy_type::host, /// Proxy tuple type created by the helper using proxy_tuple_type = tuple::type, - typename proxy_var_type::type...>; + proxy_access::constant>::type>; /// Construct the tuple used by the proxy template @@ -453,17 +474,15 @@ struct proxy_data_creator, proxy_type::host, VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, const CONTAINER& c) { - return proxy_tuple_type( + return { proxy_var_type:: - make(i, c.template get()), - proxy_data_creator< - schema, proxy_type::host, - proxy_access::constant>::template make_impl(i, c)); + make(i, c.template get())}; } }; -template -struct proxy_data_creator, proxy_type::host, +/// Helper class making the data tuple for a non-const host proxy +template +struct proxy_data_creator, proxy_type::host, proxy_access::non_constant> { /// Make all other instantiations of the struct friends @@ -473,9 +492,7 @@ struct proxy_data_creator, proxy_type::host, /// Proxy tuple type created by the helper using proxy_tuple_type = tuple::type, - typename proxy_var_type::type...>; + proxy_access::non_constant>::type>; /// Construct the tuple used by the proxy template @@ -487,21 +504,15 @@ struct proxy_data_creator, proxy_type::host, template VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { - return proxy_tuple_type( - proxy_var_type< - VARTYPE, proxy_type::host, - proxy_access::non_constant>::make(i, c.template get()), - proxy_data_creator< - schema, proxy_type::host, - proxy_access::non_constant>::template make_impl(i, - c)); + return {proxy_var_type< + VARTYPE, proxy_type::host, + proxy_access::non_constant>::make(i, c.template get())}; } }; -#endif // __cplusplus >= 201700L - +/// Helper class making the data tuple for a constant host proxy template -struct proxy_data_creator, proxy_type::device, +struct proxy_data_creator, proxy_type::host, proxy_access::constant> { /// Make all other instantiations of the struct friends @@ -510,35 +521,35 @@ struct proxy_data_creator, proxy_type::device, /// Proxy tuple type created by the helper using proxy_tuple_type = - tuple::type, - typename proxy_var_type::type...>; /// Construct the tuple used by the proxy template - VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int i, - const CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make(std::size_t i, + const CONTAINER& c) { return make_impl<0>(i, c); } private: template - VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl( - unsigned int i, const CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, + const CONTAINER& c) { return proxy_tuple_type( - proxy_var_type< - VARTYPE, proxy_type::device, - proxy_access::constant>::make(i, c.template get()), + proxy_var_type:: + make(i, c.template get()), proxy_data_creator< - schema, proxy_type::device, + schema, proxy_type::host, proxy_access::constant>::template make_impl(i, c)); } }; +/// Helper class making the data tuple for a non-const host proxy template -struct proxy_data_creator, proxy_type::device, +struct proxy_data_creator, proxy_type::host, proxy_access::non_constant> { /// Make all other instantiations of the struct friends @@ -547,34 +558,34 @@ struct proxy_data_creator, proxy_type::device, /// Proxy tuple type created by the helper using proxy_tuple_type = - tuple::type, - typename proxy_var_type::type...>; /// Construct the tuple used by the proxy template - VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int i, - CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make(std::size_t i, CONTAINER& c) { return make_impl<0>(i, c); } private: template - VECMEM_HOST_AND_DEVICE static proxy_tuple_type make_impl(unsigned int i, - CONTAINER& c) { + VECMEM_HOST static proxy_tuple_type make_impl(std::size_t i, CONTAINER& c) { return proxy_tuple_type( proxy_var_type< - VARTYPE, proxy_type::device, + VARTYPE, proxy_type::host, proxy_access::non_constant>::make(i, c.template get()), proxy_data_creator< - schema, proxy_type::device, + schema, proxy_type::host, proxy_access::non_constant>::template make_impl(i, c)); } }; +#endif // __cplusplus >= 201700L + /// @} } // namespace details diff --git a/core/include/vecmem/edm/impl/device.ipp b/core/include/vecmem/edm/impl/device.ipp index 983b8e9d..5fe4b48f 100644 --- a/core/include/vecmem/edm/impl/device.ipp +++ b/core/include/vecmem/edm/impl/device.ipp @@ -1,6 +1,6 @@ /* VecMem project, part of the ACTS project (R&D line) * - * (c) 2023 CERN for the benefit of the ACTS project + * (c) 2023-2024 CERN for the benefit of the ACTS project * * Mozilla Public License Version 2.0 */ diff --git a/core/include/vecmem/edm/impl/host.ipp b/core/include/vecmem/edm/impl/host.ipp index fd8f566b..da1e18cf 100644 --- a/core/include/vecmem/edm/impl/host.ipp +++ b/core/include/vecmem/edm/impl/host.ipp @@ -1,6 +1,6 @@ /* VecMem project, part of the ACTS project (R&D line) * - * (c) 2023 CERN for the benefit of the ACTS project + * (c) 2023-2024 CERN for the benefit of the ACTS project * * Mozilla Public License Version 2.0 */ diff --git a/core/include/vecmem/edm/impl/proxy.ipp b/core/include/vecmem/edm/impl/proxy.ipp index 41415568..64c8e849 100644 --- a/core/include/vecmem/edm/impl/proxy.ipp +++ b/core/include/vecmem/edm/impl/proxy.ipp @@ -16,7 +16,11 @@ VECMEM_HOST_AND_DEVICE proxy, PTYPE, CTYPE>::proxy( PARENT& parent, typename PARENT::size_type index) : m_data{ details::proxy_data_creator, PTYPE, CTYPE>::make( - index, parent)} {} + index, parent)} { + + static_assert(CTYPE == details::proxy_access::non_constant, + "This constructor is meant for non-const proxies."); +} template @@ -25,7 +29,11 @@ VECMEM_HOST_AND_DEVICE proxy, PTYPE, CTYPE>::proxy( const PARENT& parent, typename PARENT::size_type index) : m_data{ details::proxy_data_creator, PTYPE, CTYPE>::make( - index, parent)} {} + index, parent)} { + + static_assert(CTYPE == details::proxy_access::constant, + "This constructor is meant for constant proxies."); +} template From 78c80c15cda7e21a2f1feb89fa9975f2389e5872 Mon Sep 17 00:00:00 2001 From: Attila Krasznahorkay Date: Thu, 26 Sep 2024 17:31:37 +0200 Subject: [PATCH 5/5] Switched to references with the const proxies. As feared, this premature/misguided optimization was not helping with code performance in real life. --- core/include/vecmem/edm/details/proxy_traits.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/include/vecmem/edm/details/proxy_traits.hpp b/core/include/vecmem/edm/details/proxy_traits.hpp index e8376839..7896000e 100644 --- a/core/include/vecmem/edm/details/proxy_traits.hpp +++ b/core/include/vecmem/edm/details/proxy_traits.hpp @@ -52,9 +52,9 @@ template struct proxy_var_type, PTYPE, proxy_access::constant> { /// The scalar is kept by value in the proxy - using type = VTYPE; + using type = std::add_lvalue_reference_t>; /// It is returned as a const reference even on non-const access - using return_type = std::add_lvalue_reference_t>; + using return_type = type; /// It is returned as a const reference on const access using const_return_type = return_type; @@ -92,9 +92,9 @@ struct proxy_var_type, proxy_type::device, proxy_access::constant> { /// Vector elements are kept by value in the proxy - using type = VTYPE; + using type = std::add_lvalue_reference_t>; /// They are returned as a const reference even on non-const access - using return_type = std::add_lvalue_reference_t>; + using return_type = type; /// They are returned as a const reference on const access using const_return_type = return_type;