149e08aacStbbdev /* 2*c21e688aSSergey Zheltov Copyright (c) 2005-2022 Intel Corporation 349e08aacStbbdev 449e08aacStbbdev Licensed under the Apache License, Version 2.0 (the "License"); 549e08aacStbbdev you may not use this file except in compliance with the License. 649e08aacStbbdev You may obtain a copy of the License at 749e08aacStbbdev 849e08aacStbbdev http://www.apache.org/licenses/LICENSE-2.0 949e08aacStbbdev 1049e08aacStbbdev Unless required by applicable law or agreed to in writing, software 1149e08aacStbbdev distributed under the License is distributed on an "AS IS" BASIS, 1249e08aacStbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1349e08aacStbbdev See the License for the specific language governing permissions and 1449e08aacStbbdev limitations under the License. 1549e08aacStbbdev */ 1649e08aacStbbdev 1749e08aacStbbdev #ifndef __TBB_cache_aligned_allocator_H 1849e08aacStbbdev #define __TBB_cache_aligned_allocator_H 1949e08aacStbbdev 2049e08aacStbbdev #include "detail/_utils.h" 2149e08aacStbbdev #include "detail/_namespace_injection.h" 2249e08aacStbbdev #include <cstdlib> 2349e08aacStbbdev #include <utility> 2449e08aacStbbdev 2549e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 2649e08aacStbbdev #include <memory_resource> 2749e08aacStbbdev #endif 2849e08aacStbbdev 2949e08aacStbbdev namespace tbb { 3049e08aacStbbdev namespace detail { 3149e08aacStbbdev 3249e08aacStbbdev namespace r1 { 338827ea7dSLong Nguyen TBB_EXPORT void* __TBB_EXPORTED_FUNC cache_aligned_allocate(std::size_t size); 348827ea7dSLong Nguyen TBB_EXPORT void __TBB_EXPORTED_FUNC cache_aligned_deallocate(void* p); 358827ea7dSLong Nguyen TBB_EXPORT std::size_t __TBB_EXPORTED_FUNC cache_line_size(); 3649e08aacStbbdev } 3749e08aacStbbdev 3849e08aacStbbdev namespace d1 { 3949e08aacStbbdev 4049e08aacStbbdev template<typename T> 4149e08aacStbbdev class cache_aligned_allocator { 4249e08aacStbbdev public: 4349e08aacStbbdev using value_type = T; 4449e08aacStbbdev using propagate_on_container_move_assignment = std::true_type; 4549e08aacStbbdev 4649e08aacStbbdev //! Always defined for TBB containers (supported since C++17 for std containers) 4749e08aacStbbdev using is_always_equal = std::true_type; 4849e08aacStbbdev 4949e08aacStbbdev cache_aligned_allocator() = default; cache_aligned_allocator(const cache_aligned_allocator<U> &)5049e08aacStbbdev template<typename U> cache_aligned_allocator(const cache_aligned_allocator<U>&) noexcept {} 5149e08aacStbbdev 5249e08aacStbbdev //! Allocate space for n objects, starting on a cache/sector line. allocate(std::size_t n)53b15aabb3Stbbdev __TBB_nodiscard T* allocate(std::size_t n) { 5449e08aacStbbdev return static_cast<T*>(r1::cache_aligned_allocate(n * sizeof(value_type))); 5549e08aacStbbdev } 5649e08aacStbbdev 5749e08aacStbbdev //! Free block of memory that starts on a cache line deallocate(T * p,std::size_t)5849e08aacStbbdev void deallocate(T* p, std::size_t) { 5949e08aacStbbdev r1::cache_aligned_deallocate(p); 6049e08aacStbbdev } 6149e08aacStbbdev 6249e08aacStbbdev //! Largest value for which method allocate might succeed. max_size()6349e08aacStbbdev std::size_t max_size() const noexcept { 6449e08aacStbbdev return (~std::size_t(0) - r1::cache_line_size()) / sizeof(value_type); 6549e08aacStbbdev } 6649e08aacStbbdev 6749e08aacStbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN 6849e08aacStbbdev using pointer = value_type*; 6949e08aacStbbdev using const_pointer = const value_type*; 7049e08aacStbbdev using reference = value_type&; 7149e08aacStbbdev using const_reference = const value_type&; 7249e08aacStbbdev using difference_type = std::ptrdiff_t; 7349e08aacStbbdev using size_type = std::size_t; 7449e08aacStbbdev template<typename U> struct rebind { 7549e08aacStbbdev using other = cache_aligned_allocator<U>; 7649e08aacStbbdev }; 7749e08aacStbbdev template<typename U, typename... Args> construct(U * p,Args &&...args)7849e08aacStbbdev void construct(U *p, Args&&... args) 7949e08aacStbbdev { ::new (p) U(std::forward<Args>(args)...); } destroy(pointer p)8049e08aacStbbdev void destroy(pointer p) { p->~value_type(); } address(reference x)8149e08aacStbbdev pointer address(reference x) const { return &x; } address(const_reference x)8249e08aacStbbdev const_pointer address(const_reference x) const { return &x; } 8349e08aacStbbdev #endif // TBB_ALLOCATOR_TRAITS_BROKEN 8449e08aacStbbdev }; 8549e08aacStbbdev 8649e08aacStbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN 8749e08aacStbbdev template<> 8849e08aacStbbdev class cache_aligned_allocator<void> { 8949e08aacStbbdev public: 9049e08aacStbbdev using pointer = void*; 9149e08aacStbbdev using const_pointer = const void*; 9249e08aacStbbdev using value_type = void; 9349e08aacStbbdev template<typename U> struct rebind { 9449e08aacStbbdev using other = cache_aligned_allocator<U>; 9549e08aacStbbdev }; 9649e08aacStbbdev }; 9749e08aacStbbdev #endif 9849e08aacStbbdev 9949e08aacStbbdev template<typename T, typename U> 10049e08aacStbbdev bool operator==(const cache_aligned_allocator<T>&, const cache_aligned_allocator<U>&) noexcept { return true; } 10149e08aacStbbdev 102b15aabb3Stbbdev #if !__TBB_CPP20_COMPARISONS_PRESENT 10349e08aacStbbdev template<typename T, typename U> 10449e08aacStbbdev bool operator!=(const cache_aligned_allocator<T>&, const cache_aligned_allocator<U>&) noexcept { return false; } 105b15aabb3Stbbdev #endif 10649e08aacStbbdev 10749e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 10849e08aacStbbdev 10949e08aacStbbdev //! C++17 memory resource wrapper to ensure cache line size alignment 11049e08aacStbbdev class cache_aligned_resource : public std::pmr::memory_resource { 11149e08aacStbbdev public: cache_aligned_resource()11249e08aacStbbdev cache_aligned_resource() : cache_aligned_resource(std::pmr::get_default_resource()) {} cache_aligned_resource(std::pmr::memory_resource * upstream)11349e08aacStbbdev explicit cache_aligned_resource(std::pmr::memory_resource* upstream) : m_upstream(upstream) {} 11449e08aacStbbdev upstream_resource()11549e08aacStbbdev std::pmr::memory_resource* upstream_resource() const { 11649e08aacStbbdev return m_upstream; 11749e08aacStbbdev } 11849e08aacStbbdev 11949e08aacStbbdev private: 12049e08aacStbbdev //! We don't know what memory resource set. Use padding to guarantee alignment do_allocate(std::size_t bytes,std::size_t alignment)12149e08aacStbbdev void* do_allocate(std::size_t bytes, std::size_t alignment) override { 12249e08aacStbbdev // TODO: make it common with tbb_allocator.cpp 12349e08aacStbbdev std::size_t cache_line_alignment = correct_alignment(alignment); 12449e08aacStbbdev std::size_t space = correct_size(bytes) + cache_line_alignment; 12549e08aacStbbdev std::uintptr_t base = reinterpret_cast<std::uintptr_t>(m_upstream->allocate(space)); 12657f524caSIlya Isaev __TBB_ASSERT(base != 0, "Upstream resource returned nullptr."); 12749e08aacStbbdev 12849e08aacStbbdev // Round up to the next cache line (align the base address) 12949e08aacStbbdev std::uintptr_t result = (base + cache_line_alignment) & ~(cache_line_alignment - 1); 13049e08aacStbbdev __TBB_ASSERT((result - base) >= sizeof(std::uintptr_t), "Can`t store a base pointer to the header"); 13149e08aacStbbdev __TBB_ASSERT(space - (result - base) >= bytes, "Not enough space for the storage"); 13249e08aacStbbdev 13349e08aacStbbdev // Record where block actually starts. 13449e08aacStbbdev (reinterpret_cast<std::uintptr_t*>(result))[-1] = base; 13549e08aacStbbdev return reinterpret_cast<void*>(result); 13649e08aacStbbdev } 13749e08aacStbbdev do_deallocate(void * ptr,std::size_t bytes,std::size_t alignment)13849e08aacStbbdev void do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) override { 13949e08aacStbbdev if (ptr) { 14049e08aacStbbdev // Recover where block actually starts 14149e08aacStbbdev std::uintptr_t base = (reinterpret_cast<std::uintptr_t*>(ptr))[-1]; 14249e08aacStbbdev m_upstream->deallocate(reinterpret_cast<void*>(base), correct_size(bytes) + correct_alignment(alignment)); 14349e08aacStbbdev } 14449e08aacStbbdev } 14549e08aacStbbdev do_is_equal(const std::pmr::memory_resource & other)14649e08aacStbbdev bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { 14749e08aacStbbdev if (this == &other) { return true; } 14849e08aacStbbdev #if __TBB_USE_OPTIONAL_RTTI 14949e08aacStbbdev const cache_aligned_resource* other_res = dynamic_cast<const cache_aligned_resource*>(&other); 15049e08aacStbbdev return other_res && (upstream_resource() == other_res->upstream_resource()); 15149e08aacStbbdev #else 15249e08aacStbbdev return false; 15349e08aacStbbdev #endif 15449e08aacStbbdev } 15549e08aacStbbdev correct_alignment(std::size_t alignment)15649e08aacStbbdev std::size_t correct_alignment(std::size_t alignment) { 15749e08aacStbbdev __TBB_ASSERT(tbb::detail::is_power_of_two(alignment), "Alignment is not a power of 2"); 15849e08aacStbbdev #if __TBB_CPP17_HW_INTERFERENCE_SIZE_PRESENT 15949e08aacStbbdev std::size_t cache_line_size = std::hardware_destructive_interference_size; 16049e08aacStbbdev #else 16149e08aacStbbdev std::size_t cache_line_size = r1::cache_line_size(); 16249e08aacStbbdev #endif 16349e08aacStbbdev return alignment < cache_line_size ? cache_line_size : alignment; 16449e08aacStbbdev } 16549e08aacStbbdev correct_size(std::size_t bytes)16649e08aacStbbdev std::size_t correct_size(std::size_t bytes) { 16749e08aacStbbdev // To handle the case, when small size requested. There could be not 16849e08aacStbbdev // enough space to store the original pointer. 16949e08aacStbbdev return bytes < sizeof(std::uintptr_t) ? sizeof(std::uintptr_t) : bytes; 17049e08aacStbbdev } 17149e08aacStbbdev 17249e08aacStbbdev std::pmr::memory_resource* m_upstream; 17349e08aacStbbdev }; 17449e08aacStbbdev 17549e08aacStbbdev #endif // __TBB_CPP17_MEMORY_RESOURCE_PRESENT 17649e08aacStbbdev 17749e08aacStbbdev } // namespace d1 17849e08aacStbbdev } // namespace detail 17949e08aacStbbdev 18049e08aacStbbdev inline namespace v1 { 18149e08aacStbbdev using detail::d1::cache_aligned_allocator; 18249e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 18349e08aacStbbdev using detail::d1::cache_aligned_resource; 18449e08aacStbbdev #endif 18549e08aacStbbdev } // namespace v1 18649e08aacStbbdev } // namespace tbb 18749e08aacStbbdev 18849e08aacStbbdev #endif /* __TBB_cache_aligned_allocator_H */ 18949e08aacStbbdev 190