Skip to content

Commit

Permalink
Extracted pass_in_reg machinery to a dedicated header.
Browse files Browse the repository at this point in the history
Added more documentation links.
Minor debug build bug fixes.
  • Loading branch information
psiha committed Oct 28, 2024
1 parent ace0b4b commit fedd814
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 85 deletions.
107 changes: 107 additions & 0 deletions include/psi/vm/containers/abi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#pragma once

#include <psi/build/disable_warnings.hpp>

#include <boost/config.hpp>

#include <ranges>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
//------------------------------------------------------------------------------
namespace psi::vm
{
//------------------------------------------------------------------------------

PSI_WARNING_DISABLE_PUSH()
PSI_WARNING_MSVC_DISABLE( 5030 ) // unrecognized attribute

////////////////////////////////////////////////////////////////////////////////
// Modern(ized) attempt at 'automatized' boost::call_traits primarily to support
// efficient transparent comparators & non-inlined generic lookup functions
// which cause neither unnecessary copies of non-trivial types nor pass-by-ref
// of trivial ones.
// Largely still WiP...
// Essentially this is 'explicit IPA SROA'.
// https://gcc.gnu.org/onlinedocs/gccint/passes-and-files-of-the-compiler/inter-procedural-optimization-passes.html
////////////////////////////////////////////////////////////////////////////////

template <typename T>
bool constexpr can_be_passed_in_reg
{
(
std::is_trivial_v<T> &&
( sizeof( T ) <= 2 * sizeof( void * ) ) // assuming a sane ABI like SysV (ignoring the MS x64 disaster)
)
#if defined( __GNUC__ ) || defined( __clang__ )
|| // detect SIMD types (this could also produce false positives for large compiler-native vectors that do not fit into the register file)
requires{ __builtin_convertvector( T{}, T ); }
#endif
// This is certainly not an exhaustive list/'trait' - certain types that can
// be passed in reg cannot be detected as such by existing compiler
// functionality, e.g. Homogeneous Vector Aggregates
// https://devblogs.microsoft.com/cppblog/introducing-vector-calling-convention
// users are encouraged to provide specializations for such types.
}; // can_be_passed_in_reg

template <typename T>
struct optimal_const_ref { using type = T const &; };

template <typename Char>
struct optimal_const_ref<std::basic_string<Char>> { using type = std::basic_string_view<Char>; };

template <std::ranges::contiguous_range Rng>
struct optimal_const_ref<Rng> { using type = std::span<std::ranges::range_value_t<Rng> const>; };

template <typename T>
struct [[ clang::trivial_abi ]] pass_in_reg
{
static auto constexpr pass_by_val{ can_be_passed_in_reg<T> };

using value_type = T;
using stored_type = std::conditional_t<pass_by_val, T, typename optimal_const_ref<T>::type>;
BOOST_FORCEINLINE
constexpr pass_in_reg( auto const &... args ) noexcept requires requires { stored_type{ args... }; } : value{ args... } {}
constexpr pass_in_reg( pass_in_reg const & ) noexcept = default;
constexpr pass_in_reg( pass_in_reg && ) noexcept = default;

stored_type value;

[[ gnu::pure ]] BOOST_FORCEINLINE
constexpr operator stored_type const &() const noexcept { return value; }
}; // pass_in_reg
template <typename T>
pass_in_reg( T ) -> pass_in_reg<T>;

template <typename T>
struct [[ clang::trivial_abi ]] pass_rv_in_reg
{
static auto constexpr pass_by_val{ can_be_passed_in_reg<T> };

using value_type = T;
using stored_type = std::conditional_t<pass_by_val, T, T &&>;

constexpr pass_rv_in_reg( T && u ) noexcept : value{ std::move( u ) } {} // move for not-trivially-moveable yet trivial_abi types (that can be passed in reg)

stored_type value;

[[ gnu::pure ]] BOOST_FORCEINLINE constexpr operator stored_type const & () const & noexcept { return value ; }
[[ gnu::pure ]] BOOST_FORCEINLINE constexpr operator stored_type &&() && noexcept { return std::move( value ); }
}; // pass_rv_in_reg

template <typename T> bool constexpr reg { can_be_passed_in_reg<T> };
template <typename T> bool constexpr reg<pass_in_reg <T>>{ true };
template <typename T> bool constexpr reg<pass_rv_in_reg<T>>{ true };

template <typename T>
concept Reg = reg<T>;

// 'Explicit IPA SROA' / pass-in-reg helper end
////////////////////////////////////////////////////////////////////////////////

PSI_WARNING_DISABLE_POP()

//------------------------------------------------------------------------------
} // namespace psi::vm
//------------------------------------------------------------------------------
87 changes: 6 additions & 81 deletions include/psi/vm/containers/b+tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <psi/vm/align.hpp>
#include <psi/vm/allocation.hpp>
#include <psi/vm/containers/abi.hpp>
#include <psi/vm/vector.hpp>

#include <psi/build/disable_warnings.hpp>
Expand Down Expand Up @@ -43,91 +44,12 @@ namespace detail
}
} // namespace detail

////////////////////////////////////////////////////////////////////////////////
// Modern(ized) attempt at 'automatized' boost::call_traits primarily to support
// efficient transparent comparators & non-inlined generic lookup functions
// which cause neither unnecessary copies of non-trivial types nor pass-by-ref
// of trivial ones.
// Largely still WiP...
// Essentially this is 'explicit IPA SROA'.
// https://gcc.gnu.org/onlinedocs/gccint/passes-and-files-of-the-compiler/inter-procedural-optimization-passes.html
////////////////////////////////////////////////////////////////////////////////

template <typename T>
bool constexpr can_be_passed_in_reg
{
(
std::is_trivial_v<T> &&
( sizeof( T ) <= 2 * sizeof( void * ) ) // assuming a sane ABI like SysV (ignoring the MS x64 disaster)
)
#if defined( __GNUC__ ) || defined( __clang__ )
|| // detect SIMD types (this could also produce false positives for large compiler-native vectors that do not fit into the register file)
requires{ __builtin_convertvector( T{}, T ); }
#endif
// This is certainly not an exhaustive list/'trait' - certain types that can
// be passed in reg cannot be detected as such by existing compiler
// functionality, e.g. Homogeneous Vector Aggregates
// https://devblogs.microsoft.com/cppblog/introducing-vector-calling-convention
// users are encouraged to provide specializations for such types.
}; // can_be_passed_in_reg

template <typename T>
struct optimal_const_ref { using type = T const &; };

template <typename Char>
struct optimal_const_ref<std::basic_string<Char>> { using type = std::basic_string_view<char>; };

template <std::ranges::contiguous_range Rng>
struct optimal_const_ref<Rng> { using type = std::span<std::ranges::range_value_t<Rng> const>; };

template <typename T>
struct [[ clang::trivial_abi ]] pass_in_reg
{
static auto constexpr pass_by_val{ can_be_passed_in_reg<T> };

using value_type = T;
using stored_type = std::conditional_t<pass_by_val, T, typename optimal_const_ref<T>::type>;

constexpr pass_in_reg( T const & u ) noexcept : val{ u } {}

stored_type val;

[[ gnu::pure ]] BOOST_FORCEINLINE
constexpr operator stored_type const &() const noexcept { return val; }
}; // pass_in_reg

template <typename T>
struct [[ clang::trivial_abi ]] pass_rv_in_reg
{
static auto constexpr pass_by_val{ can_be_passed_in_reg<T> };

using value_type = T;
using stored_type = std::conditional_t<pass_by_val, T, T &&>;

constexpr pass_rv_in_reg( T && u ) noexcept : val{ std::move( u ) } {} // move for not-trivially-moveable yet trivial_abi types (that can be passed in reg)

stored_type val;

[[ gnu::pure ]] BOOST_FORCEINLINE constexpr operator stored_type const & () const noexcept { return val ; }
[[ gnu::pure ]] BOOST_FORCEINLINE constexpr operator stored_type &&() noexcept { return std::move( val ); }
}; // pass_rv_in_reg

template <typename K, bool transparent_comparator, typename StoredKeyType>
concept LookupType = transparent_comparator || std::is_same_v<StoredKeyType, K>;

template <typename K, bool transparent_comparator, typename StoredKeyType>
concept InsertableType = ( transparent_comparator && std::is_convertible_v<K, StoredKeyType> ) || std::is_same_v<StoredKeyType, K>;

template <typename T> bool constexpr reg { can_be_passed_in_reg<T> };
template <typename T> bool constexpr reg<pass_in_reg <T>>{ true };
template <typename T> bool constexpr reg<pass_rv_in_reg<T>>{ true };

template <typename T>
concept Reg = reg<T>;

// 'Explicit IPA SROA' / pass-in-reg helper end
////////////////////////////////////////////////////////////////////////////////


// user specializations are allowed and intended:

Expand Down Expand Up @@ -174,8 +96,11 @@ class bptree_base
{
storage_result success{ nodes_.map_file( file, policy ) };
# ifndef NDEBUG
p_hdr_ = &hdr();
p_nodes_ = nodes_.data();
if ( std::move( success ) )
{
p_hdr_ = &hdr();
p_nodes_ = nodes_.data();
}
# endif
if ( std::move( success ) && nodes_.empty() )
hdr() = {};
Expand Down
18 changes: 14 additions & 4 deletions src/containers/b+tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,26 @@ namespace psi::vm
// https://www.programiz.com/dsa/b-plus-tree
// https://courses.cs.washington.edu/courses/cse332/23su/lectures/9_B_Trees.pdf (version, as this impl, which has different key counts in inner vs leaf nodes)
// https://www.geeksforgeeks.org/b-trees-implementation-in-c
// https://github.com/jeffplaisance/BppTree
// https://flatcap.github.io/linux-ntfs/ntfs/concepts/tree/index.html
// https://benjamincongdon.me/blog/2021/08/17/B-Trees-More-Than-I-Thought-Id-Want-to-Know
// https://stackoverflow.com/questions/59362113/b-tree-minimum-internal-children-count-explanation
// https://web.archive.org/web/20190126073810/http://supertech.csail.mit.edu/cacheObliviousBTree.html
// https://www.researchgate.net/publication/220225482_Cache-Oblivious_Databases_Limitations_and_Opportunities
// https://www.postgresql.org/docs/current/btree.html
// https://abseil.io/about/design/btree
// https://www.scylladb.com/2021/11/23/the-taming-of-the-b-trees
// https://www.scattered-thoughts.net/writing/smolderingly-fast-btrees
// B+tree vs LSM-tree https://www.usenix.org/conference/fast22/presentation/qiao
// Data Structure Visualizations https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
// Griffin: Fast Transactional Database Index with Hash and B+-Tree https://ieeexplore.ieee.org/abstract/document/10678674
// Restructuring the concurrent B+-tree with non-blocked search operations https://www.sciencedirect.com/science/article/abs/pii/S002002550200261X
// Cache-Friendly Search Trees https://arxiv.org/pdf/1907.01631
// ZBTree: A Fast and Scalable B+-Tree for Persistent Memory https://ieeexplore.ieee.org/document/10638243

// https://abseil.io/about/design/btree
// https://github.com/tlx/tlx/tree/master/tlx/container
// https://github.com/jeffplaisance/BppTree
// https://github.com/postgres/postgres/blob/master/src/backend/access/nbtree/README
// https://github.com/scylladb/scylladb/blob/master/utils/intrusive_btree.hh

// https://en.wikipedia.org/wiki/Judy_array

Expand All @@ -50,8 +57,11 @@ bptree_base::map_memory( std::uint32_t const initial_capacity_as_number_of_nodes
{
storage_result success{ nodes_.map_memory( initial_capacity_as_number_of_nodes, value_init ) };
#ifndef NDEBUG
p_hdr_ = &hdr();
p_nodes_ = nodes_.data();
if ( std::move( success ) )
{
p_hdr_ = &hdr();
p_nodes_ = nodes_.data();
}
#endif
if ( std::move( success ) )
{
Expand Down

0 comments on commit fedd814

Please sign in to comment.