I have a codebase that uses business logic to convert csv files to xml-like config files. This code uses stringstreams heavily to construct the config files some of which has inline SQL statements in them. I was looking to refactor parts of this code and decided to use libfmt to construct the config strings. The code is definitely more readable compared the stringstreams but I was also hoping to have had a significant performance bump.
However, I was testing a simple benchmark of libfmt against other ways to construct a string and I find it consistently slow even compared to stringstreams. (Although the above test also includes the std::format (mostly out of curiosity), we haven't moved to C++20 yet and so cannot use it.)
Why is this the case and what can I do to improve it?
I have looked through the following related question but my use case is different. Why is {fmt} slower than std::stringstream?
My benchmark code I tried, is available here: https://godbolt.org/z/xrhPdarqa
godbolt is not suitable for benchmarks like this for multiple reasons, including the fact that it uses debug versions of {fmt} and other libraries. So you are effectively comparing a debug version of {fmt} with an optimized version of sstream. You even get a warning from Google Benchmark itself:
***WARNING*** Library was built as DEBUG. Timings may be affected.
A partial workaround is to use the header-only mode which shows that {fmt} is 2.5-3x faster than sstream in this case (https://godbolt.org/z/WY4d3h9vr):
--------------------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------------------
BM_test_string_append 146 ns 82.2 ns 7974239
BM_test_sstream_append 674 ns 344 ns 1465041
BM_test_fmt_string_append 273 ns 125 ns 6728817
BM_test_std_format_append 415 ns 223 ns 2596699