Нет небольшой оптимизации строк с помощью gcc?

В большинстве реализаций std::string (включая GCC) используется оптимизация небольших строк. Например. там ответ обсуждает это.

Сегодня я решил проверить, в какой момент строка в компилируемом коде переместится в кучу. К моему удивлению, мой тестовый код, похоже, показывает, что ничтожная оптимизация строк не происходит вообще!

код:

#include <iostream>
#include <string>

using std::cout;
using std::endl;

int main(int argc, char* argv[]) {
  std::string s;

  cout << "capacity: " << s.capacity() << endl;

  cout << (void*)s.c_str() << " | " << s << endl;
  for (int i=0; i<33; ++i) {
    s += 'a';
    cout << (void*)s.c_str() << " | " << s << endl;
  }

}

Выходной сигнал g++ test.cc && ./a.out равен

capacity: 0
0x7fe405f6afb8 | 
0x7b0c38 | a
0x7b0c68 | aa
0x7b0c38 | aaa
0x7b0c38 | aaaa
0x7b0c68 | aaaaa
0x7b0c68 | aaaaaa
0x7b0c68 | aaaaaaa
0x7b0c68 | aaaaaaaa
0x7b0c98 | aaaaaaaaa
0x7b0c98 | aaaaaaaaaa
0x7b0c98 | aaaaaaaaaaa
0x7b0c98 | aaaaaaaaaaaa
0x7b0c98 | aaaaaaaaaaaaa
0x7b0c98 | aaaaaaaaaaaaaa
0x7b0c98 | aaaaaaaaaaaaaaa
0x7b0c98 | aaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0x7b0cd8 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0x7b0d28 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Я предполагаю, что более крупный первый указатель, т.е. 0x7fe405f6afb8, является указателем стека, а другие указывают на кучу. Выполнение этого много раз дает одинаковые результаты в том смысле, что первый адрес всегда велик, а другие меньше; точные значения обычно различаются. Меньшие адреса всегда соответствуют стандартной мощности схемы распределения 2, например. 0x7b0c38 отображается один раз, затем 0x7b0c68 отображается один раз, затем 0x7b0c38 дважды, затем 0x7b0c68 4 раза, затем 0x7b0c98 8 раз и т.д.

После прочтения ответа Говарда, используя 64-битную машину, я ожидал увидеть тот же адрес, который был напечатан для первых 22 символов, и только потом, чтобы увидеть его изменение.

Я что-то пропустил?

Кроме того, интересно, если я скомпилирую с -O (на любом уровне), я получаю значение константы маленького указателя 0x6021f8 в первом случае вместо большого значения, и этот 0x6021f8 не изменяется независимо от того, сколько раз я запускаю программу.

Вывод g++ -v:

Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/foo/bar/gcc-6.2.0/gcc/libexec/gcc/x86_64-redhat-linux/6.2.0/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../gcc-6.2.0/configure --prefix=/foo/bar/gcc-6.2.0/gcc --build=x86_64-redhat-linux --disable-multilib --enable-languages=c,c++,fortran --with-default-libstdcxx-abi=gcc4-compatible --enable-bootstrap --enable-threads=posix --with-long-double-128 --enable-long-long --enable-lto --enable-__cxa_atexit --enable-gnu-unique-object --with-system-zlib --enable-gold
Thread model: posix
gcc version 6.2.0 (GCC)

Ответ 1

Один из ваших флагов:

--with-default-libstdcxx-abi=gcc4-compatible

и GCC4 не поддерживает оптимизацию небольших строк.


GCC5 начал поддерживать его. isocpp:

Новая реализация std::string включена по умолчанию, используя небольшую оптимизацию строк вместо подсчета ссылок копирования на запись.

который поддерживает мое утверждение.

Кроме того, Изучение std::string упоминает:

Как мы видим, более старый libstdС++ реализует copy-on-write, и поэтому он делает смысл для них не использовать оптимизацию небольших объектов.

а затем он меняет контекст, когда GCC5 входит в игру.