Skip to content

Instantly share code, notes, and snippets.

@jmal0
Created June 25, 2018 02:55
Show Gist options
  • Save jmal0/49f1ea1b3b2e9f64141aab4741480083 to your computer and use it in GitHub Desktop.
Save jmal0/49f1ea1b3b2e9f64141aab4741480083 to your computer and use it in GitHub Desktop.
C++ aligned pointer wrapper
#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