【问题标题】:No small string optimization with gcc?没有使用 gcc 的小字符串优化?
【发布时间】:2018-02-14 13:11:46
【问题描述】:

大多数std::string 实现(包括GCC)使用小字符串优化。例如。有一个answer 讨论这个。

今天,我决定检查我编译的代码中的字符串在什么时候被移动到堆中。令我惊讶的是,我的测试代码似乎表明根本没有发生任何小的字符串优化!

代码:

#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 &amp;&amp; ./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)

【问题讨论】:

  • --with-default-libstdcxx-abi=gcc4-compatible
  • @T.C.真的吗? gcc4没有小字符串优化?
  • 我想我记得必须将小字符串优化放入(返回)到语言中
  • 感谢实际的测试代码。显然,至少一个空字符串不需要堆分配。

标签: c++ string gcc memory optimization


【解决方案1】:

你的标志之一是:

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

而 GCC4 确实支持小字符串优化。


GCC5 开始支持它。 isocpp 状态:

默认启用新的 std::string 实现,使用小字符串优化而不是写时复制引用计数。

支持我的主张。

此外,Exploring std::string 提到:

如我们所见,较旧的 libstdc++ 实现了写时复制,因此它使 感觉他们不使用小对象优化。

然后当 GCC5 发挥作用时,他会改变上下文。

【讨论】:

    【解决方案2】:

    如果调用,可以检查是否默认使用C++11 ABI

    gcc -v 2>&1 | sed -n 's/.*\(--with-default-libstdcxx-abi=new\).*/\1/p'
    

    如果您没有得到结果,则使用旧的 ABI。 (取自Conandoku)

    除了 gsamaras 给出的原因之外,旧的 ABI 也用于旧的 Redhat 版本,与 C++11 ABI 不兼容:https://bugzilla.redhat.com/show_bug.cgi?id=1546704

    【讨论】:

    • 这只有在 --with-default-libstdcxx-abi= 被显式传递给 GCC configure 脚本时才有效。
    • 有趣。你会考虑检查_GLIBCXX_USE_CXX11_ABI 更安全吗?如果是,我会改变我的答案。
    • 正确的术语会更可靠。此讨论仅与原始问题无关。我假设您发布此内容是为了自己的记录。
    猜你喜欢
    • 2011-01-11
    • 2020-11-30
    • 1970-01-01
    • 2012-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-27
    相关资源
    最近更新 更多