-
-
Save foonathan/6777a1a3453dbc369179d902340d986d to your computer and use it in GitHub Desktop.
benchmark: parsing ipv4 with Boost.Spirit and lexy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <benchmark/benchmark.h> | |
#include <random> | |
#include <string> | |
#include <vector> | |
constexpr auto ipv4_count = 8 * 1024u; | |
std::vector<std::string> generate_ips() | |
{ | |
std::vector<std::string> result; | |
std::default_random_engine engine; | |
std::uniform_int_distribution<std::uint8_t> dist; | |
for (auto i = 0u; i != ipv4_count; ++i) | |
{ | |
auto a = dist(engine); | |
auto b = dist(engine); | |
auto c = dist(engine); | |
auto d = dist(engine); | |
result.push_back(std::to_string(a) + '.' + std::to_string(b) + '.' + std::to_string(c) + '.' | |
+ std::to_string(d)); | |
} | |
return result; | |
} | |
#include <lexy/dsl.hpp> | |
#include <lexy/input/string_input.hpp> | |
#include <lexy/parse.hpp> | |
namespace dsl = lexy::dsl; | |
// Parse an IPv4 address into a `std::uint32_t`. | |
struct ipv4_address | |
{ | |
// What is being matched. | |
static constexpr auto rule = [] { | |
// Match a sequence of (decimal) digits and convert it into a std::uint8_t. | |
auto octet = dsl::integer<std::uint8_t>(dsl::digits<>); | |
// Match four of them separated by periods. | |
return dsl::times<4>(octet, dsl::sep(dsl::period)); | |
}(); | |
// How the matched output is being stored. | |
static constexpr auto value | |
= lexy::callback<std::uint32_t>([](lexy::times<4, std::uint8_t> octets) { | |
std::uint32_t result = 0; | |
for (auto o : octets) | |
{ | |
result <<= 8; | |
result |= o; | |
} | |
return result; | |
}); | |
}; | |
void bm_lexy(benchmark::State& state) | |
{ | |
auto ips = generate_ips(); | |
std::uint64_t dummy = 0; | |
std::size_t bytes = 0; | |
for (auto _ : state) | |
{ | |
for (auto& ip : ips) | |
{ | |
auto input = lexy::string_input(ip); | |
auto result = lexy::parse<ipv4_address>(input, lexy::noop); | |
dummy += result.value(); | |
bytes += ip.size(); | |
benchmark::DoNotOptimize(result); | |
} | |
} | |
state.SetBytesProcessed(std::int64_t(bytes)); | |
benchmark::DoNotOptimize(dummy); | |
} | |
BENCHMARK(bm_lexy); | |
#include <boost/fusion/adapted/array.hpp> | |
#include <boost/spirit/home/x3.hpp> | |
namespace x3 = boost::spirit::x3; | |
template <typename Iterator> | |
std::uint32_t parse_ipv4(Iterator first, Iterator last) | |
{ | |
std::uint8_t octets[4] = {}; | |
auto octet = x3::uint_parser<std::uint8_t>{}; | |
auto result | |
= x3::parse(first, last, octet >> '.' >> octet >> '.' >> octet >> '.' >> octet, octets); | |
if (!result) | |
return std::uint32_t(-1); | |
std::uint32_t ipv4 = 0; | |
for (auto o : octets) | |
{ | |
ipv4 <<= 8; | |
ipv4 |= o; | |
} | |
return ipv4; | |
} | |
void bm_spirit(benchmark::State& state) | |
{ | |
auto ips = generate_ips(); | |
std::uint64_t dummy = 0; | |
std::size_t bytes = 0; | |
for (auto _ : state) | |
{ | |
for (auto& ip : ips) | |
{ | |
auto result = parse_ipv4(ip.begin(), ip.end()); | |
dummy += result; | |
bytes += ip.size(); | |
benchmark::DoNotOptimize(result); | |
} | |
} | |
state.SetBytesProcessed(std::int64_t(bytes)); | |
benchmark::DoNotOptimize(dummy); | |
} | |
BENCHMARK(bm_spirit); | |
BENCHMARK_MAIN(); |
With current master (e82030f326f4def228b78b11dc1ca54b95eb8aae):
2020-12-03T01:21:02+01:00
Running ./a.out
Run on (4 X 2800 MHz CPU s)
CPU Caches:
L1 Data 32 KiB (x2)
L1 Instruction 32 KiB (x2)
L2 Unified 256 KiB (x2)
L3 Unified 3072 KiB (x1)
Load Average: 0.26, 0.62, 0.81
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
---------------------------------------------------------------------
Benchmark Time CPU Iterations UserCounters...
---------------------------------------------------------------------
bm_lexy 395142 ns 394845 ns 1781 bytes_per_second=262.757M/s
bm_spirit 363791 ns 363624 ns 1929 bytes_per_second=285.317M/s
Good, it became faster. Thank you.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can you try the feature/optimize-ip-assembly branch?
Edit: It's now merged into main.