Skip to content

Commit 5830594

Browse files
authored
Merge pull request #351 from fastfloat/simpler_benchmark
simplify the IP benchmark
2 parents 62fb615 + 75d01f0 commit 5830594

File tree

2 files changed

+102
-59
lines changed

2 files changed

+102
-59
lines changed

benchmarks/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ include(FetchContent)
33
FetchContent_Declare(
44
counters
55
GIT_REPOSITORY https://github.com/lemire/counters.git
6-
GIT_TAG v2.0.0
6+
GIT_TAG v2.1.0
77
)
88

99
FetchContent_MakeAvailable(counters)

benchmarks/bench_ip.cpp

Lines changed: 101 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,49 @@
1111

1212
void pretty_print(size_t volume, size_t bytes, std::string name,
1313
counters::event_aggregate agg) {
14+
if (agg.inner_count > 1) {
15+
printf("# (inner count: %d)\n", agg.inner_count);
16+
}
1417
printf("%-40s : ", name.c_str());
1518
printf(" %5.2f GB/s ", bytes / agg.fastest_elapsed_ns());
16-
printf(" %5.1f Ma/s ", volume * 1000.0 / agg.fastest_elapsed_ns());
17-
printf(" %5.2f ns/d ", agg.fastest_elapsed_ns() / volume);
19+
printf(" %5.1f Mip/s ", volume * 1000.0 / agg.fastest_elapsed_ns());
20+
printf(" %5.2f ns/ip ", agg.fastest_elapsed_ns() / volume);
1821
if (counters::event_collector().has_events()) {
1922
printf(" %5.2f GHz ", agg.fastest_cycles() / agg.fastest_elapsed_ns());
20-
printf(" %5.2f c/d ", agg.fastest_cycles() / volume);
21-
printf(" %5.2f i/d ", agg.fastest_instructions() / volume);
23+
printf(" %5.2f c/ip ", agg.fastest_cycles() / volume);
24+
printf(" %5.2f i/ip ", agg.fastest_instructions() / volume);
2225
printf(" %5.2f c/b ", agg.fastest_cycles() / bytes);
2326
printf(" %5.2f i/b ", agg.fastest_instructions() / bytes);
2427
printf(" %5.2f i/c ", agg.fastest_instructions() / agg.fastest_cycles());
2528
}
2629
printf("\n");
2730
}
2831

29-
int parse_u8_fastfloat(const char *&p, const char *pend, uint8_t *out) {
32+
fastfloat_really_inline const char *seek_ip_end(const char *p,
33+
const char *pend) {
34+
const char *current = p;
35+
size_t count = 0;
36+
for (; current != pend; ++current) {
37+
if (*current == '.') {
38+
count++;
39+
if (count == 3) {
40+
++current;
41+
break;
42+
}
43+
}
44+
}
45+
while (current != pend) {
46+
if (*current <= '9' && *current >= '0') {
47+
++current;
48+
} else {
49+
break;
50+
}
51+
}
52+
return current;
53+
}
54+
55+
fastfloat_really_inline int parse_u8_fastfloat(const char *&p, const char *pend,
56+
uint8_t *out) {
3057
if (p == pend)
3158
return 0;
3259
auto r = fast_float::from_chars(p, pend, *out);
@@ -37,10 +64,11 @@ int parse_u8_fastfloat(const char *&p, const char *pend, uint8_t *out) {
3764
return 0;
3865
}
3966

40-
static inline int parse_u8_fromchars(const char *&p, const char *pend,
41-
uint8_t *out) {
42-
if (p == pend)
67+
fastfloat_really_inline int parse_u8_fromchars(const char *&p, const char *pend,
68+
uint8_t *out) {
69+
if (p == pend) {
4370
return 0;
71+
}
4472
auto r = std::from_chars(p, pend, *out);
4573
if (r.ec == std::errc()) {
4674
p = r.ptr;
@@ -50,26 +78,35 @@ static inline int parse_u8_fromchars(const char *&p, const char *pend,
5078
}
5179

5280
template <typename Parser>
53-
static inline int parse_ip_line(const char *&p, const char *pend, uint32_t &sum,
54-
Parser parse_uint8) {
55-
uint8_t o = 0;
56-
for (int i = 0; i < 4; ++i) {
57-
if (!parse_uint8(p, pend, &o))
58-
return 0;
59-
sum += o;
60-
if (i != 3) {
61-
if (p == pend || *p != '.')
62-
return 0;
63-
++p;
64-
}
81+
fastfloat_really_inline std::pair<bool, uint32_t>
82+
simple_parse_ip_line(const char *p, const char *pend, Parser parse_uint8) {
83+
uint8_t v1;
84+
if (!parse_uint8(p, pend, &v1)) {
85+
return {false, 0};
86+
}
87+
if (p == pend || *p++ != '.') {
88+
return {false, 0};
89+
}
90+
uint8_t v2;
91+
if (!parse_uint8(p, pend, &v2)) {
92+
return {false, 0};
93+
}
94+
if (p == pend || *p++ != '.') {
95+
return {false, 0};
96+
}
97+
uint8_t v3;
98+
if (!parse_uint8(p, pend, &v3)) {
99+
return {false, 0};
100+
}
101+
if (p == pend || *p++ != '.') {
102+
return {false, 0};
65103
}
66-
// consume optional '\r'
67-
if (p != pend && *p == '\r')
68-
++p;
69-
// expect '\n' or end
70-
if (p != pend && *p == '\n')
71-
++p;
72-
return 1;
104+
uint8_t v4;
105+
if (!parse_uint8(p, pend, &v4)) {
106+
return {false, 0};
107+
}
108+
return {true, (uint32_t(v1) << 24) | (uint32_t(v2) << 16) |
109+
(uint32_t(v3) << 8) | uint32_t(v4)};
73110
}
74111

75112
static std::string make_ip_line(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
@@ -87,19 +124,22 @@ static std::string make_ip_line(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
87124
}
88125

89126
int main() {
90-
constexpr size_t N = 500000;
127+
constexpr size_t N = 15000;
91128
std::mt19937 rng(1234);
92129
std::uniform_int_distribution<int> dist(0, 255);
93130

94131
std::string buf;
95-
buf.reserve(N * 16);
132+
constexpr size_t ip_size = 16;
133+
buf.reserve(N * ip_size);
96134

97135
for (size_t i = 0; i < N; ++i) {
98136
uint8_t a = (uint8_t)dist(rng);
99137
uint8_t b = (uint8_t)dist(rng);
100138
uint8_t c = (uint8_t)dist(rng);
101139
uint8_t d = (uint8_t)dist(rng);
102-
buf += make_ip_line(a, b, c, d);
140+
std::string ip_line = make_ip_line(a, b, c, d);
141+
ip_line.resize(ip_size, ' '); // pad to fixed size
142+
buf.append(ip_line);
103143
}
104144

105145
// sentinel to allow 4-byte loads at end
@@ -108,40 +148,40 @@ int main() {
108148
const size_t bytes = buf.size() - 4; // exclude sentinel from throughput
109149
const size_t volume = N;
110150

111-
// validate correctness
112-
{
113-
const char *start = buf.data();
114-
const char *end = buf.data() + bytes;
115-
const char *p = start;
116-
const char *pend = end;
117-
uint32_t sum = 0;
118-
for (size_t i = 0; i < N; ++i) {
119-
int ok = parse_ip_line(p, pend, sum, parse_u8_fromchars);
120-
if (!ok) {
121-
std::fprintf(stderr, "fromchars parse failed at line %zu\n", i);
122-
std::abort();
123-
}
124-
p = start;
125-
pend = end;
126-
ok = parse_ip_line(p, pend, sum, parse_u8_fastfloat);
127-
if (!ok) {
128-
std::fprintf(stderr, "fastswar parse failed at line %zu\n", i);
129-
std::abort();
130-
}
131-
}
132-
}
151+
volatile uint32_t sink = 0;
152+
std::string buffer(ip_size * N, ' ');
153+
154+
pretty_print(volume, bytes, "memcpy baseline", counters::bench([&]() {
155+
std::memcpy((char *)buffer.data(), buf.data(), bytes);
156+
}));
133157

134-
uint32_t sink = 0;
158+
pretty_print(volume, bytes, "just_seek_ip_end (no parse)",
159+
counters::bench([&]() {
160+
const char *p = buf.data();
161+
const char *pend = buf.data() + bytes;
162+
uint32_t sum = 0;
163+
int ok = 0;
164+
for (size_t i = 0; i < N; ++i) {
165+
const char *q = seek_ip_end(p, pend);
166+
sum += (uint32_t)(q - p);
167+
p += ip_size;
168+
}
169+
sink += sum;
170+
}));
135171

136172
pretty_print(volume, bytes, "parse_ip_std_fromchars", counters::bench([&]() {
137173
const char *p = buf.data();
138174
const char *pend = buf.data() + bytes;
139175
uint32_t sum = 0;
140176
int ok = 0;
141177
for (size_t i = 0; i < N; ++i) {
142-
ok = parse_ip_line(p, pend, sum, parse_u8_fromchars);
143-
if (!ok)
178+
auto [ok, ip] =
179+
simple_parse_ip_line(p, pend, parse_u8_fromchars);
180+
sum += ip;
181+
if (!ok) {
144182
std::abort();
183+
}
184+
p += ip_size;
145185
}
146186
sink += sum;
147187
}));
@@ -152,13 +192,16 @@ int main() {
152192
uint32_t sum = 0;
153193
int ok = 0;
154194
for (size_t i = 0; i < N; ++i) {
155-
ok = parse_ip_line(p, pend, sum, parse_u8_fastfloat);
156-
if (!ok)
195+
auto [ok, ip] =
196+
simple_parse_ip_line(p, pend, parse_u8_fastfloat);
197+
sum += ip;
198+
if (!ok) {
157199
std::abort();
200+
}
201+
p += ip_size;
158202
}
159203
sink += sum;
160204
}));
161205

162-
std::printf("sink=%u\n", sink);
163206
return EXIT_SUCCESS;
164207
}

0 commit comments

Comments
 (0)