Skip to content

Commit

Permalink
support multiple selecting record coords in Split
Browse files Browse the repository at this point in the history
  • Loading branch information
bernhardmgruber committed Oct 29, 2021
1 parent 1239f8f commit a459f9a
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 46 deletions.
93 changes: 53 additions & 40 deletions include/llama/mapping/Split.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,65 @@ namespace llama::mapping
auto partitionRecordDim(Record<Fields...>, RecordCoord<FirstCoord, Coords...>)
{
using namespace boost::mp11;
using Rec = Record<Fields...>;
if constexpr(sizeof...(Coords) == 0)
{
using With = Record<mp_at_c<Record<Fields...>, FirstCoord>>;
using Without = mp_erase_c<Record<Fields...>, FirstCoord, FirstCoord + 1>;
return mp_list<With, Without>{};
using Part1 = Record<mp_at_c<Rec, FirstCoord>>;
using Part2 = mp_erase_c<Rec, FirstCoord, FirstCoord + 1>;
return mp_list<Part1, Part2>{};
}
else
{
using Result = decltype(partitionRecordDim(
Record<mp_at_c<Record<Fields...>, FirstCoord>>{},
RecordCoord<Coords...>{}));
using With = mp_replace_at_c<Record<Fields...>, FirstCoord, mp_first<Result>>;
using Without = mp_replace_at_c<Record<Fields...>, FirstCoord, mp_second<Result>>;
return mp_list<With, Without>{};
using FieldTag = GetTag<Rec, RecordCoord<FirstCoord>>;
using FieldType = GetType<Rec, RecordCoord<FirstCoord>>;
using InnerPartition = decltype(partitionRecordDim(FieldType{}, RecordCoord<Coords...>{}));
using Part1 = Record<Field<FieldTag, mp_first<InnerPartition>>>;
using Part2 = mp_replace_at_c<Rec, FirstCoord, Field<FieldTag, mp_second<InnerPartition>>>;
return mp_list<Part1, Part2>{};
}
}

template<
std::size_t FirstDstCoord,
std::size_t... DstCoords,
std::size_t FirstSkippedCoord,
std::size_t... SkippedCoords>
constexpr auto offsetCoord(
RecordCoord<FirstDstCoord, DstCoords...>,
RecordCoord<FirstSkippedCoord, SkippedCoords...>)
template<typename Acc, typename TagList>
struct PartitionFoldOpImpl
{
if constexpr(FirstDstCoord < FirstSkippedCoord)
return RecordCoord<FirstDstCoord, DstCoords...>{};
else if constexpr(FirstDstCoord > FirstSkippedCoord)
return RecordCoord<FirstDstCoord - 1, DstCoords...>{};
else
return cat(
RecordCoord<FirstDstCoord>{},
offsetCoord(RecordCoord<DstCoords...>{}, RecordCoord<SkippedCoords...>{}));
using Part1Before = boost::mp11::mp_first<Acc>;
using Part2Before = boost::mp11::mp_second<Acc>;
using R = decltype(partitionRecordDim(Part2Before{}, GetCoordFromTags<Part2Before, TagList>{}));
using Part1After = boost::mp11::mp_first<R>;
using Part2After = boost::mp11::mp_second<R>;

using type = boost::mp11::mp_list<MergedRecordDims<Part1Before, Part1After>, Part2After>;
};

template<typename Acc, typename TagList>
using PartitionFoldOp = typename PartitionFoldOpImpl<Acc, TagList>::type;

template<typename... Fields, typename... RCs>
auto partitionRecordDim(Record<Fields...>, boost::mp11::mp_list<RCs...>)
{
using namespace boost::mp11;
using Initial = mp_list<Record<>, Record<Fields...>>; // initially, nothing selected for mapping 1
return mp_fold<mp_list<mp_drop_c<GetTags<Record<Fields...>, RCs>, 1>...>, Initial, PartitionFoldOp>{};
}

template<typename RC, typename RecordCoordForMapping1>
inline constexpr bool isSelected = RecordCoordCommonPrefixIsSame<RecordCoordForMapping1, RC>;

template<typename RC>
struct IsSelectedPredicate
{
template<typename RecordCoordForMapping1>
using fn = boost::mp11::mp_bool<isSelected<RC, RecordCoordForMapping1>>;
};

template<typename RC, typename... RecordCoordsForMapping1>
inline constexpr bool isSelected<RC, boost::mp11::mp_list<RecordCoordsForMapping1...>> = boost::mp11::
mp_any_of_q<boost::mp11::mp_list<RecordCoordsForMapping1...>, IsSelectedPredicate<RC>>::value;
} // namespace internal

/// Mapping which splits off a part of the record dimension and maps it differently then the rest.
/// \tparam RecordCoordForMapping1 A \ref RecordCoord selecting the part of the record dimension to be mapped
/// differently.
/// \tparam RecordCoordForMapping1 A \ref RecordCoord or a list of RecordCoords selecting the part of the record
/// dimension to be mapped differently.
/// \tparam MappingTemplate1 The mapping used for the selected part of the record dimension.
/// \tparam MappingTemplate2 The mapping used for the not selected part of the record dimension.
/// \tparam SeparateBlobs If true, both pieces of the record dimension are mapped to separate blobs.
Expand Down Expand Up @@ -107,19 +126,13 @@ namespace llama::mapping
LLAMA_FN_HOST_ACC_INLINE constexpr auto blobNrAndOffset(ArrayIndex ai, RecordCoord<RecordCoords...> = {}) const
-> NrAndOffset
{
if constexpr(RecordCoordCommonPrefixIsSame<RecordCoordForMapping1, RecordCoord<RecordCoords...>>)
{
using namespace boost::mp11;
// zero all coordinate values that are part of RecordCoordForMapping1
using Prefix = mp_repeat_c<mp_list_c<std::size_t, 0>, RecordCoordForMapping1::size>;
using Suffix = mp_drop_c<mp_list_c<std::size_t, RecordCoords...>, RecordCoordForMapping1::size>;
return mapping1.blobNrAndOffset(ai, RecordCoordFromList<mp_append<Prefix, Suffix>>{});
}
using Tags = boost::mp11::mp_drop_c<GetTags<RecordDim, RecordCoord<RecordCoords...>>, 1>;

if constexpr(internal::isSelected<RecordCoord<RecordCoords...>, RecordCoordForMapping1>)
return mapping1.blobNrAndOffset(ai, GetCoordFromTags<RecordDim1, Tags>{});
else
{
constexpr auto dstCoord
= internal::offsetCoord(RecordCoord<RecordCoords...>{}, RecordCoordForMapping1{});
auto nrAndOffset = mapping2.blobNrAndOffset(ai, dstCoord);
auto nrAndOffset = mapping2.blobNrAndOffset(ai, GetCoordFromTags<RecordDim2, Tags>{});
if constexpr(SeparateBlobs)
nrAndOffset.nr += Mapping1::blobCount;
else
Expand All @@ -136,7 +149,7 @@ namespace llama::mapping
};

template<
typename RecordCoordForMapping1,
typename RecordCoordsForMapping1,
template<typename...>
typename MappingTemplate1,
template<typename...>
Expand All @@ -148,7 +161,7 @@ namespace llama::mapping
using type = Split<
ArrayExtents,
RecordDim,
RecordCoordForMapping1,
RecordCoordsForMapping1,
MappingTemplate1,
MappingTemplate2,
SeparateBlobs>;
Expand Down
12 changes: 12 additions & 0 deletions tests/dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ TEST_CASE("dump.ParticleUnaligned.Split.AoSoA8.SoA.One.AoS")
true>{extents});
}

TEST_CASE("dump.ParticleUnaligned.Split.Multilist.SoA.One")
{
// split out Pos and Vel into SoA, the rest into One
dump(llama::mapping::Split<
ArrayExtents,
Particle,
boost::mp11::mp_list<llama::RecordCoord<0>, llama::RecordCoord<2>>,
llama::mapping::PreconfiguredSoA<>::type,
llama::mapping::AlignedOne,
true>{extents});
}

TEST_CASE("AoS.Aligned")
{
const auto mapping = llama::mapping::AlignedAoS<ArrayExtents, ParticleUnaligned>{extents};
Expand Down
69 changes: 63 additions & 6 deletions tests/mapping.Split.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
#include <fstream>
#include <llama/DumpMapping.hpp>

TEST_CASE("Split.partitionRecordDim.OneMemberRecord")
{
using RecordDim = llama::Record<llama::Field<int, int>>;
using R = decltype(llama::mapping::internal::partitionRecordDim(RecordDim{}, llama::RecordCoord<0>{}));
STATIC_REQUIRE(std::is_same_v<boost::mp11::mp_first<R>, RecordDim>);
STATIC_REQUIRE(std::is_same_v<boost::mp11::mp_second<R>, llama::Record<>>);
}

TEST_CASE("Split.partitionRecordDim.Vec3I")
{
using R = decltype(llama::mapping::internal::partitionRecordDim(Vec3I{}, llama::RecordCoord<1>{}));
Expand All @@ -22,17 +30,25 @@ TEST_CASE("Split.partitionRecordDim.Particle")
std::is_same_v<
boost::mp11::mp_second<R>,
llama::Record<
llama::Field<
tag::Pos,
llama::Record<
llama::Field<tag::X, double>,
llama::Field<tag::Y, double>,
llama::Field<tag::Z, double>>>,
llama::Field<tag::Pos, Vec3D>,
llama::Field<tag::Mass, float>,
llama::Field<tag::Vel, llama::Record<llama::Field<tag::X, double>, llama::Field<tag::Z, double>>>,
llama::Field<tag::Flags, bool[4]>>>);
}

TEST_CASE("Split.partitionRecordDim.Particle.List")
{
using R = decltype(llama::mapping::internal::partitionRecordDim(
Particle{},
boost::mp11::mp_list<llama::RecordCoord<0>, llama::RecordCoord<2>>{}));
STATIC_REQUIRE(std::is_same_v<
boost::mp11::mp_first<R>,
llama::Record<llama::Field<tag::Pos, Vec3D>, llama::Field<tag::Vel, Vec3D>>>);
STATIC_REQUIRE(std::is_same_v<
boost::mp11::mp_second<R>,
llama::Record<llama::Field<tag::Mass, float>, llama::Field<tag::Flags, bool[4]>>>);
}

TEST_CASE("Split.SoA_SingleBlob.AoS_Packed.1Buffer")
{
using ArrayExtents = llama::ArrayExtentsDynamic<2>;
Expand Down Expand Up @@ -106,3 +122,44 @@ TEST_CASE("Split.AoSoA8.AoS_Packed.One.SoA_SingleBlob.4Buffer")

// std::ofstream{"Split.AoSoA8.AoS.One.SoA.4Buffer.svg"} << llama::toSvg(mapping);
}


TEST_CASE("Split.Multilist.SoA.One")
{
// split out Pos and Vel into SoA, the rest into One
using ArrayExtents = llama::ArrayExtentsDynamic<1>;
auto extents = ArrayExtents{32};
auto mapping = llama::mapping::Split<
ArrayExtents,
Particle,
boost::mp11::mp_list<llama::RecordCoord<0>, llama::RecordCoord<2>>,
llama::mapping::PreconfiguredSoA<>::type,
llama::mapping::AlignedOne,
true>{extents};

CHECK(mapping.blobNrAndOffset<0, 0>({0}) == llama::NrAndOffset{0, 0});
CHECK(mapping.blobNrAndOffset<0, 1>({0}) == llama::NrAndOffset{1, 0});
CHECK(mapping.blobNrAndOffset<0, 2>({0}) == llama::NrAndOffset{2, 0});
CHECK(mapping.blobNrAndOffset<1>({0}) == llama::NrAndOffset{6, 0});
CHECK(mapping.blobNrAndOffset<2, 0>({0}) == llama::NrAndOffset{3, 0});
CHECK(mapping.blobNrAndOffset<2, 1>({0}) == llama::NrAndOffset{4, 0});
CHECK(mapping.blobNrAndOffset<2, 2>({0}) == llama::NrAndOffset{5, 0});
CHECK(mapping.blobNrAndOffset<3, 0>({0}) == llama::NrAndOffset{6, 4});
CHECK(mapping.blobNrAndOffset<3, 1>({0}) == llama::NrAndOffset{6, 5});
CHECK(mapping.blobNrAndOffset<3, 2>({0}) == llama::NrAndOffset{6, 6});
CHECK(mapping.blobNrAndOffset<3, 3>({0}) == llama::NrAndOffset{6, 7});

CHECK(mapping.blobNrAndOffset<0, 0>({31}) == llama::NrAndOffset{0, 31 * 8});
CHECK(mapping.blobNrAndOffset<0, 1>({31}) == llama::NrAndOffset{1, 31 * 8});
CHECK(mapping.blobNrAndOffset<0, 2>({31}) == llama::NrAndOffset{2, 31 * 8});
CHECK(mapping.blobNrAndOffset<1>({31}) == llama::NrAndOffset{6, 0});
CHECK(mapping.blobNrAndOffset<2, 0>({31}) == llama::NrAndOffset{3, 31 * 8});
CHECK(mapping.blobNrAndOffset<2, 1>({31}) == llama::NrAndOffset{4, 31 * 8});
CHECK(mapping.blobNrAndOffset<2, 2>({31}) == llama::NrAndOffset{5, 31 * 8});
CHECK(mapping.blobNrAndOffset<3, 0>({31}) == llama::NrAndOffset{6, 4});
CHECK(mapping.blobNrAndOffset<3, 1>({31}) == llama::NrAndOffset{6, 5});
CHECK(mapping.blobNrAndOffset<3, 2>({31}) == llama::NrAndOffset{6, 6});
CHECK(mapping.blobNrAndOffset<3, 3>({31}) == llama::NrAndOffset{6, 7});

// std::ofstream{"Split.AoSoA8.AoS.One.SoA.4Buffer.svg"} << llama::toSvg(mapping);
}

0 comments on commit a459f9a

Please sign in to comment.