diff --git a/mod/Math/Array.cxxm b/mod/Math/Array.cxxm index e1bb32f..96ff0aa 100644 --- a/mod/Math/Array.cxxm +++ b/mod/Math/Array.cxxm @@ -576,11 +576,13 @@ protected: TRIVIAL constexpr Array(S s) : sz_(s) {} // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) const storage_type *ptr_; + +public: // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) [[no_unique_address]] S sz_; private: - friend constexpr auto ssize(const Array &x) -> std::ptrdiff_t { + TRIVIAL friend constexpr auto ssize(const Array &x) -> std::ptrdiff_t { return x.size(); } }; diff --git a/mod/Math/Constraints.cxxm b/mod/Math/Constraints.cxxm index 6c0a8af..a41caa2 100644 --- a/mod/Math/Constraints.cxxm +++ b/mod/Math/Constraints.cxxm @@ -74,13 +74,8 @@ constexpr auto indsZeroNegPos(Array a) for (std::ptrdiff_t j = 0; j < a.size(); ++j) ret[orderedCmp(a[j])].insert(j); return ret; } -#ifdef __APPLE__ -// 4*4 + 8 + 8*2 = 40 -static_assert(sizeof(std::array, 2>) == 80); -#else // 4*4 + 8 + 4*2 = 32 static_assert(sizeof(std::array, 2>) == 64); -#endif template auto fourierMotzkinCore(MutDensePtrMatrix B, diff --git a/mod/Math/ManagedArray.cxxm b/mod/Math/ManagedArray.cxxm index c0bc344..8ef3d63 100644 --- a/mod/Math/ManagedArray.cxxm +++ b/mod/Math/ManagedArray.cxxm @@ -711,21 +711,12 @@ static_assert(sizeof(ManagedArray, 64, static_assert(sizeof(ManagedArray, 64, alloc::Mallocator>) == 536); -#ifdef __APPLE__ -template >()> -using Vector = ManagedArray, N>; -// Apple pads out `Vector` to 24B anyway, so may as well use full-size -static_assert(sizeof(ManagedArray, 0>) == - 24); -static_assert(sizeof(Vector) == 24); -#else +static_assert( + !std::is_standard_layout_v>>); template >()> using Vector = ManagedArray, N>; static_assert(sizeof(Vector) == 16); -#endif template >()> diff --git a/mod/Math/SOA.cxxm b/mod/Math/SOA.cxxm index 8bf9b0d..8cef672 100644 --- a/mod/Math/SOA.cxxm +++ b/mod/Math/SOA.cxxm @@ -1,4 +1,5 @@ module; +#include "Macros.hxx" export module SOA; import Allocator; import Array; @@ -44,52 +45,57 @@ namespace CapacityCalculators { /// Indicates that capacity == length struct Length { - static constexpr auto operator()(auto sz) -> std::ptrdiff_t { + TRIVIAL static constexpr auto operator()(auto sz) -> std::ptrdiff_t { return static_cast(sz); } - constexpr Length() = default; - constexpr Length(auto) {}; + TRIVIAL constexpr Length() = default; + TRIVIAL constexpr Length(auto){}; /// args are old nz, new nz /// returns `0` if not growing - constexpr auto oldnewcap(auto osz, auto nsz) + TRIVIAL constexpr auto oldnewcap(auto osz, auto nsz) -> std::array { return {(*this)(osz), (*this)(nsz)}; } - constexpr auto operator=(std::ptrdiff_t) -> Length & { return *this; } + TRIVIAL constexpr auto operator=(std::ptrdiff_t) -> Length & { return *this; } }; template constexpr auto nextpow2(I x) -> I { return static_cast(std::bit_ceil(static_cast>(x))); } struct NextPow2 { - static constexpr auto operator()(auto sz) -> std::ptrdiff_t { + TRIVIAL static constexpr auto operator()(auto sz) -> std::ptrdiff_t { std::ptrdiff_t i = std::ptrdiff_t(sz); return i ? std::max(8Z, nextpow2(i)) : 0Z; } - constexpr NextPow2() = default; - constexpr NextPow2(auto) {}; + TRIVIAL constexpr NextPow2() = default; + TRIVIAL constexpr NextPow2(auto){}; /// args are old nz, new nz /// returns `0` if not growing - constexpr auto oldnewcap(auto osz, auto nsz) + TRIVIAL constexpr auto oldnewcap(auto osz, auto nsz) -> std::array { return {(*this)(osz), (*this)(nsz)}; } - constexpr auto operator=(std::ptrdiff_t) -> NextPow2 & { return *this; } + TRIVIAL constexpr auto operator=(std::ptrdiff_t) -> NextPow2 & { + return *this; + } }; struct Explicit { std::int32_t capacity_{0}; - constexpr Explicit() = default; - constexpr Explicit(std::int32_t sz) + TRIVIAL constexpr Explicit() = default; + TRIVIAL constexpr Explicit(std::int32_t sz) : capacity_{((sz > 8) ? nextpow2(sz) : 8 * (sz > 0))} {} - constexpr Explicit(::math::Length<> sz) + TRIVIAL constexpr Explicit(::math::Length<> sz) : Explicit(std::int32_t(std::ptrdiff_t(sz))) {} - constexpr auto operator()(auto) const -> std::ptrdiff_t { return capacity_; } - constexpr auto operator=(std::int32_t cap) -> Explicit & { + TRIVIAL constexpr auto operator()(auto) const -> std::ptrdiff_t { + return capacity_; + } + TRIVIAL constexpr auto operator=(std::int32_t cap) -> Explicit & { capacity_ = cap; return *this; } /// args are old nz, new nz /// returns `0` if not growing - constexpr auto oldnewcap(auto, auto sz) -> std::array { + TRIVIAL constexpr auto oldnewcap(auto, auto sz) + -> std::array { return {capacity_, nextpow2(sz)}; } }; @@ -121,38 +127,38 @@ template struct SOAReference { char *ptr_; std::ptrdiff_t stride_; std::ptrdiff_t i_; - operator T() const { + TRIVIAL operator T() const { char *p = std::assume_aligned<8>(ptr_); return T(*reinterpret_cast *>( p + (CumSizeOf_v * stride_) + (sizeof(std::tuple_element_t) * i_))...); } - template void assign(const T &x) { + template TRIVIAL void assign(const T &x) { char *p = std::assume_aligned<8>(ptr_); *reinterpret_cast *>( p + (CumSizeOf_v * stride_) + (sizeof(std::tuple_element_t) * i_)) = x.template get(); } - constexpr SOAReference(char *p, std::ptrdiff_t s, std::ptrdiff_t idx) + TRIVIAL constexpr SOAReference(char *p, std::ptrdiff_t s, std::ptrdiff_t idx) : ptr_{p}, stride_{s}, i_{idx} {} - constexpr SOAReference(const SOAReference &) = default; - auto operator=(const T &x) -> SOAReference & { + TRIVIAL constexpr SOAReference(const SOAReference &) = default; + TRIVIAL auto operator=(const T &x) -> SOAReference & { ((void)assign(x), ...); // assign(x); return *this; } - auto operator=(SOAReference x) -> SOAReference & { + TRIVIAL auto operator=(SOAReference x) -> SOAReference & { (*this) = T(x); return *this; } - template auto get() -> std::tuple_element_t & { + template TRIVIAL auto get() -> std::tuple_element_t & { invariant(i_ >= 0); return *reinterpret_cast *>( ptr_ + (CumSizeOf_v * stride_) + (sizeof(std::tuple_element_t) * i_)); } template - auto get() const -> const std::tuple_element_t & { + TRIVIAL auto get() const -> const std::tuple_element_t & { invariant(i_ >= 0); return *reinterpret_cast *>( ptr_ + (CumSizeOf_v * stride_) + @@ -173,30 +179,39 @@ template requires(CanConstructFromMembers::value) struct SOA, std::index_sequence> { +protected: char *data_; + +public: [[no_unique_address]] S sz_; [[no_unique_address]] C capacity_; - static constexpr bool trivial = - std::is_trivially_default_constructible_v && - std::is_trivially_destructible_v; - static_assert(trivial); + + static_assert(std::is_trivially_default_constructible_v && + std::is_trivially_destructible_v); using value_type = T; using reference_type = SOAReference; - auto operator[](std::ptrdiff_t i) const -> T { + TRIVIAL constexpr SOA(char *data, S sz, C capacity) + : data_{data}, sz_{sz}, capacity_{capacity} {} + TRIVIAL constexpr SOA(const SOA &) = default; + + TRIVIAL auto operator[](std::ptrdiff_t i) const -> T { char *p = std::assume_aligned<8>(data_); std::ptrdiff_t stride = capacity_(sz_); return T(*reinterpret_cast *>( p + (CumSizeOf_v * stride) + (sizeof(std::tuple_element_t) * i))...); } - auto operator[](std::ptrdiff_t i) -> reference_type { + TRIVIAL auto operator[](std::ptrdiff_t i) -> reference_type { return {data_, capacity_(sz_), i}; } - [[nodiscard]] constexpr auto size() const -> std::ptrdiff_t { + [[nodiscard]] TRIVIAL constexpr auto size() const -> std::ptrdiff_t { return std::ptrdiff_t(sz_); } + [[nodiscard]] TRIVIAL constexpr auto capacity() const -> std::ptrdiff_t { + return std::ptrdiff_t(capacity_(sz_)); + } template - auto get(std::ptrdiff_t i) -> std::tuple_element_t & { + TRIVIAL auto get(std::ptrdiff_t i) -> std::tuple_element_t & { invariant(i >= 0); invariant(i < size()); return *reinterpret_cast *>( @@ -204,7 +219,8 @@ struct SOA, std::index_sequence> { (sizeof(std::tuple_element_t) * i)); } template - auto get(std::ptrdiff_t i) const -> const std::tuple_element_t & { + TRIVIAL auto get(std::ptrdiff_t i) const + -> const std::tuple_element_t & { invariant(i >= 0); invariant(i < size()); return *reinterpret_cast *>( @@ -212,13 +228,13 @@ struct SOA, std::index_sequence> { (sizeof(std::tuple_element_t) * i)); } template - auto get() -> math::MutArray, S> { + TRIVIAL auto get() -> math::MutArray, S> { return {reinterpret_cast *>( data_ + (CumSizeOf_v * capacity_(sz_))), sz_}; } template - auto get() const -> math::Array, S> { + TRIVIAL auto get() const -> math::Array, S> { return {reinterpret_cast *>( data_ + (CumSizeOf_v * capacity_(sz_))), sz_}; @@ -228,54 +244,57 @@ struct SOA, std::index_sequence> { RefType soa_; std::ptrdiff_t i_; - constexpr auto operator*() { return soa_[i_]; } - constexpr auto operator->() { return &soa_[i_]; } - constexpr auto operator++() -> Iterator { + TRIVIAL constexpr auto operator*() { return soa_[i_]; } + TRIVIAL constexpr auto operator->() { return &soa_[i_]; } + TRIVIAL constexpr auto operator++() -> Iterator { ++i_; return *this; } - constexpr auto operator--() -> Iterator { + TRIVIAL constexpr auto operator--() -> Iterator { --i_; return *this; } - constexpr auto operator++(int) -> Iterator { + TRIVIAL constexpr auto operator++(int) -> Iterator { Iterator it = *this; ++i_; return it; } - constexpr auto operator--(int) -> Iterator { + TRIVIAL constexpr auto operator--(int) -> Iterator { Iterator it = *this; --i_; return it; } private: - friend constexpr auto operator+(Iterator x, std::ptrdiff_t y) -> Iterator { + TRIVIAL friend constexpr auto operator+(Iterator x, std::ptrdiff_t y) + -> Iterator { x.i_ += y; return x; } - friend constexpr auto operator+(std::ptrdiff_t y, Iterator x) -> Iterator { + TRIVIAL friend constexpr auto operator+(std::ptrdiff_t y, Iterator x) + -> Iterator { x.i_ += y; return x; } - friend constexpr auto operator-(Iterator x, std::ptrdiff_t y) -> Iterator { + TRIVIAL friend constexpr auto operator-(Iterator x, std::ptrdiff_t y) + -> Iterator { x.i_ -= y; return x; } - friend constexpr auto operator==(Iterator x, Iterator y) -> bool { + TRIVIAL friend constexpr auto operator==(Iterator x, Iterator y) -> bool { return x.i_ == y.i_; } - friend constexpr auto operator<=>(Iterator x, Iterator y) + TRIVIAL friend constexpr auto operator<=>(Iterator x, Iterator y) -> std::strong_ordering { return x.i_ <=> y.i_; } }; - constexpr auto begin() -> Iterator { return {*this, 0z}; } - constexpr auto end() -> Iterator { + TRIVIAL constexpr auto begin() -> Iterator { return {*this, 0z}; } + TRIVIAL constexpr auto end() -> Iterator { return {*this, std::ptrdiff_t(sz_)}; } - constexpr auto begin() const -> Iterator { return {*this, 0z}; } - constexpr auto end() const -> Iterator { + TRIVIAL constexpr auto begin() const -> Iterator { return {*this, 0z}; } + TRIVIAL constexpr auto end() const -> Iterator { return {*this, std::ptrdiff_t(sz_)}; } @@ -301,41 +320,36 @@ template , struct ManagedSOA : public SOA { using Base = SOA; /// uninitialized allocation - constexpr ManagedSOA() : Base{nullptr, S{}, C{}} {} - ManagedSOA(S nsz) { - this->sz_ = nsz; - this->capacity_ = C(nsz); - std::ptrdiff_t stride = this->capacity_(std::ptrdiff_t(this->sz_)); - this->data_ = nsz ? alloc(stride) : nullptr; + TRIVIAL constexpr ManagedSOA() : Base{nullptr, S{}, C{}} {} + ManagedSOA(S nsz) : Base{nullptr, nsz, C(nsz)} { + if (nsz) this->data_ = alloc(this->capacity_(std::ptrdiff_t(this->sz_))); } - constexpr ManagedSOA(std::type_identity) : ManagedSOA() {} + TRIVIAL constexpr ManagedSOA(std::type_identity) : ManagedSOA() {} ManagedSOA(std::type_identity, S nsz) : ManagedSOA(nsz) {} - ManagedSOA(const ManagedSOA &other) { - this->sz_ = other.sz_; - this->capacity_ = C{this->sz_}; + ManagedSOA(const ManagedSOA &other) : Base{nullptr, other.sz_, C{other.sz_}} { if (std::ptrdiff_t L = std::ptrdiff_t(this->sz_)) { this->data_ = alloc(this->capacity_(L)); for (std::ptrdiff_t i = 0z; i < L; ++i) (*this)[i] = other[i]; - } else this->data_ = nullptr; + } } static auto alloc(std::ptrdiff_t stride) -> char * { return A::allocate(stride * Base::total_size_per); } ~ManagedSOA() { free(this->capacity_(this->sz_)); } - constexpr ManagedSOA(ManagedSOA &&other) + TRIVIAL constexpr ManagedSOA(ManagedSOA &&other) : SOA{other.data_, other.sz_, other.capacity_} { other.data_ = nullptr; other.sz_ = {}; other.capacity_ = {}; } - constexpr auto operator=(ManagedSOA &&other) -> ManagedSOA & { + TRIVIAL constexpr auto operator=(ManagedSOA &&other) -> ManagedSOA & { std::swap(this->data_, other.data_); std::swap(this->sz_, other.sz_); std::swap(this->capacity_, other.capacity_); return *this; } - constexpr auto operator=(const ManagedSOA &other) -> ManagedSOA & { + TRIVIAL constexpr auto operator=(const ManagedSOA &other) -> ManagedSOA & { if (this == &other) return *this; auto L = static_cast(other.sz_); resizeForOverwrite(other.sz_); @@ -373,7 +387,7 @@ struct ManagedSOA : public SOA { void resize(std::ptrdiff_t nsz) requires(RowVectorDimension) { resize(length(nsz)); } - constexpr void clear() { this->sz_ = {}; } + TRIVIAL constexpr void clear() { this->sz_ = {}; } /// This does not actually construct in place, as we must /// deconstruct the object to store it into the SOA. template void emplace_back(Args &&...args) { diff --git a/test/soa_test.cpp b/test/soa_test.cpp index 8d3dbfe..1bdc89c 100644 --- a/test/soa_test.cpp +++ b/test/soa_test.cpp @@ -11,7 +11,7 @@ import Tuple; // NOLINTNEXTLINE(modernize-use-trailing-return-type) int main() { - "SOATest BasicAssertions"_test = [] { + "SOATest BasicAssertions"_test = [] -> void { containers::Tuple x{3, 2.0, 5.0F}; // static_assert(math::CumSizeOf_v<0, decltype(x)> == 0); // static_assert(math::CumSizeOf_v<1, decltype(x)> == 4); @@ -25,7 +25,8 @@ int main() { static_assert(std::is_trivially_destructible_v); ::math::ManagedSOA soa(std::type_identity{}, ::math::length(5z)); - expect(soa.capacity_.capacity_ == 8); + expect(soa.capacity() == 8); + static_assert(!std::is_standard_layout_v); soa[0] = x; soa[1] = {5, 2.25, 5.5F}; soa.template get<0>(2) = 7; @@ -90,18 +91,19 @@ int main() { expect(soa.size() == 65); }; - "SOAPairTest BasicAssertions"_test = [] { + "SOAPairTest BasicAssertions"_test = [] -> void { containers::Pair x{3, 2.0}; + using Tup = decltype(x); // static_assert(math::CumSizeOf_v<0, decltype(x)> == 0); // static_assert(math::CumSizeOf_v<1, decltype(x)> == 4); // static_assert(math::CumSizeOf_v<2, decltype(x)> == 12); // math::ManagedSOA soa{std::type_identity{}, 5}; - ::math::ManagedSOA soa; + ::math::ManagedSOA soa; // math::ManagedSOA soa(std::type_identity{}); - expect(soa.capacity_.capacity_ == 0); + expect(soa.capacity() == 0); soa.push_back(x); soa[0] = x; - expect(soa.capacity_.capacity_ == 8); + expect(soa.capacity() == 8); soa.resize(5); soa[1] = {5, 2.25}; soa.template get<0>(2) = 7; @@ -109,8 +111,9 @@ int main() { soa.template get<0>()[3] = 9; soa.template get<1>()[3] = 2.75; soa[4] = {11, 3.0}; + auto soa_copy = soa; for (std::ptrdiff_t j = 0; j < 5; ++j) { - decltype(x) y = soa[j]; + Tup y = soa[j]; auto [i, d] = y; static_assert(std::same_as); static_assert(std::same_as); @@ -118,6 +121,8 @@ int main() { expect(d == (2.0 + 0.25 * j)); expect(i == soa.get<0>(j)); expect(d == soa.get<1>(j)); + expect(i == soa_copy.get<0>(j)); + expect(d == soa_copy.get<1>(j)); } soa.resize(7); soa[5] = {13, 3.25}; @@ -151,7 +156,7 @@ int main() { expect(soa.size() == 65); }; - "VecOfSOATest BasicAssertions"_test = [] { + "VecOfSOATest BasicAssertions"_test = [] -> void { ::math::Vector<::math::ManagedSOA>> vsoa; vsoa.emplace_back();