Skip to content

Commit

Permalink
support dumping computed mappings
Browse files Browse the repository at this point in the history
Fixes #423
  • Loading branch information
bernhardmgruber committed Dec 8, 2021
1 parent 574b967 commit caf422e
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 13 deletions.
140 changes: 127 additions & 13 deletions include/llama/DumpMapping.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,23 @@

# include <boost/functional/hash.hpp>
# include <fmt/format.h>
# include <optional>
# include <string>
# include <vector>

namespace llama
{
namespace internal
{
template<typename Mapping>
constexpr auto hasAnyComputedField() -> bool
{
bool computed = false;
forEachLeafCoord<typename Mapping::RecordDim>([&](auto rc)
{ computed |= llama::isComputed<Mapping, decltype(rc)>; });
return computed;
}

template<std::size_t... Coords>
auto toVec(RecordCoord<Coords...>) -> std::vector<std::size_t>
{
Expand Down Expand Up @@ -57,25 +67,116 @@ namespace llama
std::size_t size;
};

template<typename View>
void fillBlobsWithPattern(View& view, uint8_t pattern)
{
const auto& mapping = view.mapping();
for(std::size_t i = 0; i < View::Mapping::blobCount; i++)
std::memset(&view.storageBlobs[i][0], pattern, mapping.blobSize(i));
}

template<typename View, typename RecordCoord>
void boxesFromComputedField(
View& view,
typename View::Mapping::ArrayIndex ai,
RecordCoord rc,
std::vector<FieldBox<View::Mapping::ArrayIndex::rank>>& infos)
{
using Mapping = typename View::Mapping;
using RecordDim = typename Mapping::RecordDim;

auto emitInfo = [&](NrAndOffset nrAndOffset, std::size_t size) {
infos.push_back({ai, internal::toVec(rc), recordCoordTags<RecordDim>(rc), nrAndOffset, size});
};

using Type = GetType<RecordDim, decltype(rc)>;
// computed values can come from anywhere, so we can only apply heuristics
auto& blobs = view.storageBlobs;
auto&& ref = view.mapping().compute(ai, rc, blobs);

// try to find the mapped address in one of the blobs
if constexpr(std::is_reference_v<decltype(ref)>)
{
auto address = reinterpret_cast<std::intptr_t>(&ref);
for(std::size_t i = 0; i < blobs.size(); i++)
{
// TODO: this is UB, because we are comparing pointers from unrelated
// allocations
const auto front = reinterpret_cast<std::intptr_t>(&blobs[i][0]);
const auto back = reinterpret_cast<std::intptr_t>(&blobs[i][view.mapping().blobSize(i) - 1]);
if(front <= address && address <= back)
{
emitInfo(NrAndOffset{i, static_cast<std::size_t>(address - front)}, sizeof(Type));
return; // a mapping can only map to one location in the blobs
}
}
}

if constexpr(std::is_default_constructible_v<Type>)
{
const auto infosBefore = infos.size();

// try to observe written bytes
const auto pattern = std::uint8_t{0xAA};
fillBlobsWithPattern(view, pattern);
ref = Type{}; // a broad range of types is default constructible and should write
// zero bytes
auto wasTouched = [&](auto b) { return static_cast<std::uint8_t>(b) != pattern; };
for(std::size_t i = 0; i < Mapping::blobCount; i++)
{
const auto blobSize = view.mapping().blobSize(i);
const auto* begin = &blobs[i][0];
const auto* end = begin + blobSize;

auto* searchBegin = begin;
while(true)
{
const auto* touchedBegin = std::find_if(searchBegin, end, wasTouched);
if(touchedBegin == end)
break;
const auto& touchedEnd = std::find_if_not(touchedBegin + 1, end, wasTouched);
emitInfo(
NrAndOffset{i, static_cast<std::size_t>(touchedBegin - begin)},
touchedEnd - touchedBegin);
if(touchedEnd == end)
break;
searchBegin = touchedEnd + 1;
}
}

if(infosBefore != infos.size())
return;
}

// if we come here, we could not find out where the value is coming from
emitInfo(NrAndOffset{Mapping::blobCount, 0}, sizeof(Type));
}

template<typename Mapping>
auto boxesFromMapping(const Mapping& mapping) -> std::vector<FieldBox<Mapping::ArrayIndex::rank>>
{
std::vector<FieldBox<Mapping::ArrayIndex::rank>> infos;

std::optional<decltype(allocView(mapping))> view;
if constexpr(hasAnyComputedField<Mapping>())
view = allocView(mapping);

using RecordDim = typename Mapping::RecordDim;
for(auto ai : ArrayIndexRange{mapping.extents()})
{
forEachLeafCoord<RecordDim>(
[&](auto rc)
{
infos.push_back(
{ai,
internal::toVec(rc),
recordCoordTags<RecordDim>(rc),
mapping.blobNrAndOffset(ai, rc),
sizeof(GetType<RecordDim, decltype(rc)>)});
using Type = GetType<RecordDim, decltype(rc)>;
if constexpr(llama::isComputed<Mapping, decltype(rc)>)
boxesFromComputedField(view.value(), ai, rc, infos);
else
infos.push_back(
{ai,
internal::toVec(rc),
recordCoordTags<RecordDim>(rc),
mapping.blobNrAndOffset(ai, rc),
sizeof(Type)});
});
}

return infos;
}
Expand Down Expand Up @@ -122,8 +223,9 @@ namespace llama

std::string svg;

std::array<int, Mapping::blobCount + 1> blobYOffset{};
for(std::size_t i = 0; i < Mapping::blobCount; i++)
const auto hasAnyComputedField = internal::hasAnyComputedField<Mapping>();
std::array<int, Mapping::blobCount + hasAnyComputedField + 1> blobYOffset{};
for(std::size_t i = 0; i < Mapping::blobCount + hasAnyComputedField; i++)
{
const auto blobRows = (mapping.blobSize(i) + wrapByteCount - 1) / wrapByteCount;
blobYOffset[i + 1] = blobYOffset[i] + (blobRows + 1) * byteSizeInPixel; // one row gap between blobs
Expand All @@ -137,7 +239,7 @@ namespace llama
height,
blobBlockWidth / 2,
blobYOffset[i] + height / 2,
i);
i < Mapping::blobCount ? "Blob: " + std::to_string(i) : "Computed");
}

svg = fmt::format(
Expand All @@ -152,11 +254,23 @@ namespace llama
byteSizeInPixel / 2)
+ svg;

std::size_t computedSizeSoFar = 0;
for(const auto& info : infos)
{
const auto blobY = blobYOffset[info.nrAndOffset.nr];
auto x = (info.nrAndOffset.offset % wrapByteCount) * byteSizeInPixel + blobBlockWidth;
auto y = (info.nrAndOffset.offset / wrapByteCount) * byteSizeInPixel + blobY;
const auto offset = [&]
{
if(info.nrAndOffset.nr < Mapping::blobCount)
return info.nrAndOffset.offset;
else
{
const auto offset = computedSizeSoFar;
computedSizeSoFar += info.size;
return offset;
}
}();
auto x = (offset % wrapByteCount) * byteSizeInPixel + blobBlockWidth;
auto y = (offset / wrapByteCount) * byteSizeInPixel + blobY;
const auto fill = internal::color(info.recordCoord);
const auto width = byteSizeInPixel * info.size;

Expand Down
19 changes: 19 additions & 0 deletions tests/dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,25 @@ TEST_CASE("dump.Particle.Split.AoSoA8.AoS.One.SoA")
true>{extents});
}

TEST_CASE("dump.Particle.BitPacked")
{
dump(llama::mapping::Split<
ArrayExtents,
Particle,
llama::RecordCoord<0>,
llama::mapping::BitPackedFloatSoA,
llama::mapping::PreconfiguredSplit<
llama::RecordCoord<0>,
llama::mapping::PackedOne,
llama::mapping::PreconfiguredSplit<
llama::RecordCoord<0>,
llama::mapping::BitPackedFloatSoA,
llama::mapping::BitPackedIntSoA,
true>::type,
true>::type,
true>{{3, 3, extents}, {{}, {{5, 5, extents}, {1, extents}}}});
}

TEST_CASE("dump.ParticleUnaligned.AoS")
{
dump(llama::mapping::PackedAoS<ArrayExtents, ParticleUnaligned>{extents});
Expand Down

0 comments on commit caf422e

Please sign in to comment.