Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend sizeOf<> and offsetOf<> to support alignment and padding #156

Merged
merged 4 commits into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 97 additions & 43 deletions include/llama/Core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,45 +126,6 @@ namespace llama
template <typename DatumElement>
using GetDatumElementType = boost::mp11::mp_second<DatumElement>;

template <typename T>
static constexpr auto sizeOf = sizeof(T);

/// The size a datum domain if it would be a normal struct.
template <typename... DatumElements>
static constexpr auto sizeOf<DatumStruct<DatumElements...>> = (sizeOf<GetDatumElementType<DatumElements>> + ...);

namespace internal
{
template <typename T>
constexpr auto offsetOfImpl(T*, DatumCoord<>)
{
return 0;
}

template <typename... DatumElements, std::size_t FirstCoord, std::size_t... Coords>
constexpr auto offsetOfImpl(DatumStruct<DatumElements...>*, DatumCoord<FirstCoord, Coords...>)
{
std::size_t acc = 0;
boost::mp11::mp_for_each<boost::mp11::mp_iota_c<FirstCoord>>([&](auto i) constexpr {
constexpr auto index = decltype(i)::value;
using Element = boost::mp11::mp_at_c<DatumStruct<DatumElements...>, index>;
acc += sizeOf<GetDatumElementType<Element>>;
});

using Element = boost::mp11::mp_at_c<DatumStruct<DatumElements...>, FirstCoord>;
acc += offsetOfImpl((GetDatumElementType<Element>*) nullptr, DatumCoord<Coords...>{});

return acc;
}
} // namespace internal

/// The byte offset of an element in a datum domain if it would be a normal
/// struct.
/// \tparam DatumDomain Datum domain tree.
/// \tparam DatumCoord Datum coordinate of an element indatum domain tree.
template <typename DatumDomain, typename DatumCoord>
inline constexpr std::size_t offsetOf = internal::offsetOfImpl((DatumDomain*) nullptr, DatumCoord{});

template <typename T>
inline constexpr auto isDatumStruct = false;

Expand Down Expand Up @@ -311,14 +272,14 @@ namespace llama
namespace internal
{
template <typename T, std::size_t... Coords, typename Functor>
LLAMA_FN_HOST_ACC_INLINE constexpr void forEachLeaveImpl(T, DatumCoord<Coords...> coord, Functor&& functor)
LLAMA_FN_HOST_ACC_INLINE constexpr void forEachLeaveImpl(T*, DatumCoord<Coords...> coord, Functor&& functor)
{
functor(coord);
};

template <typename... Children, std::size_t... Coords, typename Functor>
LLAMA_FN_HOST_ACC_INLINE constexpr void forEachLeaveImpl(
DatumStruct<Children...>,
DatumStruct<Children...>*,
DatumCoord<Coords...>,
Functor&& functor)
{
Expand All @@ -329,7 +290,7 @@ namespace llama

LLAMA_FORCE_INLINE_RECURSIVE
forEachLeaveImpl(
GetDatumElementType<DatumElement>{},
static_cast<GetDatumElementType<DatumElement>*>(nullptr),
llama::DatumCoord<Coords..., childIndex>{},
std::forward<Functor>(functor));
});
Expand All @@ -347,7 +308,7 @@ namespace llama
{
LLAMA_FORCE_INLINE_RECURSIVE
internal::forEachLeaveImpl(
GetType<DatumDomain, DatumCoord<Coords...>>{},
static_cast<GetType<DatumDomain, DatumCoord<Coords...>>*>(nullptr),
baseCoord,
std::forward<Functor>(functor));
}
Expand Down Expand Up @@ -396,6 +357,99 @@ namespace llama
}
();

namespace internal
{
constexpr void roundUpToMultiple(std::size_t& value, std::size_t multiple)
{
value = ((value + multiple - 1) / multiple) * multiple;
}
} // namespace internal

template <typename T, bool Align = false>
inline constexpr std::size_t sizeOf = sizeof(T);

/// The size a datum domain if it would be a normal struct.
template <typename... DatumElements, bool Align>
inline constexpr std::size_t sizeOf<DatumStruct<DatumElements...>, Align> = []() constexpr
{
if constexpr (Align)
{
using namespace boost::mp11;

#if BOOST_COMP_MSVC
std::size_t size;
size = 0; // if we join this assignment with the previous line, MSVC crashes :(
#else
std::size_t size = 0;
#endif
using FlatDD = FlattenDatumDomain<DatumStruct<DatumElements...>>;
mp_for_each<mp_iota<mp_size<FlatDD>>>([&](auto i) constexpr {
using T = mp_at<FlatDD, decltype(i)>;
internal::roundUpToMultiple(size, alignof(T));
size += sizeof(T);
});

// final padding, so next struct can start right away
using FirstType = mp_first<FlatDD>;
internal::roundUpToMultiple(size, alignof(FirstType));
return size;
}
else
return (sizeOf<GetDatumElementType<DatumElements>, Align> + ...);
}
();

namespace internal
{
template <bool Align, typename T>
constexpr auto offsetOfImpl(T*, DatumCoord<>)
{
return 0;
}

template <bool Align, typename... DatumElements, std::size_t FirstCoord, std::size_t... Coords>
constexpr auto offsetOfImpl(DatumStruct<DatumElements...>*, DatumCoord<FirstCoord, Coords...>)
{
std::size_t acc = 0;
boost::mp11::mp_for_each<boost::mp11::mp_take_c<DatumStruct<DatumElements...>, FirstCoord>>([&](
auto element) constexpr { acc += sizeOf<GetDatumElementType<decltype(element)>>; });

using Element = boost::mp11::mp_at_c<DatumStruct<DatumElements...>, FirstCoord>;
acc += offsetOfImpl<Align>((GetDatumElementType<Element>*) nullptr, DatumCoord<Coords...>{});

return acc;
}
} // namespace internal

/// The byte offset of an element in a datum domain if it would be a normal
/// struct.
/// \tparam DatumDomain Datum domain tree.
/// \tparam DatumCoord Datum coordinate of an element indatum domain tree.
template <typename DatumDomain, typename DatumCoord, bool Align = false>
inline constexpr std::size_t offsetOf = []() constexpr
{
if constexpr (Align)
{
using namespace boost::mp11;

constexpr auto i = flatDatumCoord<DatumDomain, DatumCoord>;

std::size_t offset = 0;
using FlatDD = FlattenDatumDomain<DatumDomain>;
mp_for_each<mp_iota_c<i>>([&](auto i) constexpr {
using T = mp_at<FlatDD, decltype(i)>;
internal::roundUpToMultiple(offset, alignof(T));
offset += sizeof(T);
});
internal::roundUpToMultiple(offset, alignof(boost::mp11::mp_at_c<FlatDD, i>));
return offset;
}
else
return internal::offsetOfImpl<Align>((DatumDomain*) nullptr, DatumCoord{});
}
();


template <typename S>
auto structName(S) -> std::string
{
Expand Down
32 changes: 27 additions & 5 deletions include/llama/mapping/AoS.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
namespace llama::mapping
{
/// Array of struct mapping. Used to create a \ref View via \ref allocView.
/// \tparam LinearizeArrayDomainFunctor Defines how the
/// user domain should be mapped into linear numbers and how big the linear
/// domain gets.
/// \tparam AlignAndPad If true, padding bytes are inserted to guarantee that struct members are properly aligned.
/// If false, struct members are tighly packed.
/// \tparam LinearizeArrayDomainFunctor Defines how the user domain
/// should be mapped into linear numbers and how big the linear domain gets.
template <
typename T_ArrayDomain,
typename T_DatumDomain,
bool AlignAndPad = false,
typename LinearizeArrayDomainFunctor = LinearizeArrayDomainCpp>
struct AoS
{
Expand All @@ -30,18 +32,38 @@ namespace llama::mapping

LLAMA_FN_HOST_ACC_INLINE constexpr auto getBlobSize(std::size_t) const -> std::size_t
{
return LinearizeArrayDomainFunctor{}.size(arrayDomainSize) * sizeOf<DatumDomain>;
return LinearizeArrayDomainFunctor{}.size(arrayDomainSize) * sizeOf<DatumDomain, AlignAndPad>;
}

template <std::size_t... DatumDomainCoord>
LLAMA_FN_HOST_ACC_INLINE constexpr auto getBlobNrAndOffset(ArrayDomain coord) const -> NrAndOffset
{
LLAMA_FORCE_INLINE_RECURSIVE
const auto offset = LinearizeArrayDomainFunctor{}(coord, arrayDomainSize)
* sizeOf<DatumDomain> + offsetOf<DatumDomain, DatumCoord<DatumDomainCoord...>>;
* sizeOf<DatumDomain,
AlignAndPad> + offsetOf<DatumDomain, DatumCoord<DatumDomainCoord...>, AlignAndPad>;
return {0, offset};
}

ArrayDomain arrayDomainSize;
};

template <
typename ArrayDomain,
typename DatumDomain,
typename LinearizeArrayDomainFunctor = LinearizeArrayDomainCpp>
using AlignedAoS = AoS<ArrayDomain, DatumDomain, true, LinearizeArrayDomainFunctor>;

template <
typename ArrayDomain,
typename DatumDomain,
typename LinearizeArrayDomainFunctor = LinearizeArrayDomainCpp>
using PackedAoS = AoS<ArrayDomain, DatumDomain, false, LinearizeArrayDomainFunctor>;

template <bool AlignAndPad = false, typename LinearizeArrayDomainFunctor = LinearizeArrayDomainCpp>
struct PreconfiguredAoS
{
template <typename ArrayDomain, typename DatumDomain>
using type = AoS<ArrayDomain, DatumDomain, AlignAndPad, LinearizeArrayDomainFunctor>;
};
} // namespace llama::mapping
66 changes: 61 additions & 5 deletions tests/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ namespace tag
struct Weight {};
}

using XYZ = llama::DS<
llama::DE<tag::X, double>,
llama::DE<tag::Y, double>,
llama::DE<tag::Z, double>
>;
using Particle = llama::DS<
llama::DE<tag::Pos, llama::DS<
llama::DE<tag::X, double>,
llama::DE<tag::Y, double>,
llama::DE<tag::Z, double>
>>,
llama::DE<tag::Pos, XYZ>,
llama::DE<tag::Weight, float>,
llama::DE<llama::NoName, int>,
llama::DE<tag::Vel,llama::DS<
Expand Down Expand Up @@ -117,8 +118,18 @@ TEST_CASE("prettyPrintType")

TEST_CASE("sizeOf")
{
STATIC_REQUIRE(llama::sizeOf<float> == 4);
STATIC_REQUIRE(llama::sizeOf<XYZ> == 24);
STATIC_REQUIRE(llama::sizeOf<Particle> == 52);
}

TEST_CASE("sizeOf.Align")
{
STATIC_REQUIRE(llama::sizeOf<float, true> == 4);
STATIC_REQUIRE(llama::sizeOf<XYZ, true> == 24);
STATIC_REQUIRE(llama::sizeOf<Particle, true> == 56);
}

TEST_CASE("offsetOf")
{
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<>> == 0);
Expand All @@ -138,6 +149,51 @@ TEST_CASE("offsetOf")
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<4, 3>> == 51);
}

TEST_CASE("offsetOf.Align")
{
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<>, true> == 0);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<0>, true> == 0);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<0, 0>, true> == 0);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<0, 1>, true> == 8);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<0, 2>, true> == 16);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<1>, true> == 24);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<2>, true> == 28);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<3>, true> == 32);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<3, 0>, true> == 32);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<3, 1>, true> == 40);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<4>, true> == 48);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<4, 0>, true> == 48);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<4, 1>, true> == 49);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<4, 2>, true> == 50);
STATIC_REQUIRE(llama::offsetOf<Particle, llama::DatumCoord<4, 3>, true> == 51);
}

template <int i>
struct S;

TEST_CASE("alignment")
{
using DD = llama::DS<
llama::DE<tag::X, float>,
llama::DE<tag::Y, double>,
llama::DE<tag::Z, bool>,
llama::DE<tag::Weight, std::uint16_t>>;

STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<>, false> == 0);
STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<0>, false> == 0);
STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<1>, false> == 4);
STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<2>, false> == 12);
STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<3>, false> == 13);
STATIC_REQUIRE(llama::sizeOf<DD, false> == 15);

STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<>, true> == 0);
STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<0>, true> == 0);
STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<1>, true> == 8); // aligned
STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<2>, true> == 16);
STATIC_REQUIRE(llama::offsetOf<DD, llama::DatumCoord<3>, true> == 18); // aligned
STATIC_REQUIRE(llama::sizeOf<DD, true> == 20);
}

TEST_CASE("GetCoordFromTags")
{
using namespace llama::literals;
Expand Down
Loading