public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug libstdc++/110801] New: std::format code runs slower than equivalent {fmt} code
@ 2023-07-25 10:58 redi at gcc dot gnu.org
  2023-08-15 21:42 ` [Bug libstdc++/110801] " redi at gcc dot gnu.org
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: redi at gcc dot gnu.org @ 2023-07-25 10:58 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110801

            Bug ID: 110801
           Summary: std::format code runs slower than equivalent {fmt}
                    code
           Product: gcc
           Version: 13.1.1
            Status: UNCONFIRMED
          Keywords: missed-optimization
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: redi at gcc dot gnu.org
  Target Milestone: ---

Using the benchmark from
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r10.html#Benchmarks
and adding:

#if __has_include(<format>)
#include <format>
#endif

#ifdef __cpp_lib_format
void std_format(benchmark::State& s) {
  size_t result = 0;
  while (s.KeepRunning()) {
    for (auto i: data)
      result += std::format("{}", i).size();
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(std_format);

void std_format_to(benchmark::State& s) {
  size_t result = 0;
  while (s.KeepRunning()) {
    for (auto i: data) {
      char buffer[12];
      result += std::format_to(buffer, "{}", i) - buffer;
    }
  }
  benchmark::DoNotOptimize(result);
}
BENCHMARK(std_format_to);
#endif

I get:

Benchmark              Time             CPU   Iterations
--------------------------------------------------------
sprintf           708600 ns       706474 ns          946
ostringstream    1216025 ns      1210087 ns          589
to_string         178579 ns       178088 ns         3824
format            306344 ns       305365 ns         2345
format_to         145606 ns       145223 ns         4940
std_format        514969 ns       513563 ns         1376
std_format_to     436502 ns       435402 ns         1567

The std::to_string performance is good, but std::format is much slower than
fmt::format.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Bug libstdc++/110801] std::format code runs slower than equivalent {fmt} code
  2023-07-25 10:58 [Bug libstdc++/110801] New: std::format code runs slower than equivalent {fmt} code redi at gcc dot gnu.org
@ 2023-08-15 21:42 ` redi at gcc dot gnu.org
  2023-08-17 17:26 ` redi at gcc dot gnu.org
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-15 21:42 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110801

--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Created attachment 55739
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=55739&action=edit
Add special case for format("{}", integer)

With this patch std::format is much closer to fmt::format:

Benchmark              Time             CPU   Iterations
--------------------------------------------------------
sprintf           554621 ns       553889 ns         1241
ostringstream     932465 ns       931258 ns          746
to_string         122602 ns       122425 ns         5424
format            241978 ns       241656 ns         2939
format_to         109541 ns       109391 ns         6282
std_format        282151 ns       281787 ns         2490
std_format_to     225596 ns       225301 ns         3080

std::format_to could still be faster. It should be potentially as fast as
to_string.

The patch is just a prototype that only optimizes for integers, but the idea
could be extended to other types too. For integers, floats, strings, and
pointers we can skip the formatter::parse and formatter::format calls for a
"{}" format string and just do the basic output form, with none of the
additional code for alternative presentation forms, alignment, width, precision
etc.

As well as short-circuiting most of the formatting logic, the other part of the
optimization is writing directly to the output buffer if it is a contiguous
iterator (or a sink iterator that writes to a contiguous container). This is
more efficient than writing to a buffer and then copying to the output.  This
could be extended to work with a back_insert_iterator<string> or
back_insert_iterator<vector>, as we could extract the container, resize it, and
then write directly into it.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Bug libstdc++/110801] std::format code runs slower than equivalent {fmt} code
  2023-07-25 10:58 [Bug libstdc++/110801] New: std::format code runs slower than equivalent {fmt} code redi at gcc dot gnu.org
  2023-08-15 21:42 ` [Bug libstdc++/110801] " redi at gcc dot gnu.org
@ 2023-08-17 17:26 ` redi at gcc dot gnu.org
  2023-11-18 21:44 ` cvs-commit at gcc dot gnu.org
  2023-11-18 21:51 ` redi at gcc dot gnu.org
  3 siblings, 0 replies; 5+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-17 17:26 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110801

--- Comment #2 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #1)
> Created attachment 55739 [details]
> Add special case for format("{}", integer)

The _Seq_sink::_M_get_pointer and _Iter_sink::_M_get_pointer overrides in this
patch are broken. They assume that no other output has already been done to the
sink, but that isn't true if you have a formatter that writes some output then
uses format("{}", i) to write some more (e.g. chrono formatters). The
format("{}", i) call will use the new fast path and call _M_get_pointer which
currently gives a pointer to the start of the buffer, but that overwrites
anything already written there.

I have a fix for that.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Bug libstdc++/110801] std::format code runs slower than equivalent {fmt} code
  2023-07-25 10:58 [Bug libstdc++/110801] New: std::format code runs slower than equivalent {fmt} code redi at gcc dot gnu.org
  2023-08-15 21:42 ` [Bug libstdc++/110801] " redi at gcc dot gnu.org
  2023-08-17 17:26 ` redi at gcc dot gnu.org
@ 2023-11-18 21:44 ` cvs-commit at gcc dot gnu.org
  2023-11-18 21:51 ` redi at gcc dot gnu.org
  3 siblings, 0 replies; 5+ messages in thread
From: cvs-commit at gcc dot gnu.org @ 2023-11-18 21:44 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110801

--- Comment #3 from CVS Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:41a5ea4cab2c59f9911325281f7df1d3ae846d48

commit r14-5587-g41a5ea4cab2c59f9911325281f7df1d3ae846d48
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Aug 15 22:43:41 2023 +0100

    libstdc++: Add fast path for std::format("{}", x) [PR110801]

    This optimizes the simple case of formatting a single string, integer
    or bool, with no format-specifier (so no padding, alignment, alternate
    form etc.)

    libstdc++-v3/ChangeLog:

            PR libstdc++/110801
            * include/std/format (_Sink_iter::_M_reserve): New member
            function.
            (_Sink::_Reservation): New nested class.
            (_Sink::_M_reserve, _Sink::_M_bump): New virtual functions.
            (_Seq_sink::_M_reserve, _Seq_sink::_M_bump): New virtual
            overrides.
            (_Iter_sink<O, ContigIter>::_M_reserve): Likewise.
            (__do_vformat_to): Use new functions to optimize "{}" case.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Bug libstdc++/110801] std::format code runs slower than equivalent {fmt} code
  2023-07-25 10:58 [Bug libstdc++/110801] New: std::format code runs slower than equivalent {fmt} code redi at gcc dot gnu.org
                   ` (2 preceding siblings ...)
  2023-11-18 21:44 ` cvs-commit at gcc dot gnu.org
@ 2023-11-18 21:51 ` redi at gcc dot gnu.org
  3 siblings, 0 replies; 5+ messages in thread
From: redi at gcc dot gnu.org @ 2023-11-18 21:51 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110801

Jonathan Wakely <redi at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |NEW
   Last reconfirmed|                            |2023-11-18
     Ever confirmed|0                           |1

--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
I get these numbers now:

sprintf           530172 ns       529123 ns         1366
ostringstream    1150474 ns      1147048 ns          613
to_string         150262 ns       149901 ns         4680
format            284007 ns       283124 ns         2481
format_to         142947 ns       142612 ns         5046
std_format        340518 ns       339737 ns         2062
std_format_to     296434 ns       295710 ns         2407

There's still room for improvement.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-11-18 21:51 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-25 10:58 [Bug libstdc++/110801] New: std::format code runs slower than equivalent {fmt} code redi at gcc dot gnu.org
2023-08-15 21:42 ` [Bug libstdc++/110801] " redi at gcc dot gnu.org
2023-08-17 17:26 ` redi at gcc dot gnu.org
2023-11-18 21:44 ` cvs-commit at gcc dot gnu.org
2023-11-18 21:51 ` redi at gcc dot gnu.org

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).