mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +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:
|
Version 208:
|
||||||
|
|
||||||
* Add get_lowest_layer free function
|
* Add get_lowest_layer free function
|
||||||
|
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
Reference in New Issue
Block a user