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
// 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
bool
operator()(
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 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();
auto p2 = rhs.data();
auto pend = p1 + lhs.size();
char a, b;
while(p1 < pend)
{
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;
Int v1, v2;
std::memcpy( &v1, p1, S );
std::memcpy( &v2, p2, S );
if((v1 ^ v2) & Mask)
return false;
}
};
using map_type = std::unordered_map<
string_view, field, hash, iequal>;
for(; n; ++p1, ++p2, --n)
if(( *p1 ^ *p2) & 0xDF)
return false;
return true;
}
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())
return field::unknown;
auto it = map.find(s);
if(it == map.end())
return field::unknown;
return it->second;
i += 255;
s2 = by_name_[i];
if(equals(s, s2))
return static_cast<field>(i);
return field::unknown;
}
//