Skip to content

Commit 2bff2e0

Browse files
committed
Update string formatting benchmarks
Also add benchmarks for formatting strings without floats. Floats are going to be slower in libfly until major compilers support std::to_chars for floating point types.
1 parent aebd272 commit 2bff2e0

File tree

2 files changed

+110
-29
lines changed

2 files changed

+110
-29
lines changed

bench/string/README.md

+56-18
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,69 @@ Benchmark of the libfly JSON parser against the popular {fmt} library and the ST
77

88
## Results
99

10-
Results below are the median of 1000001 iterations of creating a formatted string. Obviously, libfly
11-
could use some work :)
10+
All results below are the median of 1000001 iterations of creating a formatted string.
11+
12+
### Formatting with floats
1213

1314
| Formatter | Duration (ns) |
1415
| :-- | --: |
15-
| libfly | 2.337 |
16-
| {fmt} | 0.592 |
17-
| STL IO Streams | 1.857 |
16+
| libfly | 1.784 |
17+
| STL IO Streams | 1.877 |
18+
| {fmt} | 0.586 |
19+
20+
### Formatting without floats
21+
22+
| Formatter | Duration (ns) |
23+
| :-- | --: |
24+
| libfly | 0.748 |
25+
| {fmt} | 0.411 |
26+
| STL IO Streams | 1.011 |
27+
28+
## Profiles
29+
30+
### Formatting with floats
31+
32+
```
33+
Each sample counts as 0.01 seconds.
34+
% cumulative self self total
35+
time seconds seconds calls ms/call ms/call name
36+
10.45 0.46 0.07 2000002 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format_value<double, false>(fly::detail::BasicFormatSpecifier<char>&&, double const&)
37+
10.45 0.53 0.07 1000001 0.00 0.00 fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format(fly::detail::BasicFormatString<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>&&)
38+
8.96 0.59 0.06 5000005 0.00 0.00 void fly::detail::BasicFormatParameters<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::visit<fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format(fly::detail::BasicFormatString<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>&&)::{lambda(auto:1&&, auto:2 const&)#1}, 1ul>(fly::detail::BasicFormatSpecifier<char>&&, fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format(fly::detail::BasicFormatString<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>&&)::{lambda(auto:1&&, auto:2 const&)#1}) const
39+
5.97 0.63 0.04 11000011 0.00 0.00 TLS init function for fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::s_stream
40+
2.99 0.65 0.02 1000013 0.00 0.00 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::reserve(unsigned long)
41+
1.49 0.66 0.01 2000004 0.00 0.00 fly::detail::BasicStreamModifiers<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::~BasicStreamModifiers()
42+
1.49 0.67 0.01 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format_value<unsigned char, false>(fly::detail::BasicFormatSpecifier<char>&&, unsigned char, bool)
43+
0.00 0.67 0.00 2000004 0.00 0.00 fly::detail::BasicStreamModifiers<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::BasicStreamModifiers(std::ostream&)
44+
0.00 0.67 0.00 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format_value<char [4], false>(fly::detail::BasicFormatSpecifier<char>&&, char const (&) [4])
45+
0.00 0.67 0.00 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format_value<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, false>(fly::detail::BasicFormatSpecifier<char>&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
46+
0.00 0.67 0.00 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format_value<unsigned int, false>(fly::detail::BasicFormatSpecifier<char>&&, unsigned int, bool)
47+
0.00 0.67 0.00 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::append_string<char [4], false>(char const (&) [4], unsigned long)
48+
0.00 0.67 0.00 1000001 0.00 0.00 std::enable_if<std::__or_<std::__or_<std::is_same<std::remove_cv<unsigned int>::type, signed char>, std::is_same<std::remove_cv<unsigned int>::type, short>, std::is_same<std::remove_cv<unsigned int>::type, int>, std::is_same<std::remove_cv<unsigned int>::type, long>, std::is_same<std::remove_cv<unsigned int>::type, long long> >, std::__or_<std::is_same<std::remove_cv<unsigned int>::type, unsigned char>, std::is_same<std::remove_cv<unsigned int>::type, unsigned short>, std::is_same<std::remove_cv<unsigned int>::type, unsigned int>, std::is_same<std::remove_cv<unsigned int>::type, unsigned long>, std::is_same<std::remove_cv<unsigned int>::type, unsigned long long> >, std::is_same<char, std::remove_cv<unsigned int>::type> >::value, std::to_chars_result>::type std::__to_chars_i<unsigned int>(char*, char*, unsigned int, int)
49+
0.00 0.67 0.00 2131 0.00 0.00 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
50+
0.00 0.67 0.00 1196 0.00 0.00 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
51+
0.00 0.67 0.00 1016 0.00 0.00 std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::vector(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&)
52+
```
1853

19-
## Profile
54+
### Formatting without floats
2055

2156
```
2257
Each sample counts as 0.01 seconds.
2358
% cumulative self self total
2459
time seconds seconds calls ms/call ms/call name
25-
26.92 0.56 0.21 1000001 0.00 0.00 fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format(fly::detail::BasicFormatString<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>&&)
26-
3.85 0.65 0.03 6000038 0.00 0.00 fly::detail::BasicStreamModifiers<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::BasicStreamModifiers(std::ostream&)
27-
3.85 0.68 0.03 2000002 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::set_numeric_options<double>(fly::detail::BasicStreamModifiers<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&, fly::detail::BasicFormatSpecifier<char> const&, double const&) const
28-
2.56 0.73 0.02 6000038 0.00 0.00 fly::detail::BasicStreamModifiers<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::~BasicStreamModifiers()
29-
2.56 0.75 0.02 6000006 0.00 0.00 fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::set_generic_options(fly::detail::BasicStreamModifiers<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&, fly::detail::BasicFormatSpecifier<char> const&) const
30-
1.28 0.76 0.01 2 5.00 5.00 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::reserve(unsigned long)
31-
1.28 0.77 0.01 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace(unsigned long, unsigned long, char const*, unsigned long)
32-
1.28 0.78 0.01 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::swap(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
33-
0.00 0.78 0.00 1000001 0.00 0.00 void fly::detail::BasicStringStreamer<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::stream_string<char const (&) [4]>(std::ostream&, char const (&) [4], unsigned long)
34-
0.00 0.78 0.00 1196 0.00 0.00 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
35-
0.00 0.78 0.00 1172 0.00 0.00 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
36-
0.00 0.78 0.00 1016 0.00 0.00 std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::vector(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&)
60+
16.42 0.50 0.11 1000001 0.00 0.00 fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, int, int, char const (&) [4], decltype(nullptr), char>::format(fly::detail::BasicFormatString<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, int, int, char const (&) [4], decltype(nullptr), char>&&)
61+
7.46 0.62 0.05 3000003 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, int, int, char const (&) [4], decltype(nullptr), char>::format_value<unsigned int, false>(fly::detail::BasicFormatSpecifier<char>&&, unsigned int, bool)
62+
4.48 0.65 0.03 3000003 0.00 0.00 TLS init function for fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::s_stream
63+
1.49 0.67 0.01 fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>::format(fly::detail::BasicFormatString<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double, int, double, char const (&) [4], decltype(nullptr), char>&&)
64+
0.00 0.67 0.00 3000003 0.00 0.00 std::enable_if<std::__or_<std::__or_<std::is_same<std::remove_cv<unsigned int>::type, signed char>, std::is_same<std::remove_cv<unsigned int>::type, short>, std::is_same<std::remove_cv<unsigned int>::type, int>, std::is_same<std::remove_cv<unsigned int>::type, long>, std::is_same<std::remove_cv<unsigned int>::type, long long> >, std::__or_<std::is_same<std::remove_cv<unsigned int>::type, unsigned char>, std::is_same<std::remove_cv<unsigned int>::type, unsigned short>, std::is_same<std::remove_cv<unsigned int>::type, unsigned int>, std::is_same<std::remove_cv<unsigned int>::type, unsigned long>, std::is_same<std::remove_cv<unsigned int>::type, unsigned long long> >, std::is_same<char, std::remove_cv<unsigned int>::type> >::value, std::to_chars_result>::type std::__to_chars_i<unsigned int>(char*, char*, unsigned int, int)
65+
0.00 0.67 0.00 1000013 0.00 0.00 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::reserve(unsigned long)
66+
0.00 0.67 0.00 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, int, int, char const (&) [4], decltype(nullptr), char>::format_value<char [4], false>(fly::detail::BasicFormatSpecifier<char>&&, char const (&) [4])
67+
0.00 0.67 0.00 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, int, int, char const (&) [4], decltype(nullptr), char>::format_value<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, false>(fly::detail::BasicFormatSpecifier<char>&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
68+
0.00 0.67 0.00 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, int, int, char const (&) [4], decltype(nullptr), char>::format_value<unsigned char, false>(fly::detail::BasicFormatSpecifier<char>&&, unsigned char, bool)
69+
0.00 0.67 0.00 1000001 0.00 0.00 void fly::detail::BasicStringFormatter<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, int, int, char const (&) [4], decltype(nullptr), char>::append_string<char [4], false>(char const (&) [4], unsigned long)
70+
0.00 0.67 0.00 2131 0.00 0.00 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
71+
0.00 0.67 0.00 1196 0.00 0.00 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
72+
0.00 0.67 0.00 1016 0.00 0.00 std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::vector(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&)
73+
0.00 0.67 0.00 139 0.00 0.00 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()
74+
0.00 0.67 0.00 120 0.00 0.00 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag)
3775
```

bench/string/benchmark_string.cpp

+54-11
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,21 @@
1313
namespace {
1414

1515
using StringTable = fly::benchmark::Table<std::string, double>;
16+
static constexpr std::size_t s_iterations = 1000001;
1617

1718
class StringBase
1819
{
1920
public:
2021
virtual ~StringBase() = default;
21-
virtual void format() = 0;
22+
virtual void format_with_floats() = 0;
23+
virtual void format_without_floats() = 0;
2224
};
2325

2426
// libfly - https://github.com/trflynn89/libfly
2527
class LibflyFormat : public StringBase
2628
{
2729
public:
28-
void format() override
30+
void format_with_floats() override
2931
{
3032
FLY_UNUSED(fly::String::format(
3133
"{:.10f}:{:04}:{:+}:{}:{}:{}:%\n",
@@ -36,24 +38,42 @@ class LibflyFormat : public StringBase
3638
nullptr,
3739
'X'));
3840
}
41+
42+
void format_without_floats() override
43+
{
44+
FLY_UNUSED(fly::String::format(
45+
"{:10}:{:04}:{:+}:{}:{}:{}:%\n",
46+
1234,
47+
42,
48+
313,
49+
"str",
50+
nullptr,
51+
'X'));
52+
}
3953
};
4054

4155
// {fmt} - https://github.com/fmtlib/fmt
4256
class FmtFormat : public StringBase
4357
{
4458
public:
45-
void format() override
59+
void format_with_floats() override
4660
{
4761
FLY_UNUSED(
4862
fmt::format("{:.10f}:{:04}:{:+}:{}:{}:{}:%\n", 1.234, 42, 3.13, "str", nullptr, 'X'));
4963
}
64+
65+
void format_without_floats() override
66+
{
67+
FLY_UNUSED(
68+
fmt::format("{:10}:{:04}:{:+}:{}:{}:{}:%\n", 1234, 42, 313, "str", nullptr, 'X'));
69+
}
5070
};
5171

5272
// STL IO Streams
5373
class STLStreamFormat : public StringBase
5474
{
5575
public:
56-
void format() override
76+
void format_with_floats() override
5777
{
5878
std::stringstream stream;
5979
stream << std::setprecision(10) << std::fixed << 1.234 << ':'
@@ -62,22 +82,28 @@ class STLStreamFormat : public StringBase
6282
<< std::resetiosflags(std::ios::showpos) << ':' << "str" << ':' << nullptr << ':'
6383
<< 'X' << ":%\n";
6484
}
65-
};
6685

67-
} // namespace
86+
void format_without_floats() override
87+
{
88+
std::stringstream stream;
89+
stream << std::setw(10) << 1234 << ':' << std::setw(4) << std::setfill('0') << 42
90+
<< std::setfill(' ') << ':' << std::setiosflags(std::ios::showpos) << 313
91+
<< std::resetiosflags(std::ios::showpos) << ':' << "str" << ':' << nullptr << ':'
92+
<< 'X' << ":%\n";
93+
}
94+
};
6895

69-
CATCH_TEST_CASE("String", "[bench]")
96+
template <typename WithFloats>
97+
void run_format_test(std::string &&name)
7098
{
71-
static constexpr std::size_t s_iterations = 1000001;
72-
7399
std::map<std::string, std::unique_ptr<StringBase>> formatters;
74100
#if !defined(FLY_PROFILE)
75101
formatters.emplace("{fmt}", std::make_unique<FmtFormat>());
76102
formatters.emplace("STL IO Streams", std::make_unique<STLStreamFormat>());
77103
#endif
78104
formatters.emplace("libfly", std::make_unique<LibflyFormat>());
79105

80-
StringTable table("String Formatting", {"Formatter", "Duration (ns)"});
106+
StringTable table(std::move(name), {"Formatter", "Duration (ns)"});
81107

82108
for (auto &formatter : formatters)
83109
{
@@ -86,7 +112,16 @@ CATCH_TEST_CASE("String", "[bench]")
86112
for (std::size_t i = 0; i < s_iterations; ++i)
87113
{
88114
const auto start = std::chrono::system_clock::now();
89-
formatter.second->format();
115+
116+
if constexpr (std::is_same_v<WithFloats, std::true_type>)
117+
{
118+
formatter.second->format_with_floats();
119+
}
120+
else
121+
{
122+
formatter.second->format_without_floats();
123+
}
124+
90125
const auto end = std::chrono::system_clock::now();
91126

92127
const auto duration = std::chrono::duration<double>(end - start);
@@ -101,3 +136,11 @@ CATCH_TEST_CASE("String", "[bench]")
101136

102137
std::cout << table << '\n';
103138
}
139+
140+
} // namespace
141+
142+
CATCH_TEST_CASE("String", "[bench]")
143+
{
144+
run_format_test<std::true_type>("Formatting (with floats)");
145+
run_format_test<std::false_type>("Formatting (without floats)");
146+
}

0 commit comments

Comments
 (0)