From f47772f6f4efcd46bc571882e5d971f7301dc7ec Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 16 Sep 2022 21:25:20 +0300 Subject: [PATCH] Implement faster hash_range when value_type is char --- .../container_hash/detail/hash_range.hpp | 158 ++++++++++++++++++ include/boost/container_hash/hash.hpp | 6 +- 2 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 include/boost/container_hash/detail/hash_range.hpp diff --git a/include/boost/container_hash/detail/hash_range.hpp b/include/boost/container_hash/detail/hash_range.hpp new file mode 100644 index 0000000..7450e1e --- /dev/null +++ b/include/boost/container_hash/detail/hash_range.hpp @@ -0,0 +1,158 @@ +// Copyright 2022 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_HASH_DETAIL_HASH_RANGE_HPP +#define BOOST_HASH_DETAIL_HASH_RANGE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ +namespace hash_detail +{ + +template struct is_char_type: public boost::false_type {}; + +#if CHAR_BIT == 8 + +template<> struct is_char_type: public boost::true_type {}; +template<> struct is_char_type: public boost::true_type {}; +template<> struct is_char_type: public boost::true_type {}; + +#endif + +template +inline typename boost::enable_if_< !is_char_type::value_type>::value >::type + hash_range( std::size_t& seed, It first, It last ) +{ + for( ; first != last; ++first ) + { + hash_combine::value_type>( seed, *first ); + } +} + +template +inline typename boost::enable_if_< + is_char_type::value_type>::value && + is_same::iterator_category, std::random_access_iterator_tag>::value +>::type + hash_range( std::size_t& seed, It first, It last ) +{ + std::size_t n = static_cast( last - first ); + + for( ; n >= 4; first += 4, n -= 4 ) + { + // clang 5+, gcc 5+ figure out this pattern and use a single mov on x86 + // gcc on s390x and power BE even knows how to use load-reverse + + boost::uint32_t w = + static_cast( static_cast( first[0] ) ) | + static_cast( static_cast( first[1] ) ) << 8 | + static_cast( static_cast( first[2] ) ) << 16 | + static_cast( static_cast( first[3] ) ) << 24; + + hash_combine( seed, w ); + } + + { + // add a trailing suffix byte of 0x01 because otherwise sequences of + // trailing zeroes are indistinguishable from end of string + + boost::uint32_t w = 0x01u; + + switch( n ) + { + case 1: + + w = + static_cast( static_cast( first[0] ) ) | + 0x0100u; + + break; + + case 2: + + w = + static_cast( static_cast( first[0] ) ) | + static_cast( static_cast( first[1] ) ) << 8 | + 0x010000u; + + break; + + case 3: + + w = + static_cast( static_cast( first[0] ) ) | + static_cast( static_cast( first[1] ) ) << 8 | + static_cast( static_cast( first[2] ) ) << 16 | + 0x01000000u; + + break; + } + + hash_combine( seed, w ); + } +} + +template +inline typename boost::enable_if_< + is_char_type::value_type>::value && + !is_same::iterator_category, std::random_access_iterator_tag>::value +>::type + hash_range( std::size_t& seed, It first, It last ) +{ + for( ;; ) + { + boost::uint32_t w = 0; + + if( first == last ) + { + hash_combine( seed, w | 0x01u ); + return; + } + + w |= static_cast( static_cast( *first ) ); + ++first; + + if( first == last ) + { + hash_combine( seed, w | 0x0100u ); + return; + } + + w |= static_cast( static_cast( *first ) ) << 8; + ++first; + + if( first == last ) + { + hash_combine( seed, w | 0x010000u ); + return; + } + + w |= static_cast( static_cast( *first ) ) << 16; + ++first; + + if( first == last ) + { + hash_combine( seed, w | 0x01000000u ); + return; + } + + w |= static_cast( static_cast( *first ) ) << 24; + ++first; + + hash_combine( seed, w ); + } +} + +} // namespace hash_detail +} // namespace boost + +#endif // #ifndef BOOST_HASH_DETAIL_HASH_RANGE_HPP diff --git a/include/boost/container_hash/hash.hpp b/include/boost/container_hash/hash.hpp index 090250b..56bfa01 100644 --- a/include/boost/container_hash/hash.hpp +++ b/include/boost/container_hash/hash.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -559,10 +560,7 @@ namespace boost template inline void hash_range( std::size_t& seed, It first, It last ) { - for( ; first != last; ++first ) - { - hash_combine::value_type>( seed, *first ); - } + hash_detail::hash_range( seed, first, last ); } template