diff --git a/include/psi/vm/containers/abi.hpp b/include/psi/vm/containers/abi.hpp new file mode 100644 index 0000000..3bc3d29 --- /dev/null +++ b/include/psi/vm/containers/abi.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +//------------------------------------------------------------------------------ +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 +bool constexpr can_be_passed_in_reg +{ + ( + std::is_trivial_v && + ( 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 +struct optimal_const_ref { using type = T const &; }; + +template +struct optimal_const_ref> { using type = std::basic_string_view; }; + +template +struct optimal_const_ref { using type = std::span const>; }; + +template +struct [[ clang::trivial_abi ]] pass_in_reg +{ + static auto constexpr pass_by_val{ can_be_passed_in_reg }; + + using value_type = T; + using stored_type = std::conditional_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 +pass_in_reg( T ) -> pass_in_reg; + +template +struct [[ clang::trivial_abi ]] pass_rv_in_reg +{ + static auto constexpr pass_by_val{ can_be_passed_in_reg }; + + using value_type = T; + using stored_type = std::conditional_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 bool constexpr reg { can_be_passed_in_reg }; +template bool constexpr reg>{ true }; +template bool constexpr reg>{ true }; + +template +concept Reg = reg; + +// 'Explicit IPA SROA' / pass-in-reg helper end +//////////////////////////////////////////////////////////////////////////////// + +PSI_WARNING_DISABLE_POP() + +//------------------------------------------------------------------------------ +} // namespace psi::vm +//------------------------------------------------------------------------------ diff --git a/include/psi/vm/containers/b+tree.hpp b/include/psi/vm/containers/b+tree.hpp index 6cf7d58..ba830e2 100644 --- a/include/psi/vm/containers/b+tree.hpp +++ b/include/psi/vm/containers/b+tree.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -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 -bool constexpr can_be_passed_in_reg -{ - ( - std::is_trivial_v && - ( 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 -struct optimal_const_ref { using type = T const &; }; - -template -struct optimal_const_ref> { using type = std::basic_string_view; }; - -template -struct optimal_const_ref { using type = std::span const>; }; - -template -struct [[ clang::trivial_abi ]] pass_in_reg -{ - static auto constexpr pass_by_val{ can_be_passed_in_reg }; - - using value_type = T; - using stored_type = std::conditional_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 -struct [[ clang::trivial_abi ]] pass_rv_in_reg -{ - static auto constexpr pass_by_val{ can_be_passed_in_reg }; - - using value_type = T; - using stored_type = std::conditional_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 concept LookupType = transparent_comparator || std::is_same_v; template concept InsertableType = ( transparent_comparator && std::is_convertible_v ) || std::is_same_v; -template bool constexpr reg { can_be_passed_in_reg }; -template bool constexpr reg>{ true }; -template bool constexpr reg>{ true }; - -template -concept Reg = reg; - -// 'Explicit IPA SROA' / pass-in-reg helper end -//////////////////////////////////////////////////////////////////////////////// - // user specializations are allowed and intended: @@ -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() = {}; diff --git a/src/containers/b+tree.cpp b/src/containers/b+tree.cpp index efe91eb..7a60074 100644 --- a/src/containers/b+tree.cpp +++ b/src/containers/b+tree.cpp @@ -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 @@ -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 ) ) {