Skip to content

Commit

Permalink
Changed the semantics of Aligned<T> to not guarantee that T will be a…
Browse files Browse the repository at this point in the history
…lone on the aligned memory block of size alignment. Aligned<T[]> still guarantees that inherently as an array. CacheAligned<T> is to be used to when the guarantee of being alone on that aligned memory block is needed. CacheAligned classes also automatically determine the cache line size at run-time.
  • Loading branch information
NickStrupat committed May 5, 2014
1 parent 20309ae commit 6d430c3
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 60 deletions.
77 changes: 34 additions & 43 deletions Aligned.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,65 @@
#include <cstdint>
#include <memory>
#include "AlignedBase.hpp"
#include "RoundUp.hpp"

#define ALIGNED_BYTES_SIZE(T, sizeOfTPaddedToAlignment) sizeof(T) + sizeOfTPaddedToAlignment - 1
#define ALIGNED_REF(T, bytes, sizeOfTPaddedToAlignment) *reinterpret_cast<T *>(uintptr_t(bytes) + (sizeOfTPaddedToAlignment - 1) & ~uintptr_t(sizeOfTPaddedToAlignment - 1))
#define ALIGNED_ARRAY_BYTES_SIZE(T, sizeOfTPaddedToAlignment, size) sizeof(T) + (sizeOfTPaddedToAlignment * size) - 1
#define ALIGNED_ARRAY_INDEX(T, bytes, sizeOfTPaddedToAlignment, index) *reinterpret_cast<T *>((uintptr_t(bytes) + (sizeOfTPaddedToAlignment - 1) & ~uintptr_t(sizeOfTPaddedToAlignment - 1)) + (sizeOfTPaddedToAlignment * index))

template<typename T, std::size_t Alignment = -1>
class Aligned : AlignedBase<Alignment> {
static const std::size_t SizeOfTPaddedToAlignmentLessOne = RoundUp<sizeof(T), Alignment>::value - 1;
protected:
uint8_t bytes[SizeOfTPaddedToAlignmentLessOne + SizeOfTPaddedToAlignmentLessOne + 1];
class Aligned : AlignedBase<T, Alignment> {
uint8_t bytes[ALIGNED_BYTES_SIZE(T, SizeOfTPaddedToAlignment)];
public:
class PaddedType { uint8_t bytes[RoundUp<sizeof(T), Alignment>::value]; };
Aligned() {}
Aligned(T const & value) { ref() = value; }
T & ref() {
return *reinterpret_cast<T *>(uintptr_t(bytes) + SizeOfTPaddedToAlignmentLessOne & ~uintptr_t(SizeOfTPaddedToAlignmentLessOne));
}
T & ref() { return ALIGNED_REF(T, bytes, SizeOfTPaddedToAlignment); }
T const & ref() const { return ref(); }
};

template<typename T>
class Aligned<T, -1> : AlignedBase<T, -1> {
std::unique_ptr<uint8_t[]> const pBytes;
protected:
Aligned(std::size_t alignment, void * ignoredParameterForOverload) : AlignedBase(alignment), pBytes(new uint8_t[alignment + sizeOfTPaddedToAlignment - 1]) {}
public:
Aligned(std::size_t alignment) : AlignedBase(alignment), pBytes(new uint8_t[ALIGNED_BYTES_SIZE(T, sizeOfTPaddedToAlignment)]) {}
Aligned(std::size_t alignment, T const & value) : Aligned(alignment) { ref() = value; }
T & ref() { return ALIGNED_REF(T, pBytes.get(), sizeOfTPaddedToAlignment); }
T const & ref() const { return ref(); }
};

template<typename T, std::size_t Size, std::size_t Alignment>
class Aligned<T[Size], Alignment> : AlignedBase<Alignment> {
static const std::size_t SizeOfTPaddedToAlignment = RoundUp<sizeof(T), Alignment>::value;
uint8_t bytes[sizeof(T) + (SizeOfTPaddedToAlignment * Size) - 1];
class Aligned<T[Size], Alignment> : AlignedBase<T, Alignment> {
uint8_t bytes[ALIGNED_ARRAY_BYTES_SIZE(T, SizeOfTPaddedToAlignment, Size)];
public:
Aligned() {}
T & operator[](std::size_t index) {
return *reinterpret_cast<T *>((uintptr_t(bytes) + (SizeOfTPaddedToAlignment - 1) & ~uintptr_t(SizeOfTPaddedToAlignment - 1)) + (SizeOfTPaddedToAlignment * index));
}
T & operator[](std::size_t index) { return ALIGNED_ARRAY_INDEX(T, bytes, SizeOfTPaddedToAlignment, index); }
T const & operator[](std::size_t index) const { return operator[](index); }
};

template<typename T, std::size_t Alignment>
class Aligned<T[], Alignment> : AlignedBase<Alignment>{
static const std::size_t SizeOfTPaddedToAlignment = RoundUp<sizeof(T), Alignment>::value;
class Aligned<T[], Alignment> : AlignedBase<T, Alignment> {
std::unique_ptr<uint8_t[]> const pBytes;
Aligned();
public:
Aligned(std::size_t size) : pBytes(new uint8_t[sizeof(T) + (SizeOfTPaddedToAlignment * size) - 1]) {}
T & operator[](std::size_t index) {
return *reinterpret_cast<T *>((uintptr_t(pBytes.get()) + (SizeOfTPaddedToAlignment - 1) & ~uintptr_t(SizeOfTPaddedToAlignment - 1)) + (SizeOfTPaddedToAlignment * index));
}
Aligned(std::size_t size) : pBytes(new uint8_t[ALIGNED_ARRAY_BYTES_SIZE(T, SizeOfTPaddedToAlignment, size)]) {}
T & operator[](std::size_t index) { return ALIGNED_ARRAY_INDEX(T, pBytes.get(), SizeOfTPaddedToAlignment, index); }
T const & operator[](std::size_t index) const { return operator[](index); }
};

template<typename T>
class Aligned<T, -1> : AlignedBase<-1>{
std::size_t const sizeOfTPaddedToAlignmentLessOne;
class Aligned<T[], -1> : AlignedBase<T, -1> {
std::unique_ptr<uint8_t[]> const pBytes;
public:
Aligned(std::size_t alignment = AlignedBase::cacheLineSize()) : AlignedBase(alignment), sizeOfTPaddedToAlignmentLessOne(roundUp(sizeof(T), alignment) - 1), pBytes(new uint8_t[sizeof(T) +sizeOfTPaddedToAlignmentLessOne]) {}
Aligned(T const & value, std::size_t alignment) : Aligned(alignment) { ref() = value; }
T & ref() {
return *reinterpret_cast<T *>(uintptr_t(pBytes.get()) + sizeOfTPaddedToAlignmentLessOne & ~uintptr_t(sizeOfTPaddedToAlignmentLessOne));
}
T const & ref() const { return ref(); }
};

template<typename T>
class Aligned<T[], -1> : AlignedBase<-1>{
std::size_t const sizeOfTPaddedToAlignment;
std::unique_ptr<uint8_t> const pBytes;
Aligned();
public:
Aligned(std::size_t size, std::size_t alignment = AlignedBase::cacheLineSize())
: AlignedBase<-1>(alignment),
sizeOfTPaddedToAlignment(roundUp(sizeof(T), alignment)), pBytes(new uint8_t[sizeof(T) + (sizeOfTPaddedToAlignment * size) - 1]) {}
T & operator[](std::size_t index) {
return *reinterpret_cast<T *>((uintptr_t(pBytes.get()) + (sizeOfTPaddedToAlignment - 1) & ~uintptr_t(sizeOfTPaddedToAlignment - 1)) + (sizeOfTPaddedToAlignment * index));
}
Aligned(std::size_t alignment, std::size_t size) : AlignedBase<T, -1>(alignment), pBytes(new uint8_t[ALIGNED_ARRAY_BYTES_SIZE(T, sizeOfTPaddedToAlignment, size)]) {}
T & operator[](std::size_t index) { return ALIGNED_ARRAY_INDEX(T, pBytes.get(), sizeOfTPaddedToAlignment, index); }
T const & operator[](std::size_t index) const { return operator[](index); }
};

#undef ALIGNED_BYTES_SIZE
#undef ALIGNED_REF
#undef ALIGNED_ARRAY_BYTES_SIZE
#undef ALIGNED_ARRAY_INDEX

#endif
29 changes: 15 additions & 14 deletions AlignedBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,32 @@

#include <cstddef>
#include <exception>
#include "CacheLineSize/cacheLineSize.h"
#include "RoundUp.hpp"
#include "IsPowerOfTwo.hpp"

#define ALIGNMENT_MUST_BE_A_MULTIPLE_OF_2 "Alignment must be a multiple of 2"
#define ALIGNMENT_MUST_BE_A_POWER_OF_2 "Alignment must be a power of 2"

template<std::size_t Alignment = -1>
template<typename T, std::size_t Alignment = -1>
class AlignedBase {
static_assert(Alignment % 2 == 0, ALIGNMENT_MUST_BE_A_MULTIPLE_OF_2);
static_assert(IsPowerOfTwo<std::size_t, Alignment>::Result, ALIGNMENT_MUST_BE_A_POWER_OF_2);
protected:
static const std::size_t SizeOfTPaddedToAlignment = RoundUp<std::size_t, sizeof(T), Alignment>::Result;
};

template<>
class AlignedBase<-1> {
template<typename T>
class AlignedBase<T, -1> {
public:
class AlignmentException : public std::exception {
friend class AlignedBase<-1>;
AlignmentException() : std::exception(ALIGNMENT_MUST_BE_A_MULTIPLE_OF_2) {}
friend class AlignedBase<T, -1>;
AlignmentException() : std::exception(ALIGNMENT_MUST_BE_A_POWER_OF_2) {}
};
AlignedBase & operator=(AlignedBase const &) = delete;
protected:
AlignedBase(std::size_t alignment) {
if (alignment % 2 != 0)
std::size_t const sizeOfTPaddedToAlignment;
AlignedBase(std::size_t alignment) : sizeOfTPaddedToAlignment(roundUp(sizeof(T), alignment)) {
if (!isPowerOfTwo(alignment))
throw new AlignmentException();
}
static std::size_t cacheLineSize() {
static std::size_t cacheLineSize = cache_line_size();
return cacheLineSize;
}
};

#undef ALIGNMENT_MUST_BE_A_MULTIPLE_OF_2
Expand Down
35 changes: 35 additions & 0 deletions CacheAligned.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef CACHEALIGNED_HPP_INCLUDED
#define CACHEALIGNED_HPP_INCLUDED

#include <cstddef>
#include <cstdint>
#include "CacheAlignedBase.hpp"
#include "Aligned.hpp"
#include "RoundUp.hpp"

template<typename T>
class CacheAligned : protected Aligned<T>, CacheAlignedBase {
public:
CacheAligned() : Aligned(cacheLineSize(), nullptr) {}
CacheAligned(T const & value) : CacheAligned() { ref() = value; }
T & ref() { return *reinterpret_cast<T *>(&Aligned::ref()); }
T const & ref() const { return ref(); }
};

template<typename T, std::size_t Size>
class CacheAligned<T[Size]> : Aligned<T[]>, CacheAlignedBase{
public:
CacheAligned() : Aligned(cacheLineSize(), Size) {}
T & operator[](std::size_t index) { return *reinterpret_cast<T *>(&Aligned::operator[](index)); }
T const & operator[](std::size_t index) const { return operator[](index); }
};

template<typename T>
class CacheAligned<T[]> : Aligned<T[]>, CacheAlignedBase {
public:
CacheAligned(std::size_t size) : Aligned(cacheLineSize(), size) {}
T & operator[](std::size_t index) { return *reinterpret_cast<T *>(&Aligned::operator[](index)); }
T const & operator[](std::size_t index) const { return operator[](index); }
};

#endif
15 changes: 15 additions & 0 deletions CacheAlignedBase.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef CACHEALIGNEDBASE_HPP_INCLUDED
#define CACHEALIGNEDBASE_HPP_INCLUDED

#include <cstddef>
#include "CacheLineSize/cacheLineSize.h"

class CacheAlignedBase {
protected:
static std::size_t cacheLineSize() {
static std::size_t cacheLineSize = cache_line_size();
return cacheLineSize;
}
};

#endif
18 changes: 18 additions & 0 deletions IsPowerOfTwo.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef ISPOWEROFTWO_HPP_INCLUDED
#define ISPOWEROFTWO_HPP_INCLUDED

#define ISPOWEROFTWO(x) (x != 0) && ((x & (x - 1)) == 0)

template<typename T>
inline bool isPowerOfTwo(T x) {
return ISPOWEROFTWO(x);
}

template<typename T, T X>
struct IsPowerOfTwo {
static const bool Result = ISPOWEROFTWO(X);
};

#undef ISPOWEROFTWO

#endif
10 changes: 7 additions & 3 deletions RoundUp.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
#ifndef ROUNDUP_HPP_INCLUDED
#define ROUNDUP_HPP_INCLUDED

#define ROUNDUP(from, to) from + to - 1 - (from - 1) % to

template<typename T>
inline T roundUp(T from, T to) {
return from + to - 1 - (from - 1) % to;
return ROUNDUP(from, to);
}

template<size_t From, size_t To>
template<typename T, T From, T To>
struct RoundUp {
enum { value = From + To - 1 - (From -1) % To };
static const T Result = ROUNDUP(From, To);
};

#undef ROUNDUP

#endif

0 comments on commit 6d430c3

Please sign in to comment.