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.
// assumes inputs have equal length static
bool bool
operator()( equals(string_view lhs, string_view rhs)
string_view lhs,
string_view rhs) const
{ {
using Int = std::uint32_t; // or std::size_t
auto n = lhs.size();
if(n != rhs.size())
return false;
auto p1 = lhs.data(); auto p1 = lhs.data();
auto p2 = rhs.data(); auto p2 = rhs.data();
auto pend = p1 + lhs.size(); auto constexpr S = sizeof(Int);
char a, b; auto constexpr Mask = static_cast<Int>(
while(p1 < pend) 0xDFDFDFDFDFDFDFDF & ~Int{0});
for(; n >= S; p1 += S, p2 += S, n -= S)
{ {
a = *p1++; Int v1, v2;
b = *p2++; std::memcpy( &v1, p1, S );
if(a != b) std::memcpy( &v2, p2, S );
goto slow; if((v1 ^ v2) & Mask)
}
return true;
while(p1 < pend)
{
slow:
if( beast::detail::ascii_tolower(a) !=
beast::detail::ascii_tolower(b))
return false; return false;
a = *p1++;
b = *p2++;
} }
for(; n; ++p1, ++p2, --n)
if(( *p1 ^ *p2) & 0xDF)
return false;
return true; return true;
} }
};
using map_type = std::unordered_map<
string_view, field, hash, iequal>;
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];
if(equals(s, s2))
return static_cast<field>(i);
return field::unknown; return field::unknown;
auto it = map.find(s);
if(it == map.end())
return field::unknown;
return it->second;
} }
// //