Skip to content

Instantly share code, notes, and snippets.

@foonathan
Created December 2, 2020 07:44
Show Gist options
  • Save foonathan/6777a1a3453dbc369179d902340d986d to your computer and use it in GitHub Desktop.
Save foonathan/6777a1a3453dbc369179d902340d986d to your computer and use it in GitHub Desktop.
benchmark: parsing ipv4 with Boost.Spirit and lexy
#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();
@foonathan
Copy link
Author

foonathan commented Dec 2, 2020

Can you try the feature/optimize-ip-assembly branch?

Edit: It's now merged into main.

@ilyapopov
Copy link

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

@foonathan
Copy link
Author

Good, it became faster. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment