mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 04:47:29 +02:00
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:
committed by
Vinnie Falco
parent
150614cc9a
commit
e0db595760
@ -1,3 +1,9 @@
|
||||
Version 209:
|
||||
|
||||
* Faster http::string_to_field
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Version 208:
|
||||
|
||||
* Add get_lowest_layer free function
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
//
|
||||
|
Reference in New Issue
Block a user