1111
1212void 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
5280template <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
75112static 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
89126int 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