From b247389f49025aafa0fa527600f4fd53372a8129 Mon Sep 17 00:00:00 2001 From: Attila Krasznahorkay <30694331+krasznaa@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:49:40 +0200 Subject: [PATCH] SoA Proxy, main branch (2024.09.25.) (#296) * 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. * 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. * 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. * Tweaked the new code and its documentation a little. * Switched to references with the const proxies. As feared, this premature/misguided optimization was not helping with code performance in real life. --- CMakeLists.txt | 2 + .../vecmem/containers/device_vector.hpp | 4 +- core/include/vecmem/edm/container.hpp | 9 +- .../vecmem/edm/details/proxy_traits.hpp | 593 ++++++++++++++++++ core/include/vecmem/edm/device.hpp | 56 +- core/include/vecmem/edm/host.hpp | 65 +- core/include/vecmem/edm/impl/device.ipp | 103 ++- core/include/vecmem/edm/impl/host.ipp | 123 ++-- core/include/vecmem/edm/impl/proxy.ipp | 61 ++ core/include/vecmem/edm/proxy.hpp | 99 +++ core/include/vecmem/utils/copy.hpp | 10 +- core/include/vecmem/utils/impl/copy.ipp | 10 +- core/include/vecmem/utils/tuple.hpp | 16 + 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 | 98 ++- 17 files changed, 1163 insertions(+), 122 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/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/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 2577f117..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 */ @@ -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,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 >; + 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..7896000e --- /dev/null +++ b/core/include/vecmem/edm/details/proxy_traits.hpp @@ -0,0 +1,593 @@ +/* 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/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 +#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 = std::add_lvalue_reference_t>; + /// It is returned as a const reference even on non-const access + using return_type = type; + /// It is returned as a const reference on const access + using const_return_type = return_type; + + /// Helper function constructing a scalar proxy variable + template + VECMEM_HOST_AND_DEVICE 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 + VECMEM_HOST_AND_DEVICE static return_type make(ITYPE, + return_type variable) { + return variable; + } +}; + +/// 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 = 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 = 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) { + + 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 = 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 + VECMEM_HOST_AND_DEVICE + 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 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 + VECMEM_HOST_AND_DEVICE + 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< + std::add_const_t>>>; + + /// Helper function constructing a vector proxy variable + VECMEM_HOST_AND_DEVICE + static return_type make(typename jagged_device_vector::size_type i, + jagged_device_vector& vec) { + + return vec.at(i); + } +}; + +#if __cplusplus >= 201700L + +/// 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 = + 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 = + 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 = + 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 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 = + 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 = + 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 = + 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 vector::size_type i, + 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 + 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 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 + 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 +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 data tuples +/// @{ + +/// Technical base class for the @c proxy_data_creator traits +template +struct proxy_data_creator; + +/// 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 + 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 + 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_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())}; + } +}; + +/// 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 + 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 + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int 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) { + + return {proxy_var_type< + VARTYPE, proxy_type::device, + proxy_access::non_constant>::make(i, c.template get())}; + } +}; + +/// 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 + 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 + 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_AND_DEVICE 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)); + } +}; + +/// 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 + 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 + VECMEM_HOST_AND_DEVICE static proxy_tuple_type make(unsigned int 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) { + + 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 + +/// 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 + 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 + VECMEM_HOST static proxy_tuple_type make(std::size_t 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) { + + return { + proxy_var_type:: + make(i, c.template get())}; + } +}; + +/// 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 + 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 + VECMEM_HOST static proxy_tuple_type make(std::size_t 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) { + + return {proxy_var_type< + VARTYPE, proxy_type::host, + proxy_access::non_constant>::make(i, c.template get())}; + } +}; + +/// 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 + 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 + VECMEM_HOST static proxy_tuple_type make(std::size_t 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) { + + 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)); + } +}; + +/// 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 + 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 + VECMEM_HOST static proxy_tuple_type make(std::size_t 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) { + + 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)); + } +}; + +#endif // __cplusplus >= 201700L + +/// @} + +} // namespace details +} // namespace edm +} // namespace vecmem 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 44ab1e47..54fa63dd 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 @@ -33,9 +34,10 @@ 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 host> { +template class INTERFACE> +class host, INTERFACE> { public: /// The schema describing the host's payload @@ -45,6 +47,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 +91,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 +160,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 +172,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/device.ipp b/core/include/vecmem/edm/impl/device.ipp index 07f2f409..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 */ @@ -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 e3c38e97..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 */ @@ -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()) + ")"); + } + + // Use the unprotected function. + return this->operator[](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()) + ")"); + } + + // Use the unprotected function. + return this->operator[](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..64c8e849 --- /dev/null +++ b/core/include/vecmem/edm/impl/proxy.ipp @@ -0,0 +1,61 @@ +/* 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)} { + + static_assert(CTYPE == details::proxy_access::non_constant, + "This constructor is meant for non-const proxies."); +} + +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)} { + + static_assert(CTYPE == details::proxy_access::constant, + "This constructor is meant for constant proxies."); +} + +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..c4b52ffb --- /dev/null +++ b/core/include/vecmem/edm/proxy.hpp @@ -0,0 +1,99 @@ +/* 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 +/// @tparam PTYPE The type of the proxy (host or device) +/// @tparam CTYPE The access mode of the proxy (const or non-const) +/// +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/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 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 1da56471..cd244b8c 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) { @@ -137,15 +150,66 @@ 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) { + 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); } + +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]); + } + } +}