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:
* Add get_lowest_layer free function

View File

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