Skip to content

Commit 341eb19

Browse files
committed
wip: uint8_t parsing
Signed-off-by: Shikhar <[email protected]>
1 parent 157deae commit 341eb19

File tree

7 files changed

+1528
-0
lines changed

7 files changed

+1528
-0
lines changed

benchmarks/bench_uint8/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CXX = clang++
2+
3+
all: bench_ip
4+
5+
bench_ip: benchmarks/bench_ip.cpp ../../include/fast_float/ascii_number.h
6+
$(CXX) -std=c++17 -O3 -Wall -o bench_ip benchmarks/bench_ip.cpp -Ibenchmarks -I../../include
7+
8+
debug: benchmarks/bench_ip.cpp ../../include/fast_float/ascii_number.h
9+
$(CXX) -std=c++17 -O0 -g3 -Wall -o bench_ip_debug benchmarks/bench_ip.cpp -Ibenchmarks -I../../include
10+
11+
clean:
12+
rm -f bench_ip bench_ip_debug
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#include "performancecounters/benchmarker.h"
2+
#include "fast_float/fast_float.h"
3+
#include <charconv>
4+
#include <cstdint>
5+
#include <cstdio>
6+
#include <cstdlib>
7+
#include <cstring>
8+
#include <random>
9+
10+
void pretty_print(size_t volume, size_t bytes, std::string name,
11+
event_aggregate agg) {
12+
printf("%-40s : ", name.c_str());
13+
printf(" %5.2f GB/s ", bytes / agg.fastest_elapsed_ns());
14+
printf(" %5.1f Ma/s ", volume * 1000.0 / agg.fastest_elapsed_ns());
15+
printf(" %5.2f ns/d ", agg.fastest_elapsed_ns() / volume);
16+
if (collector.has_events()) {
17+
printf(" %5.2f GHz ", agg.fastest_cycles() / agg.fastest_elapsed_ns());
18+
printf(" %5.2f c/d ", agg.fastest_cycles() / volume);
19+
printf(" %5.2f i/d ", agg.fastest_instructions() / volume);
20+
printf(" %5.2f c/b ", agg.fastest_cycles() / bytes);
21+
printf(" %5.2f i/b ", agg.fastest_instructions() / bytes);
22+
printf(" %5.2f i/c ", agg.fastest_instructions() / agg.fastest_cycles());
23+
}
24+
printf("\n");
25+
}
26+
27+
int parse_u8_fastswar(const char *&p, const char *pend, uint8_t *out) {
28+
if (p == pend)
29+
return 0;
30+
auto r = fast_float::from_chars(p, pend, *out);
31+
if (r.ec == std::errc()) {
32+
p = r.ptr;
33+
return 1;
34+
}
35+
return 0;
36+
}
37+
38+
static inline int parse_u8_fromchars(const char *&p, const char *pend,
39+
uint8_t *out) {
40+
if (p == pend)
41+
return 0;
42+
auto r = std::from_chars(p, pend, *out);
43+
if (r.ec == std::errc()) {
44+
p = r.ptr;
45+
return 1;
46+
}
47+
return 0;
48+
}
49+
50+
template <typename Parser>
51+
static inline int parse_ip_line(const char *&p, const char *pend, uint32_t &sum,
52+
Parser parse_uint8) {
53+
uint8_t o = 0;
54+
for (int i = 0; i < 4; ++i) {
55+
if (!parse_uint8(p, pend, &o))
56+
return 0;
57+
sum += o;
58+
if (i != 3) {
59+
if (p == pend || *p != '.')
60+
return 0;
61+
++p;
62+
}
63+
}
64+
// consume optional '\r'
65+
if (p != pend && *p == '\r')
66+
++p;
67+
// expect '\n' or end
68+
if (p != pend && *p == '\n')
69+
++p;
70+
return 1;
71+
}
72+
73+
static std::string make_ip_line(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
74+
std::string s;
75+
s.reserve(16);
76+
s += std::to_string(a);
77+
s += '.';
78+
s += std::to_string(b);
79+
s += '.';
80+
s += std::to_string(c);
81+
s += '.';
82+
s += std::to_string(d);
83+
s += '\n';
84+
return s;
85+
}
86+
87+
int main() {
88+
constexpr size_t N = 500000;
89+
std::mt19937 rng(1234);
90+
std::uniform_int_distribution<int> dist(0, 255);
91+
92+
std::string buf;
93+
buf.reserve(N * 16);
94+
95+
for (size_t i = 0; i < N; ++i) {
96+
uint8_t a = (uint8_t)dist(rng);
97+
uint8_t b = (uint8_t)dist(rng);
98+
uint8_t c = (uint8_t)dist(rng);
99+
uint8_t d = (uint8_t)dist(rng);
100+
buf += make_ip_line(a, b, c, d);
101+
}
102+
103+
// sentinel to allow 4-byte loads at end
104+
buf.append(4, '\0');
105+
106+
const size_t bytes = buf.size() - 4; // exclude sentinel from throughput
107+
const size_t volume = N;
108+
109+
// validate correctness
110+
{
111+
const char *start = buf.data();
112+
const char *end = buf.data() + bytes;
113+
const char *p = start;
114+
const char *pend = end;
115+
uint32_t sum = 0;
116+
for (size_t i = 0; i < N; ++i) {
117+
int ok = parse_ip_line(p, pend, sum, parse_u8_fromchars);
118+
if (!ok) {
119+
std::fprintf(stderr, "fromchars parse failed at line %zu\n", i);
120+
std::abort();
121+
}
122+
p = start;
123+
pend = end;
124+
ok = parse_ip_line(p, pend, sum, parse_u8_fastswar);
125+
if (!ok) {
126+
std::fprintf(stderr, "fastswar parse failed at line %zu\n", i);
127+
std::abort();
128+
}
129+
}
130+
}
131+
132+
uint32_t sink = 0;
133+
134+
pretty_print(volume, bytes, "parse_ip_fromchars", bench([&]() {
135+
const char *p = buf.data();
136+
const char *pend = buf.data() + bytes;
137+
uint32_t sum = 0;
138+
int ok = 0;
139+
for (size_t i = 0; i < N; ++i) {
140+
ok = parse_ip_line(p, pend, sum, parse_u8_fromchars);
141+
if (!ok)
142+
std::abort();
143+
}
144+
sink += sum;
145+
}));
146+
147+
pretty_print(volume, bytes, "parse_ip_fastswar", bench([&]() {
148+
const char *p = buf.data();
149+
const char *pend = buf.data() + bytes;
150+
uint32_t sum = 0;
151+
int ok = 0;
152+
for (size_t i = 0; i < N; ++i) {
153+
ok = parse_ip_line(p, pend, sum, parse_u8_fastswar);
154+
if (!ok)
155+
std::abort();
156+
}
157+
sink += sum;
158+
}));
159+
160+
std::printf("sink=%u\n", sink);
161+
return 0;
162+
}

0 commit comments

Comments
 (0)