Faster http::string_to_field:

* Optimize hash calculation and equality for field strings

Experiments show the digest and equality functions taking the
most time. Optimizing L1 cache usage did not show significant
improvements.
This commit is contained in:
Peter Dimov
2019-02-02 15:27:39 +02:00
committed by Vinnie Falco
parent 150614cc9a
commit e0db595760
2 changed files with 84 additions and 84 deletions

View File

@@ -1,3 +1,9 @@
Version 209:
* Faster http::string_to_field
--------------------------------------------------------------------------------
Version 208: Version 208:
* Add get_lowest_layer free function * Add get_lowest_layer free function

View File

@@ -15,6 +15,7 @@
#include <array> #include <array>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <cstring>
#include <boost/assert.hpp> #include <boost/assert.hpp>
namespace boost { namespace boost {
@@ -28,58 +29,66 @@ struct field_table
using array_type = using array_type =
std::array<string_view, 353>; std::array<string_view, 353>;
struct hash // Strings are converted to lowercase
static
std::uint32_t
digest(string_view s)
{ {
std::size_t std::uint32_t r = 0;
operator()(string_view s) const std::size_t n = s.size();
unsigned char const* p =reinterpret_cast<
unsigned char const*>(s.data());
while(n >= 4)
{ {
auto const n = s.size(); std::uint32_t v;
return std::memcpy(&v, p, 4);
beast::detail::ascii_tolower(s[0]) * r = r * 5 + ( v | 0x20202020 );
beast::detail::ascii_tolower(s[n/2]) ^ p += 4;
beast::detail::ascii_tolower(s[n-1]); // hist[] = 331, 10, max_load_factor = 0.15f n -= 4;
} }
}; while( n > 0 )
{
r = r * 5 + ( *p | 0x20 );
++p;
--n;
}
return r;
}
struct iequal // This comparison is case-insensitive, and the
// strings must contain only valid http field characters.
static
bool
equals(string_view lhs, string_view rhs)
{ {
// assumes inputs have equal length using Int = std::uint32_t; // or std::size_t
bool auto n = lhs.size();
operator()( if(n != rhs.size())
string_view lhs, return false;
string_view rhs) const auto p1 = lhs.data();
auto p2 = rhs.data();
auto constexpr S = sizeof(Int);
auto constexpr Mask = static_cast<Int>(
0xDFDFDFDFDFDFDFDF & ~Int{0});
for(; n >= S; p1 += S, p2 += S, n -= S)
{ {
auto p1 = lhs.data(); Int v1, v2;
auto p2 = rhs.data(); std::memcpy( &v1, p1, S );
auto pend = p1 + lhs.size(); std::memcpy( &v2, p2, S );
char a, b; if((v1 ^ v2) & Mask)
while(p1 < pend) return false;
{
a = *p1++;
b = *p2++;
if(a != b)
goto slow;
}
return true;
while(p1 < pend)
{
slow:
if( beast::detail::ascii_tolower(a) !=
beast::detail::ascii_tolower(b))
return false;
a = *p1++;
b = *p2++;
}
return true;
} }
}; for(; n; ++p1, ++p2, --n)
if(( *p1 ^ *p2) & 0xDF)
using map_type = std::unordered_map< return false;
string_view, field, hash, iequal>; return true;
}
array_type by_name_; array_type by_name_;
std::vector<map_type> by_size_;
enum { N = 5155 };
unsigned char map_[ N ][ 2 ] = {};
/* /*
From: From:
@@ -442,58 +451,43 @@ struct field_table
"Xref" "Xref"
}}) }})
{ {
// find the longest field length for(std::size_t i = 1, n = 256; i < n; ++i)
std::size_t high = 0;
for(auto const& s : by_name_)
if(high < s.size())
high = s.size();
// build by_size map
// skip field::unknown
by_size_.resize(high + 1);
for(auto& map : by_size_)
map.max_load_factor(.15f);
for(std::size_t i = 1;
i < by_name_.size(); ++i)
{ {
auto const& s = by_name_[i]; auto sv = by_name_[ i ];
by_size_[s.size()].emplace( auto h = digest(sv);
s, static_cast<field>(i)); auto j = h % N;
BOOST_ASSERT(map_[j][0] == 0);
map_[j][0] = static_cast<unsigned char>(i);
} }
#if 0 for(std::size_t i = 256, n = by_name_.size(); i < n; ++i)
// This snippet calculates the performance
// of the hash function and map settings
{ {
std::vector<std::size_t> hist; auto sv = by_name_[i];
for(auto const& map : by_size_) auto h = digest(sv);
{ auto j = h % N;
for(std::size_t i = 0; i < map.bucket_count(); ++i) BOOST_ASSERT(map_[j][1] == 0);
{ map_[j][1] = static_cast<unsigned char>(i - 255);
auto const n = map.bucket_size(i);
if(n > 0)
{
if(hist.size() < n)
hist.resize(n);
++hist[n-1];
}
}
}
} }
#endif
} }
field field
string_to_field(string_view s) const string_to_field(string_view s) const
{ {
if(s.size() >= by_size_.size()) auto h = digest(s);
auto j = h % N;
int i = map_[j][0];
string_view s2 = by_name_[i];
if(i != 0 && equals(s, s2))
return static_cast<field>(i);
i = map_[j][1];
if(i == 0)
return field::unknown; return field::unknown;
auto const& map = by_size_[s.size()]; i += 255;
if(map.empty()) s2 = by_name_[i];
return field::unknown;
auto it = map.find(s); if(equals(s, s2))
if(it == map.end()) return static_cast<field>(i);
return field::unknown; return field::unknown;
return it->second;
} }
// //