1*51c0b2f7Stbbdev /* 2*51c0b2f7Stbbdev Copyright (c) 2005-2020 Intel Corporation 3*51c0b2f7Stbbdev 4*51c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*51c0b2f7Stbbdev you may not use this file except in compliance with the License. 6*51c0b2f7Stbbdev You may obtain a copy of the License at 7*51c0b2f7Stbbdev 8*51c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0 9*51c0b2f7Stbbdev 10*51c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software 11*51c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*51c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*51c0b2f7Stbbdev See the License for the specific language governing permissions and 14*51c0b2f7Stbbdev limitations under the License. 15*51c0b2f7Stbbdev */ 16*51c0b2f7Stbbdev 17*51c0b2f7Stbbdev #ifndef __TBB_cache_aligned_allocator_H 18*51c0b2f7Stbbdev #define __TBB_cache_aligned_allocator_H 19*51c0b2f7Stbbdev 20*51c0b2f7Stbbdev #include "tbb/detail/_utils.h" 21*51c0b2f7Stbbdev #include <cstdlib> 22*51c0b2f7Stbbdev #include <utility> 23*51c0b2f7Stbbdev 24*51c0b2f7Stbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 25*51c0b2f7Stbbdev #include <memory_resource> 26*51c0b2f7Stbbdev #endif 27*51c0b2f7Stbbdev 28*51c0b2f7Stbbdev namespace tbb { 29*51c0b2f7Stbbdev namespace detail { 30*51c0b2f7Stbbdev 31*51c0b2f7Stbbdev namespace r1 { 32*51c0b2f7Stbbdev void* __TBB_EXPORTED_FUNC cache_aligned_allocate(std::size_t size); 33*51c0b2f7Stbbdev void __TBB_EXPORTED_FUNC cache_aligned_deallocate(void* p); 34*51c0b2f7Stbbdev std::size_t __TBB_EXPORTED_FUNC cache_line_size(); 35*51c0b2f7Stbbdev } 36*51c0b2f7Stbbdev 37*51c0b2f7Stbbdev namespace d1 { 38*51c0b2f7Stbbdev 39*51c0b2f7Stbbdev template<typename T> 40*51c0b2f7Stbbdev class cache_aligned_allocator { 41*51c0b2f7Stbbdev public: 42*51c0b2f7Stbbdev using value_type = T; 43*51c0b2f7Stbbdev using propagate_on_container_move_assignment = std::true_type; 44*51c0b2f7Stbbdev 45*51c0b2f7Stbbdev //! Always defined for TBB containers (supported since C++17 for std containers) 46*51c0b2f7Stbbdev using is_always_equal = std::true_type; 47*51c0b2f7Stbbdev 48*51c0b2f7Stbbdev cache_aligned_allocator() = default; 49*51c0b2f7Stbbdev template<typename U> cache_aligned_allocator(const cache_aligned_allocator<U>&) noexcept {} 50*51c0b2f7Stbbdev 51*51c0b2f7Stbbdev //! Allocate space for n objects, starting on a cache/sector line. 52*51c0b2f7Stbbdev T* allocate(std::size_t n) { 53*51c0b2f7Stbbdev return static_cast<T*>(r1::cache_aligned_allocate(n * sizeof(value_type))); 54*51c0b2f7Stbbdev } 55*51c0b2f7Stbbdev 56*51c0b2f7Stbbdev //! Free block of memory that starts on a cache line 57*51c0b2f7Stbbdev void deallocate(T* p, std::size_t) { 58*51c0b2f7Stbbdev r1::cache_aligned_deallocate(p); 59*51c0b2f7Stbbdev } 60*51c0b2f7Stbbdev 61*51c0b2f7Stbbdev //! Largest value for which method allocate might succeed. 62*51c0b2f7Stbbdev std::size_t max_size() const noexcept { 63*51c0b2f7Stbbdev return (~std::size_t(0) - r1::cache_line_size()) / sizeof(value_type); 64*51c0b2f7Stbbdev } 65*51c0b2f7Stbbdev 66*51c0b2f7Stbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN 67*51c0b2f7Stbbdev using pointer = value_type*; 68*51c0b2f7Stbbdev using const_pointer = const value_type*; 69*51c0b2f7Stbbdev using reference = value_type&; 70*51c0b2f7Stbbdev using const_reference = const value_type&; 71*51c0b2f7Stbbdev using difference_type = std::ptrdiff_t; 72*51c0b2f7Stbbdev using size_type = std::size_t; 73*51c0b2f7Stbbdev template<typename U> struct rebind { 74*51c0b2f7Stbbdev using other = cache_aligned_allocator<U>; 75*51c0b2f7Stbbdev }; 76*51c0b2f7Stbbdev template<typename U, typename... Args> 77*51c0b2f7Stbbdev void construct(U *p, Args&&... args) 78*51c0b2f7Stbbdev { ::new (p) U(std::forward<Args>(args)...); } 79*51c0b2f7Stbbdev void destroy(pointer p) { p->~value_type(); } 80*51c0b2f7Stbbdev pointer address(reference x) const { return &x; } 81*51c0b2f7Stbbdev const_pointer address(const_reference x) const { return &x; } 82*51c0b2f7Stbbdev #endif // TBB_ALLOCATOR_TRAITS_BROKEN 83*51c0b2f7Stbbdev }; 84*51c0b2f7Stbbdev 85*51c0b2f7Stbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN 86*51c0b2f7Stbbdev template<> 87*51c0b2f7Stbbdev class cache_aligned_allocator<void> { 88*51c0b2f7Stbbdev public: 89*51c0b2f7Stbbdev using pointer = void*; 90*51c0b2f7Stbbdev using const_pointer = const void*; 91*51c0b2f7Stbbdev using value_type = void; 92*51c0b2f7Stbbdev template<typename U> struct rebind { 93*51c0b2f7Stbbdev using other = cache_aligned_allocator<U>; 94*51c0b2f7Stbbdev }; 95*51c0b2f7Stbbdev }; 96*51c0b2f7Stbbdev #endif 97*51c0b2f7Stbbdev 98*51c0b2f7Stbbdev template<typename T, typename U> 99*51c0b2f7Stbbdev bool operator==(const cache_aligned_allocator<T>&, const cache_aligned_allocator<U>&) noexcept { return true; } 100*51c0b2f7Stbbdev 101*51c0b2f7Stbbdev template<typename T, typename U> 102*51c0b2f7Stbbdev bool operator!=(const cache_aligned_allocator<T>&, const cache_aligned_allocator<U>&) noexcept { return false; } 103*51c0b2f7Stbbdev 104*51c0b2f7Stbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 105*51c0b2f7Stbbdev 106*51c0b2f7Stbbdev //! C++17 memory resource wrapper to ensure cache line size alignment 107*51c0b2f7Stbbdev class cache_aligned_resource : public std::pmr::memory_resource { 108*51c0b2f7Stbbdev public: 109*51c0b2f7Stbbdev cache_aligned_resource() : cache_aligned_resource(std::pmr::get_default_resource()) {} 110*51c0b2f7Stbbdev explicit cache_aligned_resource(std::pmr::memory_resource* upstream) : m_upstream(upstream) {} 111*51c0b2f7Stbbdev 112*51c0b2f7Stbbdev std::pmr::memory_resource* upstream_resource() const { 113*51c0b2f7Stbbdev return m_upstream; 114*51c0b2f7Stbbdev } 115*51c0b2f7Stbbdev 116*51c0b2f7Stbbdev private: 117*51c0b2f7Stbbdev //! We don't know what memory resource set. Use padding to guarantee alignment 118*51c0b2f7Stbbdev void* do_allocate(std::size_t bytes, std::size_t alignment) override { 119*51c0b2f7Stbbdev // TODO: make it common with tbb_allocator.cpp 120*51c0b2f7Stbbdev std::size_t cache_line_alignment = correct_alignment(alignment); 121*51c0b2f7Stbbdev std::size_t space = correct_size(bytes) + cache_line_alignment; 122*51c0b2f7Stbbdev std::uintptr_t base = reinterpret_cast<std::uintptr_t>(m_upstream->allocate(space)); 123*51c0b2f7Stbbdev __TBB_ASSERT(base != 0, "Upstream resource returned NULL."); 124*51c0b2f7Stbbdev 125*51c0b2f7Stbbdev // Round up to the next cache line (align the base address) 126*51c0b2f7Stbbdev std::uintptr_t result = (base + cache_line_alignment) & ~(cache_line_alignment - 1); 127*51c0b2f7Stbbdev __TBB_ASSERT((result - base) >= sizeof(std::uintptr_t), "Can`t store a base pointer to the header"); 128*51c0b2f7Stbbdev __TBB_ASSERT(space - (result - base) >= bytes, "Not enough space for the storage"); 129*51c0b2f7Stbbdev 130*51c0b2f7Stbbdev // Record where block actually starts. 131*51c0b2f7Stbbdev (reinterpret_cast<std::uintptr_t*>(result))[-1] = base; 132*51c0b2f7Stbbdev return reinterpret_cast<void*>(result); 133*51c0b2f7Stbbdev } 134*51c0b2f7Stbbdev 135*51c0b2f7Stbbdev void do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) override { 136*51c0b2f7Stbbdev if (ptr) { 137*51c0b2f7Stbbdev // Recover where block actually starts 138*51c0b2f7Stbbdev std::uintptr_t base = (reinterpret_cast<std::uintptr_t*>(ptr))[-1]; 139*51c0b2f7Stbbdev m_upstream->deallocate(reinterpret_cast<void*>(base), correct_size(bytes) + correct_alignment(alignment)); 140*51c0b2f7Stbbdev } 141*51c0b2f7Stbbdev } 142*51c0b2f7Stbbdev 143*51c0b2f7Stbbdev bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { 144*51c0b2f7Stbbdev if (this == &other) { return true; } 145*51c0b2f7Stbbdev #if __TBB_USE_OPTIONAL_RTTI 146*51c0b2f7Stbbdev const cache_aligned_resource* other_res = dynamic_cast<const cache_aligned_resource*>(&other); 147*51c0b2f7Stbbdev return other_res && (upstream_resource() == other_res->upstream_resource()); 148*51c0b2f7Stbbdev #else 149*51c0b2f7Stbbdev return false; 150*51c0b2f7Stbbdev #endif 151*51c0b2f7Stbbdev } 152*51c0b2f7Stbbdev 153*51c0b2f7Stbbdev std::size_t correct_alignment(std::size_t alignment) { 154*51c0b2f7Stbbdev __TBB_ASSERT(tbb::detail::is_power_of_two(alignment), "Alignment is not a power of 2"); 155*51c0b2f7Stbbdev #if __TBB_CPP17_HW_INTERFERENCE_SIZE_PRESENT 156*51c0b2f7Stbbdev std::size_t cache_line_size = std::hardware_destructive_interference_size; 157*51c0b2f7Stbbdev #else 158*51c0b2f7Stbbdev std::size_t cache_line_size = r1::cache_line_size(); 159*51c0b2f7Stbbdev #endif 160*51c0b2f7Stbbdev return alignment < cache_line_size ? cache_line_size : alignment; 161*51c0b2f7Stbbdev } 162*51c0b2f7Stbbdev 163*51c0b2f7Stbbdev std::size_t correct_size(std::size_t bytes) { 164*51c0b2f7Stbbdev // To handle the case, when small size requested. There could be not 165*51c0b2f7Stbbdev // enough space to store the original pointer. 166*51c0b2f7Stbbdev return bytes < sizeof(std::uintptr_t) ? sizeof(std::uintptr_t) : bytes; 167*51c0b2f7Stbbdev } 168*51c0b2f7Stbbdev 169*51c0b2f7Stbbdev std::pmr::memory_resource* m_upstream; 170*51c0b2f7Stbbdev }; 171*51c0b2f7Stbbdev 172*51c0b2f7Stbbdev #endif // __TBB_CPP17_MEMORY_RESOURCE_PRESENT 173*51c0b2f7Stbbdev 174*51c0b2f7Stbbdev } // namespace d1 175*51c0b2f7Stbbdev } // namespace detail 176*51c0b2f7Stbbdev 177*51c0b2f7Stbbdev inline namespace v1 { 178*51c0b2f7Stbbdev using detail::d1::cache_aligned_allocator; 179*51c0b2f7Stbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 180*51c0b2f7Stbbdev using detail::d1::cache_aligned_resource; 181*51c0b2f7Stbbdev #endif 182*51c0b2f7Stbbdev } // namespace v1 183*51c0b2f7Stbbdev } // namespace tbb 184*51c0b2f7Stbbdev 185*51c0b2f7Stbbdev #endif /* __TBB_cache_aligned_allocator_H */ 186*51c0b2f7Stbbdev 187