Skip to content

Commit

Permalink
Added swap_columns<>()
Browse files Browse the repository at this point in the history
  • Loading branch information
marzer committed Aug 1, 2023
1 parent 498c4ba commit b5931ef
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 58 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Added `soagen::table_base`
- Added `soagen::iterator_base`
- Added `Base` template argument to `soagen::table` for CRTP
- Added `swap_columns<>()`
- Made `column_indices` member struct into `enum class columns`

## v0.1.2
Expand Down
14 changes: 14 additions & 0 deletions examples/entities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,20 @@ namespace soagen::examples
return *this;
}

/// @brief Swaps two columns.
///
/// @availability The two columns must have the same underlying value_type.
template <auto A, auto B>
SOAGEN_ALWAYS_INLINE
SOAGEN_CPP20_CONSTEXPR
entities& swap_columns() //
noexcept(noexcept(
std::declval<table_type&>().template swap_columns<static_cast<size_t>(A), static_cast<size_t>(B)>()))
{
table_.template swap_columns<static_cast<size_t>(A), static_cast<size_t>(B)>();
return *this;
}

#if SOAGEN_DOXYGEN

/// @brief Resizes the table to the given number of rows.
Expand Down
28 changes: 28 additions & 0 deletions examples/shapes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,20 @@ namespace soagen::examples
return *this;
}

/// @brief Swaps two columns.
///
/// @availability The two columns must have the same underlying value_type.
template <auto A, auto B>
SOAGEN_ALWAYS_INLINE
SOAGEN_CPP20_CONSTEXPR
boxes& swap_columns() //
noexcept(noexcept(
std::declval<table_type&>().template swap_columns<static_cast<size_t>(A), static_cast<size_t>(B)>()))
{
table_.template swap_columns<static_cast<size_t>(A), static_cast<size_t>(B)>();
return *this;
}

#if SOAGEN_DOXYGEN

/// @brief Resizes the table to the given number of rows.
Expand Down Expand Up @@ -1983,6 +1997,20 @@ namespace soagen::examples
return *this;
}

/// @brief Swaps two columns.
///
/// @availability The two columns must have the same underlying value_type.
template <auto A, auto B>
SOAGEN_ALWAYS_INLINE
SOAGEN_CPP20_CONSTEXPR
spheres& swap_columns() //
noexcept(noexcept(
std::declval<table_type&>().template swap_columns<static_cast<size_t>(A), static_cast<size_t>(B)>()))
{
table_.template swap_columns<static_cast<size_t>(A), static_cast<size_t>(B)>();
return *this;
}

#if SOAGEN_DOXYGEN

/// @brief Resizes the table to the given number of rows.
Expand Down
131 changes: 102 additions & 29 deletions src/soagen/hpp/single/soagen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3602,11 +3602,14 @@ namespace soagen::detail

// columns

template <size_t Index>
using column = type_at_index<Index, Columns...>;
template <auto Index>
using column = type_at_index<static_cast<size_t>(Index), Columns...>;

template <typename IndexConstant>
using column_from_ic = type_at_index<IndexConstant::value, Columns...>;
using column_from_ic = type_at_index<static_cast<size_t>(IndexConstant::value), Columns...>;

template <auto Index>
using storage_type = typename column<static_cast<size_t>(Index)>::storage_type;

using column_pointers = std::byte* [column_count];
using const_column_pointers = std::byte* const[column_count];
Expand Down Expand Up @@ -3831,7 +3834,7 @@ namespace soagen::detail
{
static constexpr size_t column_index = column_count - decltype(ic)::value - 1u;

if constexpr (!std::is_trivially_destructible_v<typename column<column_index>::storage_type>)
if constexpr (!std::is_trivially_destructible_v<storage_type<column_index>>)
{
SOAGEN_ASSUME(columns[column_index]);
SOAGEN_ASSUME(leftmost_column <= rightmost_column);
Expand Down Expand Up @@ -3865,7 +3868,7 @@ namespace soagen::detail
{
static constexpr size_t column_index = column_count - decltype(ic)::value - 1u;

if constexpr (!std::is_trivially_destructible_v<typename column<column_index>::storage_type>)
if constexpr (!std::is_trivially_destructible_v<storage_type<column_index>>)
{
SOAGEN_ASSUME(columns[column_index]);

Expand Down Expand Up @@ -3935,8 +3938,7 @@ namespace soagen::detail
size_t constructed_columns = {};

const auto constructor = [&](auto ic) //
noexcept(
std::is_nothrow_default_constructible_v<typename column<decltype(ic)::value>::storage_type>)
noexcept(std::is_nothrow_default_constructible_v<storage_type<decltype(ic)::value>>)
{
column_from_ic<decltype(ic)>::default_construct(columns[decltype(ic)::value], index);

Expand Down Expand Up @@ -4054,8 +4056,7 @@ namespace soagen::detail

const auto constructor =
[&](auto ic, auto&& arg) noexcept(
std::is_nothrow_constructible_v<typename column_from_ic<decltype(ic)>::storage_type,
decltype(arg)&&>)
std::is_nothrow_constructible_v<storage_type<decltype(ic)::value>, decltype(arg)&&>)
{
column_from_ic<decltype(ic)>::construct_at(columns[decltype(ic)::value],
index,
Expand Down Expand Up @@ -4126,8 +4127,7 @@ namespace soagen::detail
size_t constructed_columns = {};

const auto constructor =
[&](auto ic) noexcept(
std::is_nothrow_move_constructible_v<typename column_from_ic<decltype(ic)>::storage_type>)
[&](auto ic) noexcept(std::is_nothrow_move_constructible_v<storage_type<decltype(ic)::value>>)
{
column_from_ic<decltype(ic)>::move_construct(dest_columns[decltype(ic)::value],
dest_index,
Expand Down Expand Up @@ -4259,8 +4259,7 @@ namespace soagen::detail
size_t constructed_columns = {};

const auto constructor =
[&](auto ic) noexcept(
std::is_nothrow_copy_constructible_v<typename column_from_ic<decltype(ic)>::storage_type>)
[&](auto ic) noexcept(std::is_nothrow_copy_constructible_v<storage_type<decltype(ic)::value>>)
{
column_from_ic<decltype(ic)>::copy_construct(dest_columns[decltype(ic)::value],
dest_index,
Expand Down Expand Up @@ -4501,7 +4500,7 @@ namespace soagen::detail
}
}

//--- swap -----------------------------------------------------------------------------------------------------
//--- swap rows ------------------------------------------------------------------------------------------------

private:
template <typename... Args, size_t... Cols>
Expand Down Expand Up @@ -4531,6 +4530,35 @@ namespace soagen::detail
swap_rows(lhs_columns, lhs_index, rhs_columns, rhs_index, std::make_index_sequence<column_count>{});
}

//--- swap columns ---------------------------------------------------------------------------------------------

template <size_t A, size_t B>
static constexpr bool can_swap_columns =
A == B || (std::is_same_v<storage_type<A>, storage_type<B>> && column<A>::is_swappable);

template <size_t A, size_t B>
static constexpr bool can_nothrow_swap_columns =
A == B || (std::is_same_v<storage_type<A>, storage_type<B>> && column<A>::is_nothrow_swappable);

SOAGEN_HIDDEN_CONSTRAINT((can_swap_columns<A, B>), size_t A, size_t B)
SOAGEN_CPP20_CONSTEXPR
static void swap_columns([[maybe_unused]] column_pointers& columns,
[[maybe_unused]] size_t start,
[[maybe_unused]] size_t count) //
noexcept(can_nothrow_swap_columns<A, B>)
{
if constexpr (A != B)
{
static_assert(std::is_same_v<storage_type<A>, storage_type<B>>);
static_assert(column<A>::is_swappable);
static_assert(column<B>::is_swappable);

count += start;
for (; start < count; start++)
column<A>::swap(columns[A], start, columns[B], start);
}
}

//--- equality -------------------------------------------------------------------------------------------------

private:
Expand Down Expand Up @@ -4647,7 +4675,7 @@ namespace soagen
using column = type_at_index<static_cast<size_t>(Index), Columns...>;

template <typename IndexConstant>
using column_from_ic = type_at_index<IndexConstant::value, Columns...>;
using column_from_ic = type_at_index<static_cast<size_t>(IndexConstant::value), Columns...>;

static constexpr size_t column_alignments[column_count] = { Columns::alignment... };

Expand Down Expand Up @@ -4796,22 +4824,26 @@ namespace soagen::detail
return true;
};

inline static constexpr size_t min_actual_column_alignment =
max(size_t{ __STDCPP_DEFAULT_NEW_ALIGNMENT__ }, alignof(std::max_align_t), size_t{ 16 });

// trait for determining the _actual_ alignment of a table column, taking the allocator and
// table-allocation semantics into account (since the full allocation for a table always has
// alignment == table_traits::largest_alignment).
//
// note that this has absolutely nothing to do with the aligned_stride; that is still calculated
// according to the user's specified alignment requirements. this trait is _only_ used
// to help the compiler via assume_aligned.
template <typename Traits, typename Allocator, auto ColumnIndex>
template <typename Traits, typename Allocator, size_t Column>
inline constexpr size_t actual_column_alignment =
Traits::template column<static_cast<size_t>(ColumnIndex)>::alignment;
max(Traits::template column<Column>::alignment, min_actual_column_alignment);

template <typename Traits, typename Allocator>
inline constexpr size_t actual_column_alignment<Traits, Allocator, 0> =
max(Traits::template column<0>::alignment,
allocator_traits<Allocator>::min_alignment,
Traits::largest_alignment);
Traits::largest_alignment,
min_actual_column_alignment);

//------------------------------------------------------------------------------------------------------------------
// generic allocation class for tracking the column pointers and the actual size in bytes
Expand Down Expand Up @@ -4961,6 +4993,7 @@ namespace soagen::detail
allocator(),
n_bytes,
std::align_val_t{ max(alignment, allocator_traits<Allocator>::min_alignment) }));

SOAGEN_ASSUME(ptr != nullptr);
std::memset(ptr, 0, n_bytes);

Expand Down Expand Up @@ -5202,12 +5235,11 @@ namespace soagen::detail
size_t prev = {};
for (size_t i = 0; i < Traits::column_count - 1u; i++)
{
ends[i] = prev + Traits::column_sizes[i] * capacity;
ends[i] = (ends[i] + Traits::column_alignments[i + 1u] - 1u) //
& ~(Traits::column_alignments[i + 1u] - 1u);
const auto align = max(Traits::column_alignments[i + 1u], min_actual_column_alignment);

SOAGEN_ASSUME(ends[i] % Traits::column_alignments[i + 1u] == 0u);
prev = ends[i];
ends[i] = prev + Traits::column_sizes[i] * capacity;
ends[i] = (ends[i] + align - 1u) & ~(align - 1u);
prev = ends[i];
}

// last end doesn't need to be aligned (it's just the total buffer size)
Expand All @@ -5219,15 +5251,12 @@ namespace soagen::detail
{
SOAGEN_ASSUME(ends[Traits::column_count - 1u]);

auto alloc = base::allocate(ends[Traits::column_count - 1u], Traits::largest_alignment);
auto alloc = base::allocate(ends[Traits::column_count - 1u], actual_column_alignment<Traits, Allocator, 0>);
SOAGEN_ASSUME(alloc.columns[0]);
SOAGEN_ASSUME(alloc.size_in_bytes == ends[Traits::column_count - 1u]);

for (size_t i = 1; i < Traits::column_count; i++)
{
alloc.columns[i] = alloc.columns[0] + ends[i - 1u];
SOAGEN_ASSUME(reinterpret_cast<uintptr_t>(alloc.columns[i]) % Traits::column_alignments[i] == 0u);
}

return alloc;
}
Expand Down Expand Up @@ -5429,9 +5458,11 @@ namespace soagen::detail
{
if (i)
{
if (const size_t rem = buf_end % Traits::column_alignments[i - 1u]; rem > 0u)
const auto align = max(Traits::column_alignments[i - 1u], min_actual_column_alignment);

if (const size_t rem = buf_end % align; rem > 0u)
{
if (!add_without_overflowing(buf_end, Traits::column_alignments[i - 1u] - rem, buf_end))
if (!add_without_overflowing(buf_end, align - rem, buf_end))
return false;
}
}
Expand Down Expand Up @@ -5553,6 +5584,48 @@ namespace soagen::detail

base::count_++;
}

private:
template <size_t A, size_t B>
static constexpr bool can_swap_columns =
Traits::template can_swap_columns<A, B>
|| (std::is_same_v<typename Traits::template storage_type<A>, typename Traits::template storage_type<B>>
&& actual_column_alignment<Traits, Allocator, A> == actual_column_alignment<Traits, Allocator, B>);

template <size_t A, size_t B>
static constexpr bool can_nothrow_swap_columns =
Traits::template can_nothrow_swap_columns<A, B>
|| (std::is_same_v<typename Traits::template storage_type<A>, typename Traits::template storage_type<B>>
&& actual_column_alignment<Traits, Allocator, A> == actual_column_alignment<Traits, Allocator, B>);

public:
template <size_t A, size_t B>
SOAGEN_CPP20_CONSTEXPR
void swap_columns() //
noexcept(can_nothrow_swap_columns<A, B>)
{
static_assert(can_swap_columns<A, B>);

if constexpr (A != B)
{
using storage_a = typename Traits::template storage_type<A>;
using storage_b = typename Traits::template storage_type<B>;
static_assert(std::is_same_v<storage_a, storage_b>);

// if they have the same base alignment, we can just swap the two pointers
// rather than having to do an element-wise swap
if constexpr (actual_column_alignment<Traits, Allocator, A>
== actual_column_alignment<Traits, Allocator, B>)
{
std::swap(base::alloc_.columns[A], base::alloc_.columns[B]);
}

else
{
Traits::template swap_columns<A, B>(base::alloc_.columns, {}, base::count_);
}
}
}
};

//------------------------------------------------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit b5931ef

Please sign in to comment.