public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory
@ 2023-08-08 10:42 janschultke at googlemail dot com
  2023-08-08 10:43 ` [Bug libstdc++/110945] " janschultke at googlemail dot com
                   ` (16 more replies)
  0 siblings, 17 replies; 18+ messages in thread
From: janschultke at googlemail dot com @ 2023-08-08 10:42 UTC (permalink / raw)
  To: gcc-bugs

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

            Bug ID: 110945
           Summary: std::basic_string::assign dramatically slower than
                    other means of copying memory
           Product: gcc
           Version: 12.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: janschultke at googlemail dot com
  Target Milestone: ---

See https://quick-bench.com/q/bqGjfyd180oOlJhiY_XnURMNKG8

Using the copy constructor performs best, and ends up using std::memcpy
internally. Even using .resize() and std::copy is much faster than .assign(),
because it is subject to more partial loop unrolling.

basic_string::assign:
https://github.com/gcc-mirror/gcc/blob/25c4b1620ebc10fceabd86a34fdbbaf8037e7e82/libstdc%2B%2B-v3/include/bits/basic_string.h#L1713C28-L1713C28

this calls the four-iterator form of .replace():
https://github.com/gcc-mirror/gcc/blob/25c4b1620ebc10fceabd86a34fdbbaf8037e7e82/libstdc%2B%2B-v3/include/bits/basic_string.h#L2378

this calls this form of _M_replace_dispatch(): (I think)
https://github.com/gcc-mirror/gcc/blob/25c4b1620ebc10fceabd86a34fdbbaf8037e7e82/libstdc%2B%2B-v3/include/bits/basic_string.tcc#L430

this calls _M_replace():
https://github.com/gcc-mirror/gcc/blob/25c4b1620ebc10fceabd86a34fdbbaf8037e7e82/libstdc%2B%2B-v3/include/bits/basic_string.tcc#L507

in this case, it should call _S_move():
https://github.com/gcc-mirror/gcc/blob/25c4b1620ebc10fceabd86a34fdbbaf8037e7e82/libstdc%2B%2B-v3/include/bits/basic_string.h#L431

this calls char_traits::move():
https://github.com/gcc-mirror/gcc/blob/25c4b1620ebc10fceabd86a34fdbbaf8037e7e82/libstdc%2B%2B-v3/include/bits/char_traits.h#L223

and that calls __builtin_memcpy()

However, I must have followed this chain of calls incorrectly, because I do not
see a call to memmove in the output assembly, and most of the time is spent
here:

>        nopl   (%rax)
>        movdqa 0x42d8a0(%rdx),%xmm0
> 63.27% movups %xmm0,(%rax,%rdx,1)
> 36.69% add    $0x10,%rdx
> 0.03%  cmp    $0x100000,%rdx

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
@ 2023-08-08 10:43 ` janschultke at googlemail dot com
  2023-08-08 10:51 ` janschultke at googlemail dot com
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: janschultke at googlemail dot com @ 2023-08-08 10:43 UTC (permalink / raw)
  To: gcc-bugs

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

Jan Schultke <janschultke at googlemail dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |missed-optimization

--- Comment #1 from Jan Schultke <janschultke at googlemail dot com> ---
Oops, I meant that it calls __builtin_memmove(). Well, neither memmove nor
memcpy are visible in the output.

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
  2023-08-08 10:43 ` [Bug libstdc++/110945] " janschultke at googlemail dot com
@ 2023-08-08 10:51 ` janschultke at googlemail dot com
  2023-08-08 10:55 ` janschultke at googlemail dot com
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: janschultke at googlemail dot com @ 2023-08-08 10:51 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #2 from Jan Schultke <janschultke at googlemail dot com> ---
Also it looks like GCC doesn't emit memcpy or memmove in either of the first
benchmarks. Those statements refer to the corresponding clang output, actually.
What's consistent for both compilers is that .assign() is dramatically slower
than any other method.

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
  2023-08-08 10:43 ` [Bug libstdc++/110945] " janschultke at googlemail dot com
  2023-08-08 10:51 ` janschultke at googlemail dot com
@ 2023-08-08 10:55 ` janschultke at googlemail dot com
  2023-08-08 11:02 ` redi at gcc dot gnu.org
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: janschultke at googlemail dot com @ 2023-08-08 10:55 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #3 from Jan Schultke <janschultke at googlemail dot com> ---
When increasing the input size to 8 MiB, the results become more similar to
what clang delivers for 1 MiB too:
https://quick-bench.com/q/DFHYW6eZq-FAif8xuLkBOPwzYWA

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (2 preceding siblings ...)
  2023-08-08 10:55 ` janschultke at googlemail dot com
@ 2023-08-08 11:02 ` redi at gcc dot gnu.org
  2023-08-08 11:03 ` redi at gcc dot gnu.org
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 11:02 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Please provide the testcase in a usable form, not just a link to an external
site (which uses its own custom benchmark macros). This is requested at
https://gcc.gnu.org/

The relevant code is:

#include <string>
#include <array>

auto make_bytes() {
  std::array<unsigned char, 1024 * 1024> result;
  for (int i = 0; i < result.size(); i++) {
    result[i] = i % 256;
  }
  return result;
}

auto raw_data = make_bytes();

static void BenchmarkAssign() {
  std::string target_data;
  target_data.assign(raw_data.begin(), raw_data.end());
}

This is not equivalent to the other forms of copying in the benchmark, because
string::assign has to handle possible aliasing. It's valid to do things like
str.assign(str.data()+1, str.data()+2).

The copy constructor doesn't have to deal with that case (the input can't
possibly alias the object under construction). The assignment operator form
also just uses the copy constructor followed by a cheap move. For the
resize+std::copy form there's a precondition on std::copy that the start of the
output range is not in the input range, which rules out the problematic forms
of aliasing.

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (3 preceding siblings ...)
  2023-08-08 11:02 ` redi at gcc dot gnu.org
@ 2023-08-08 11:03 ` redi at gcc dot gnu.org
  2023-08-08 11:16 ` redi at gcc dot gnu.org
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 11:03 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #5 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #4)
> Please provide the testcase in a usable form, not just a link to an external
> site (which uses its own custom benchmark macros). This is requested at
> https://gcc.gnu.org/

Oops, I meant https://gcc.gnu.org/bugs.html

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (4 preceding siblings ...)
  2023-08-08 11:03 ` redi at gcc dot gnu.org
@ 2023-08-08 11:16 ` redi at gcc dot gnu.org
  2023-08-08 11:17 ` redi at gcc dot gnu.org
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 11:16 UTC (permalink / raw)
  To: gcc-bugs

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

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

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

--- Comment #6 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #4)
> because string::assign has to handle possible aliasing. It's valid to do
> things like str.assign(str.data()+1, str.data()+2).

Even more problematic is something like:

str.assign(make_move_iterator(str.begin()), make_move_iterator(str.begin()+n));

or something similar with a counted_iterator wrapped in a common_iterator.

We can't rely on the iterators not being (convertible to) const char* to decide
that they don't alias the existing content, because arbitrary iterator types
could still alias the characters in the string.

It might be better to not use replace(begin(), end(), first, last) for
assign(first, last) though, because when we're replacing the entire string we
don't need the code that decides if we need to shuffle the existing content
around.

And _M_replace_dispatch creates a new copy anyway:

      _M_replace_dispatch(const_iterator __i1, const_iterator __i2,
                          _InputIterator __k1, _InputIterator __k2,
                          std::__false_type)
      {
        // _GLIBCXX_RESOLVE_LIB_DEFECTS
        // 2788. unintentionally require a default constructible allocator
        const basic_string __s(__k1, __k2, this->get_allocator());
        const size_type __n1 = __i2 - __i1;
        return _M_replace(__i1 - begin(), __n1, __s._M_data(),
                          __s.size());
      }

So maybe assign(InputIterator, InputIterator) could just do:

        basic_string&
        assign(_InputIterator __first, _InputIterator __last)
        { return assign(basic_string(__first, __last)); }

We know _M_replace_dispatch will make a copy anyway.

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (5 preceding siblings ...)
  2023-08-08 11:16 ` redi at gcc dot gnu.org
@ 2023-08-08 11:17 ` redi at gcc dot gnu.org
  2023-08-08 11:19 ` janschultke at googlemail dot com
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 11:17 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #7 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(except with correct allocator propagation)

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (6 preceding siblings ...)
  2023-08-08 11:17 ` redi at gcc dot gnu.org
@ 2023-08-08 11:19 ` janschultke at googlemail dot com
  2023-08-08 11:23 ` redi at gcc dot gnu.org
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: janschultke at googlemail dot com @ 2023-08-08 11:19 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #8 from Jan Schultke <janschultke at googlemail dot com> ---
(In reply to Jonathan Wakely from comment #4)
> Please provide the testcase in a usable form, not just a link to an external
> site (which uses its own custom benchmark macros). This is requested at
> https://gcc.gnu.org/

Thanks, I will remember to do that in the future.

> This is not equivalent to the other forms of copying in the benchmark,
> because string::assign has to handle possible aliasing. It's valid to do
> things like str.assign(str.data()+1, str.data()+2).

From what I could read in the `char_traits::move` code that presumably gets
called, this function explicitly tests for overlap between the memory regions,
and dispatches to cheap functions if possible. The input size was 8 MiB, so it
is unlikely that the overhead from this overlap detection is contributing in
any relevant capacity.

Basically, due to this overlap testing, `assign` SHOULD be just as fast as
other methods if there is no overlap (and in this case, there clearly is none).
However, it is 14x slower, so something is off.

Either I haven't followed the logic correctly, or there is a mistake in this
dispatching logic which leads to much worse performance for .assign().

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (7 preceding siblings ...)
  2023-08-08 11:19 ` janschultke at googlemail dot com
@ 2023-08-08 11:23 ` redi at gcc dot gnu.org
  2023-08-08 11:27 ` redi at gcc dot gnu.org
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 11:23 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #9 from Jonathan Wakely <redi at gcc dot gnu.org> ---
That improves things:

----------------------------------------------------------------
Benchmark                      Time             CPU   Iterations
----------------------------------------------------------------
BenchmarkInit              38136 ns        38069 ns        18243
BenchmarkAssignmentOp      45476 ns        45382 ns        15038
BenchmarkAssign            45767 ns        45653 ns        15457
BenchmarkCopy              56617 ns        56515 ns        11721

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (8 preceding siblings ...)
  2023-08-08 11:23 ` redi at gcc dot gnu.org
@ 2023-08-08 11:27 ` redi at gcc dot gnu.org
  2023-08-08 11:36 ` redi at gcc dot gnu.org
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 11:27 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #10 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jan Schultke from comment #8)
> From what I could read in the `char_traits::move` code that presumably gets
> called, this function explicitly tests for overlap between the memory
> regions, and dispatches to cheap functions if possible. The input size was 8
> MiB, so it is unlikely that the overhead from this overlap detection is
> contributing in any relevant capacity.

I think you're reading it wrong. The overlap detection in char_traits::move is
only for constant evaluation, because we can't use memmove.

The overlap detection that matters here is in _M_replace, long before we use
char_traits::move.

> Basically, due to this overlap testing, `assign` SHOULD be just as fast as
> other methods if there is no overlap (and in this case, there clearly is
> none). However, it is 14x slower, so something is off.
> 
> Either I haven't followed the logic correctly, or there is a mistake in this
> dispatching logic which leads to much worse performance for .assign().

Or the optimizers don't optimize away all the checks in _M_replace and so we
don't unroll everything to a simple memmove, but do all the runtime checks
every time. Which is what I think is happening.

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (9 preceding siblings ...)
  2023-08-08 11:27 ` redi at gcc dot gnu.org
@ 2023-08-08 11:36 ` redi at gcc dot gnu.org
  2023-08-08 11:56 ` redi at gcc dot gnu.org
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 11:36 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #11 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #6)
> And _M_replace_dispatch creates a new copy anyway:
> 
>       _M_replace_dispatch(const_iterator __i1, const_iterator __i2,
> 			  _InputIterator __k1, _InputIterator __k2,
> 			  std::__false_type)
>       {
> 	// _GLIBCXX_RESOLVE_LIB_DEFECTS
> 	// 2788. unintentionally require a default constructible allocator
> 	const basic_string __s(__k1, __k2, this->get_allocator());
> 	const size_type __n1 = __i2 - __i1;
> 	return _M_replace(__i1 - begin(), __n1, __s._M_data(),
> 			  __s.size());
>       }

When distance(k1, k2) > this->capacity() this function will make two copies of
[k1,k2) and allocate twice. So even with the checks for disjunct strings, we do
a lot more work than the copy construction benchmarks.

With this change we make a single allocation+copy and then do a cheap move
assignment:

--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -1711,4 +1711,4 @@
         basic_string&
         assign(_InputIterator __first, _InputIterator __last)
-        { return this->replace(begin(), end(), __first, __last); }
+        { return *this = basic_string(__first, __last, get_allocator()); }

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (10 preceding siblings ...)
  2023-08-08 11:36 ` redi at gcc dot gnu.org
@ 2023-08-08 11:56 ` redi at gcc dot gnu.org
  2023-08-08 12:02 ` redi at gcc dot gnu.org
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 11:56 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #12 from Jonathan Wakely <redi at gcc dot gnu.org> ---
That would also benefit this overload:

      basic_string&
      assign(initializer_list<_CharT> __l)
      { return this->assign(__l.begin(), __l.size()); }

That currently goes via replace(begin(), end(), l.begin(), l.end()) but we know
that an initializer_list cannot possibly alias the string's contents.

But we can do even better and avoid any copy if __l.size() <= capacity():

      basic_string&
      assign(initializer_list<_CharT> __l)
      {
        const size_type __n = __l.size();
        if (__n > capacity())
          *this = basic_string(__l.begin(), __l.end(), get_allocator());
        else
          {
            if (__n)
              _S_copy(_M_data(), __l.begin(), __n);
            _M_set_length(__n);
          }
        return *this;
      }

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (11 preceding siblings ...)
  2023-08-08 11:56 ` redi at gcc dot gnu.org
@ 2023-08-08 12:02 ` redi at gcc dot gnu.org
  2023-08-08 12:54 ` redi at gcc dot gnu.org
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 12:02 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #13 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #11)
> With this change we make a single allocation+copy and then do a cheap move
> assignment:
> 
> --- a/libstdc++-v3/include/bits/basic_string.h
> +++ b/libstdc++-v3/include/bits/basic_string.h
> @@ -1711,4 +1711,4 @@
>          basic_string&
>          assign(_InputIterator __first, _InputIterator __last)
> -        { return this->replace(begin(), end(), __first, __last); }
> +        { return *this = basic_string(__first, __last, get_allocator()); }

Except it's not cheap for C++98 because it's a copy, so we're back to
allocating and copying everything twice. That's solvable though.

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (12 preceding siblings ...)
  2023-08-08 12:02 ` redi at gcc dot gnu.org
@ 2023-08-08 12:54 ` redi at gcc dot gnu.org
  2023-08-08 16:04 ` redi at gcc dot gnu.org
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 12:54 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #14 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Ah, and the patch will pessimize cases like str.assign(str2.begin(),
str2.end()) where
str.capacity() >= str2.capacity().

The current implementation in terms of replace(begin(), end(), first, last)
handles that, because replace is overloaded for string iterators and pointers.

Also solvable, but it's getting more complicated.

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (13 preceding siblings ...)
  2023-08-08 12:54 ` redi at gcc dot gnu.org
@ 2023-08-08 16:04 ` redi at gcc dot gnu.org
  2023-08-17 20:31 ` cvs-commit at gcc dot gnu.org
  2023-08-17 20:40 ` redi at gcc dot gnu.org
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-08 16:04 UTC (permalink / raw)
  To: gcc-bugs

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

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

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Assignee|unassigned at gcc dot gnu.org      |redi at gcc dot gnu.org
             Status|NEW                         |ASSIGNED

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (14 preceding siblings ...)
  2023-08-08 16:04 ` redi at gcc dot gnu.org
@ 2023-08-17 20:31 ` cvs-commit at gcc dot gnu.org
  2023-08-17 20:40 ` redi at gcc dot gnu.org
  16 siblings, 0 replies; 18+ messages in thread
From: cvs-commit at gcc dot gnu.org @ 2023-08-17 20:31 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #15 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:cc3d7baf2741777e99567d4301802c99f5775619

commit r14-3306-gcc3d7baf2741777e99567d4301802c99f5775619
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Aug 8 16:31:42 2023 +0100

    libstdc++: Optimize std::string::assign(Iter, Iter) [PR110945]

    Calling string::assign(Iter, Iter) with "foreign" iterators (not the
    string's own iterator or pointer types) currently constructs a temporary
    string and then calls replace to copy the characters from it. That means
    we copy from the iterators twice, and if the replace operation has to
    grow the string then we also allocate twice.

    By using *this = basic_string(first, last, get_allocator()) we only
    perform a single allocation+copy and then do a cheap move assignment
    instead of a second copy (and possible allocation). But that alternative
    has to be done conditionally, so that we don't pessimize the native
    iterator case (the string's own iterator and pointer types) which
    currently select efficient overloads of replace which will not allocate
    at all if the string already has sufficient capacity. For C++20 we can
    extend that efficient case to work for any contiguous iterator with the
    right value type, not just for the string's native iterators.

    So the change is to inline the code that decides whether to work in
    place or to allocate+copy (instead of deciding that via overload
    resolution for replace), and for the allocate+copy case do a move
    assignment instead of another call to replace.

    For C++98 there is no change, as we can't do an efficient move
    assignment anyway, so keep the current code.

    We can also simplify assign(initializer_list<CharT>) because the backing
    array for an initializer_list is always disjunct with *this, so most of
    the code in _M_replace is not needed.

    libstdc++-v3/ChangeLog:

            PR libstdc++/110945
            * include/bits/basic_string.h (basic_string::assign(Iter, Iter)):
            Dispatch to _M_replace or move assignment from a temporary,
            based on the iterator type.

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

* [Bug libstdc++/110945] std::basic_string::assign dramatically slower than other means of copying memory
  2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
                   ` (15 preceding siblings ...)
  2023-08-17 20:31 ` cvs-commit at gcc dot gnu.org
@ 2023-08-17 20:40 ` redi at gcc dot gnu.org
  16 siblings, 0 replies; 18+ messages in thread
From: redi at gcc dot gnu.org @ 2023-08-17 20:40 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #16 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Fixed on trunk. This seems worth considering for backports later though.

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

end of thread, other threads:[~2023-08-17 20:40 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-08 10:42 [Bug libstdc++/110945] New: std::basic_string::assign dramatically slower than other means of copying memory janschultke at googlemail dot com
2023-08-08 10:43 ` [Bug libstdc++/110945] " janschultke at googlemail dot com
2023-08-08 10:51 ` janschultke at googlemail dot com
2023-08-08 10:55 ` janschultke at googlemail dot com
2023-08-08 11:02 ` redi at gcc dot gnu.org
2023-08-08 11:03 ` redi at gcc dot gnu.org
2023-08-08 11:16 ` redi at gcc dot gnu.org
2023-08-08 11:17 ` redi at gcc dot gnu.org
2023-08-08 11:19 ` janschultke at googlemail dot com
2023-08-08 11:23 ` redi at gcc dot gnu.org
2023-08-08 11:27 ` redi at gcc dot gnu.org
2023-08-08 11:36 ` redi at gcc dot gnu.org
2023-08-08 11:56 ` redi at gcc dot gnu.org
2023-08-08 12:02 ` redi at gcc dot gnu.org
2023-08-08 12:54 ` redi at gcc dot gnu.org
2023-08-08 16:04 ` redi at gcc dot gnu.org
2023-08-17 20:31 ` cvs-commit at gcc dot gnu.org
2023-08-17 20:40 ` 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).