ほとんどの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 && ./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
が1回、次に0x7b0c68
が1回、0x7b0c38
が2回、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はnot小さな文字列の最適化をサポートします。
GCC5はそれをサポートし始めました。 isocpp 状態:
Std :: stringの新しい実装は、コピーオンライト参照カウントの代わりに小さい文字列の最適化を使用して、デフォルトで有効になっています。
これは私の主張を裏付けるものです。
さらに、 std :: stringの探索 言及:
ご覧のとおり、古いlibstdc ++はコピーオンライトを実装しているため、小さなオブジェクトの最適化を利用しないのは理にかなっています。
そして、GCC5が登場すると、彼はコンテキストを変更します。