Created
June 25, 2018 02:55
-
-
Save jmal0/49f1ea1b3b2e9f64141aab4741480083 to your computer and use it in GitHub Desktop.
C++ aligned pointer wrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstddef> | |
#include <cstdlib> | |
#include <memory> | |
//! @brief Wraps compiler specific builtin alignment assumptions | |
template <std::size_t alignment, typename T> | |
T* assume_aligned(T* ptr) | |
{ | |
#if defined(__INTEL_COMPILER) | |
// ICPC defines __GNUC__ so check this first | |
__assume_aligned(ptr, alignment); | |
return ptr; | |
#elif defined(__GNUC__) || defined(__clang__) | |
// Clang supports GCC's __builtin_assume_aligned | |
return static_cast<T*>(__builtin_assume_aligned(ptr, alignment)); | |
#else | |
// Sucks to suck MSVC | |
return ptr; | |
#endif | |
} | |
#if __cplusplus >= 201101L | |
//! @brief Provide overload of assume_aligned for unique_ptr | |
template <std::size_t alignment, typename T, typename Deleter> | |
T* assume_aligned(const std::unique_ptr<T[], Deleter>& ptr) | |
{ | |
return assume_aligned<alignment>(ptr.get()); | |
} | |
#endif | |
//! @class Aligned_ptr Wrapper for pointer to aligned memory | |
//! @details | |
//! This class can be used to encourage optimizing compilers to generate | |
//! aligned instructions. gcc 7+, and clang 3.8+ have been observed to | |
//! generate intel aligned move instructions with the use of this class. | |
template <typename T, | |
std::size_t alignment = 64, | |
typename Storage = T*> | |
class Aligned_ptr | |
{ | |
public: | |
//! @brief Allocate a pointer to data of given size and alignment | |
explicit Aligned_ptr(const std::size_t size) : | |
ptr(static_cast<T*>(aligned_alloc(alignment, size))) | |
{ | |
} | |
//! @brief Implicit cast to aligned raw pointer | |
//! @details | |
//! This is provided so Aligned_uptr can be passed as Aligned_ptr. | |
//! Explicit cast is required to guard against memory leaks. | |
explicit operator Aligned_ptr<T, alignment, T*>() const | |
{ | |
return Aligned_ptr<T, alignment, T*>(&this->ptr[0]); | |
} | |
//! @brief Get element after applying builtin alignment assumption | |
T& operator[](const std::size_t idx) | |
{ | |
return this->data()[idx]; | |
} | |
//! @brief Get element after applying builtin alignment assumption | |
const T& operator[](const std::size_t idx) const | |
{ | |
return this->data()[idx]; | |
} | |
//! @brief Get non-const pointer after applying builtin alignment assumption | |
T* data() | |
{ | |
return assume_aligned<alignment>(this->ptr); | |
} | |
//! @brief Get const pointer after applying builtin alignment assumption | |
const T* data() const | |
{ | |
return assume_aligned<alignment>(this->ptr); | |
} | |
private: | |
Storage ptr; | |
// Make all template instantiations friends so cast operator can call private constructor | |
template <typename T2, std::size_t alignment2, typename Storage2> | |
friend class Aligned_ptr; | |
//! @brief Private constructor provided for cast operator | |
explicit Aligned_ptr(T* other) : | |
ptr(other) | |
{ | |
} | |
}; | |
#if __cplusplus >= 201101L | |
//! @brief Convenience alias for unique_ptr to aligned memory | |
template <typename T, std::size_t alignment = 64, typename Deleter = std::default_delete<T>> | |
using Aligned_uptr = Aligned_ptr<T, alignment, std::unique_ptr<T[], Deleter>>; | |
//! Not tested | |
template <typename T, std::size_t alignment = 64> | |
using Aligned_sptr = Aligned_ptr<T, alignment, std::shared_ptr<T[]>>; | |
#endif | |
template <typename T> | |
Aligned_uptr<T> f(const Aligned_ptr<T> LHS, | |
const Aligned_ptr<T> RHS, | |
const std::size_t n_elems) | |
{ | |
Aligned_uptr<T> dst(n_elems); | |
for (std::size_t i = 0; i < n_elems; ++i) | |
{ | |
dst[i] = LHS[i] + RHS[i]; | |
} | |
return dst; | |
} | |
template <typename T> | |
Aligned_uptr<T> f2(const Aligned_uptr<T>& LHS, | |
const Aligned_uptr<T>& RHS, | |
const std::size_t n_elems) | |
{ | |
return f(static_cast<Aligned_ptr<T>>(LHS), | |
static_cast<Aligned_ptr<T>>(RHS), | |
n_elems); | |
} | |
template Aligned_uptr<float> f2(const Aligned_uptr<float>&, | |
const Aligned_uptr<float>&, | |
std::size_t); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment