Skip to content

Commit

Permalink
CarpetX: consolidate poison values into a single class
Browse files Browse the repository at this point in the history
  • Loading branch information
rhaas80 committed Dec 27, 2024
1 parent 852808f commit 49de23b
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 63 deletions.
142 changes: 79 additions & 63 deletions CarpetX/src/valid.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <cctk_Parameters.h>

#include <algorithm>
#include <cstdint>
#include <cstring>
#include <functional>
#include <limits>
Expand Down Expand Up @@ -153,13 +152,6 @@ void warn_if_invalid(const GHExt::GlobalData::ArrayGroupData &groupdata, int vi,

// Poison values to catch uninitialized variables

#if defined CCTK_REAL_PRECISION_4
constexpr std::uint32_t ipoison = 0xffc00000UL + 0xdead;
#elif defined CCTK_REAL_PRECISION_8
constexpr std::uint64_t ipoison = 0xfff8000000000000ULL + 0xdeadbeef;
#endif
static_assert(sizeof ipoison == sizeof(CCTK_REAL));

// Poison grid functions
void poison_invalid_gf(const active_levels_t &active_levels, const int gi,
const int vi, const int tl) {
Expand All @@ -170,8 +162,9 @@ void poison_invalid_gf(const active_levels_t &active_levels, const int gi,
static Timer timer("poison_invalid<GF>");
Interval interval(timer);

const poison_value_t<CCTK_REAL> poison_value;
CCTK_REAL poison;
std::memcpy(&poison, &ipoison, sizeof poison);
poison_value.set_to_poison(poison);

active_levels.loop_parallel([&](const int patch, const int level,
const int index, const int component,
Expand Down Expand Up @@ -237,20 +230,35 @@ void poison_invalid_ga(const int gi, const int vi, const int tl) {
if (valid.valid_all())
return;

const size_t typesize = size_t(CCTK_VarTypeSize(group.vartype));
char poison[typesize];
// for int: deadbeef for little endian machines
for (size_t i = 0; i < typesize; i += sizeof poison)
std::memcpy(&poison[i], &ipoison, std::min(sizeof poison, typesize - i));

if (!valid.valid_int) {
int dimension = arraygroupdata.dimension;
const int *gsh = arraygroupdata.gsh;
int n_elems = 1;
for (int i = 0; i < dimension; i++)
n_elems *= gsh[i];
for (int i = 0; i < n_elems; i++)
memcpy(arraygroupdata.data.at(tl).data_at(vi + i), poison, typesize);
assert(group.vartype == CCTK_VARIABLE_COMPLEX ||
group.vartype == CCTK_VARIABLE_REAL ||
group.vartype == CCTK_VARIABLE_INT);
switch (group.vartype) {
case CCTK_VARIABLE_COMPLEX: {
CCTK_COMPLEX *restrict ptr = static_cast<CCTK_COMPLEX *>(
arraygroupdata.data.at(tl).data_at(vi * n_elems));
const poison_value_t<CCTK_COMPLEX> poison_value;
poison_value.set_to_poison(ptr, n_elems);
} break;
case CCTK_VARIABLE_REAL: {
CCTK_REAL *restrict ptr = static_cast<CCTK_REAL *>(
arraygroupdata.data.at(tl).data_at(vi * n_elems));
const poison_value_t<CCTK_REAL> poison_value;
poison_value.set_to_poison(ptr, n_elems);
} break;
case CCTK_VARIABLE_INT: {
CCTK_INT *restrict ptr = static_cast<CCTK_INT *>(
arraygroupdata.data.at(tl).data_at(vi * n_elems));
const poison_value_t<CCTK_INT> poison_value;
poison_value.set_to_poison(ptr, n_elems);
} break;
}
}
}

Expand All @@ -271,13 +279,8 @@ void check_valid_gf(const active_levels_t &active_levels, const int gi,

const auto is_poison = [] CCTK_DEVICE CCTK_HOST(
const CCTK_REAL val) CCTK_ATTRIBUTE_ALWAYS_INLINE {
#if defined CCTK_REAL_PRECISION_4
std::uint32_t ival;
#elif defined CCTK_REAL_PRECISION_8
std::uint64_t ival;
#endif
std::memcpy(&ival, &val, sizeof ival);
if (ival == ipoison)
poison_value_t<CCTK_REAL> const poison_value;
if (poison_value.is_poison(val))
return true;
using std::isnan;
if (nan_handling != nan_handling_t::allow_nans && isnan(val))
Expand Down Expand Up @@ -470,7 +473,7 @@ void check_valid_gf(const active_levels_t &active_levels, const int gi,

// Ensure arrays are not poisoned
void check_valid_ga(const int gi, const int vi, const int tl,
const nan_handling_t nan_handling,
const nan_handling_t nan_handling1,
const std::function<std::string()> &msg) {
DECLARE_CCTK_PARAMETERS;
if (!poison_undefined_values)
Expand All @@ -481,51 +484,64 @@ void check_valid_ga(const int gi, const int vi, const int tl,

auto &restrict globaldata = ghext->globaldata;
auto &restrict arraygroupdata = *globaldata.arraygroupdata.at(gi);
const valid_t &valid = arraygroupdata.valid.at(tl).at(vi).get();

// arrays have no boundary so we expect them to alway be valid
assert(valid.valid_outer && valid.valid_ghosts);
if (!valid.valid_int)
return;

cGroup group;
int ierr = CCTK_GroupData(gi, &group);
assert(!ierr);

std::size_t nan_count{0};

const valid_t &valid = arraygroupdata.valid.at(tl).at(vi).get();
if (valid.valid_all())
return;

const size_t typesize = size_t(CCTK_VarTypeSize(group.vartype));
char poison[typesize];
// for int: deadbeef for little endian machines
for (size_t i = 0; i < typesize; i += sizeof poison)
std::memcpy(&poison[i], &ipoison, std::min(sizeof poison, typesize - i));
#warning "TODO"
using std::isnan;
constexpr nan_handling_t nan_handling = nan_handling_t::forbid_nans;

// arrays have no boundary so we expect them to alway be valid
assert(valid.valid_outer && valid.valid_ghosts);
std::size_t nan_count{0};

if (valid.valid_int) {
int dimension = arraygroupdata.dimension;
const int *gsh = arraygroupdata.gsh;
int n_elems = 1;
for (int i = 0; i < dimension; i++)
n_elems *= gsh[i];
if (group.vartype == CCTK_VARIABLE_REAL) {
const CCTK_REAL *restrict const ptr = static_cast<const CCTK_REAL *const>(
arraygroupdata.data.at(tl).data_at(vi));
for (int i = 0; i < n_elems; i++) {
using std::isnan;
if (CCTK_BUILTIN_EXPECT(nan_handling == nan_handling_t::allow_nans
? std::memcmp(ptr, poison, sizeof *ptr) == 0
: isnan(*ptr),
false))
++nan_count;
}
} else {
for (int i = 0; i < n_elems; i++) {
if (CCTK_BUILTIN_EXPECT(
std::memcmp(arraygroupdata.data.at(tl).data_at(vi + i), poison,
typesize) == 0,
false))
++nan_count;
}
int dimension = arraygroupdata.dimension;
const int *gsh = arraygroupdata.gsh;
int n_elems = 1;
for (int i = 0; i < dimension; i++)
n_elems *= gsh[i];
switch (group.vartype) {
case CCTK_VARIABLE_COMPLEX: {
const poison_value_t<CCTK_COMPLEX> poison_value;
const CCTK_COMPLEX *restrict const ptr =
static_cast<const CCTK_COMPLEX *const>(
arraygroupdata.data.at(tl).data_at(vi * n_elems));
for (int i = 0; i < n_elems; i++) {
if (CCTK_BUILTIN_EXPECT(
poison_value.is_poison(ptr[i]) ||
(nan_handling != nan_handling_t::allow_nans &&
(isnan(ptr[i].real()) || isnan(ptr[i].imag()))),
false))
++nan_count;
}
} break;
case CCTK_VARIABLE_REAL: {
const poison_value_t<CCTK_REAL> poison_value;
const CCTK_REAL *restrict const ptr = static_cast<const CCTK_REAL *const>(
arraygroupdata.data.at(tl).data_at(vi * n_elems));
for (int i = 0; i < n_elems; i++) {
if (CCTK_BUILTIN_EXPECT(
poison_value.is_poison(ptr[i]) ||
(nan_handling != nan_handling_t::allow_nans && isnan(ptr[i])),
false))
++nan_count;
}
} break;
case CCTK_VARIABLE_INT: {
const poison_value_t<CCTK_INT> poison_value;
const CCTK_INT *restrict const ptr = static_cast<const CCTK_INT *const>(
arraygroupdata.data.at(tl).data_at(vi * n_elems));
for (int i = 0; i < n_elems; i++) {
if (CCTK_BUILTIN_EXPECT(poison_value.is_poison(ptr[i]), false))
++nan_count;
}
} break;
}

if (nan_count == 0)
Expand Down
66 changes: 66 additions & 0 deletions CarpetX/src/valid.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <yaml-cpp/yaml.h>
#include <zlib.h>

#include <cstdint>
#include <functional>
#include <iomanip>
#include <iostream>
Expand Down Expand Up @@ -227,6 +228,71 @@ checksums_t calculate_checksums(
void check_checksums(const checksums_t &checksums,
const std::function<std::string()> &where);

////////////////////////////////////////////////////////////////////////////////

template <typename T> struct ipoison_t;

template <> struct ipoison_t<CCTK_COMPLEX> {
#if defined CCTK_REAL_PRECISION_4
const std::uint32_t value[2] = {0xffc00000UL + 0xdead, 0xffc00000UL + 0xdead};
#elif defined CCTK_REAL_PRECISION_8
const std::uint64_t value[2] = {0xfff8000000000000ULL + 0xdeadbeef,
0xfff8000000000000ULL + 0xdeadbeef};
#endif
static_assert(sizeof value == sizeof(CCTK_COMPLEX));
};

template <> struct ipoison_t<CCTK_REAL> {
#if defined CCTK_REAL_PRECISION_4
const std::uint32_t value[1] = {0xffc00000UL + 0xdead};
#elif defined CCTK_REAL_PRECISION_8
const std::uint64_t value[1] = {0xfff8000000000000ULL + 0xdeadbeef};
#endif
static_assert(sizeof value == sizeof(CCTK_REAL));
};

template <> struct ipoison_t<CCTK_INT> {
const std::uint32_t value[1] = {0xdeadbeef};
static_assert(sizeof value == sizeof(CCTK_INT));
};

template <typename T> class poison_value_t {
public:
poison_value_t() = default;

CCTK_HOST CCTK_DEVICE bool is_poison(const T &val) const {
// no memcmp on CUDA :-(
// return std::memcmp(&val, &ipoison.value, sizeof(val)) == 0;

typedef decltype(ipoison.value) array_type;
typedef typename std::remove_extent<array_type>::type const_type;
const size_t array_size = std::extent<array_type>::value;

const_type *ival = reinterpret_cast<const_type *>(&val);
bool retval = false;
for (size_t i = 0; i < array_size; ++i)
if (ival[i] == ipoison.value[i])
retval = true;
return retval;
}

void set_to_poison(T &val) const {
std::memcpy(static_cast<void *>(&val), &ipoison.value, sizeof(val));
}

void set_to_poison(void *val, size_t count) const {
set_to_poison(reinterpret_cast<T *>(val), count);
}

void set_to_poison(T *val, size_t count) const {
for (size_t i = 0; i < count; ++i)
set_to_poison(val[i]);
}

private:
const ipoison_t<T> ipoison;
};

} // namespace CarpetX

#endif // #ifndef CARPETX_CARPETX_VALID_HXX

0 comments on commit 49de23b

Please sign in to comment.