14

(免責事項:C ++標準がこれについて何を言っているのかわかりません..私は恐ろしいです)

非常に大きな文字列を操作しているときに、std::stringがコピーオンライトを使用していることに気付きました。観察された動作を再現する最小のループを作成することができました。たとえば、次のループは疑わしいほど高速に実行されます。

#include <string>
using std::string;
int main(void) {
    string basestr(1024 * 1024 * 10, 'A');
    for (int i = 0; i < 100; i++) {
        string a_copy = basestr;
    }
}

ループ本体a_copy[1] = 'B';に書き込みを追加すると、実際のコピーが行われたようで、プログラムは数ミリ秒ではなく0.3秒で実行されました。100回の書き込みで約100倍遅くなりました。

しかし、それからそれは奇妙になりました。一部の文字列は、書き込みも読み取りも行われず、実行時間には反映されませんでした。実行時間は、文字列に対する操作の数にほぼ正確に比例していました。少し掘り下げてみると、文字列から読み取るだけでもパフォーマンスが低下することがわかりました。そのため、GNU STL文字列がコピーオンリード(?)を使用していると思いました。

#include <string>
using std::string;
int main(void) {
    string basestr(1024 * 1024 * 10, 'A');
    for (int i = 0; i < 100; i++) {
        string a_copy = basestr;
        a_copy[99]; // this also ran in 0.3s!
    }
}

しばらくの間、発見を楽しんだ後、ベース文字列からの読み取り(operator []を使用)も、おもちゃのプログラム全体で0.3秒かかることがわかりました。これには100%満足していません。STL文字列は実際にコピーオンライトですか、それともコピーオンライトを許可していますか?私は、operator []が、それが返す参照を保持し、後でそれに書き込む人に対して、いくつかの安全策を持っていると思うようになりました。これは本当に本当ですか?そうでない場合、実際に何が起こっているのでしょうか。誰かがC++標準の関連するセクションを指すことができれば、それもありがたいです。

参考までに、とGNUSTLを使用g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3しています。

4

3 に答える 3

14

operator[]C ++は、 forの読み取りと書き込みを区別しませんが、 operator[]for constオブジェクトと可変(非const)オブジェクトのみを区別します。a_copyは可変であるため、可変がoperator[]選択されます。この演算子は(可変)参照を返すため、コピーが強制されます。

効率が懸念される場合は、をにキャストしa_copyて、のバージョンconst stringを強制的に使用することができます。これにより、内部バッファーのコピーは作成されません。constoperator[]

char f = static_cast<const string>(a_copy)[99];
于 2010-11-01T08:11:59.227 に答える
13

C ++標準では、コピーオンライトやその他の実装の詳細を禁止または義務付けていませんstd::string。セマンティクスと複雑さの要件が満たされている限り、実装は好きな実装戦略を選択できます。

operator[]const文字列では、文字列を変更する次の操作までの任意の時点で文字列を変更するために使用できる参照を返すため、事実上「書き込み」操作であることに注意してください。このような変更によってコピーが影響を受けることはありません。

これら2つのうちの1つをプロファイリングしてみましたか?

const string a_copy = basestr;
a_copy[99];

または

string a_copy = basestr;
const std::string& a_copy_ref = a_copy;
a_copy_ref[99];
于 2010-11-01T08:14:49.787 に答える
2

このコードを試してください:

#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

template<typename T>
void dump(std::ostream & ostr, const T & val)
{
    const unsigned char * cp = reinterpret_cast<const unsigned char *>(&val);
    for(int i=0; i<sizeof(T); i++)
        ostr
            << setw(2) << setfill('0') << hex << (int)cp[i] << ' ';
    ostr << endl;
}

int main(void) {
    string a = "hello world";
    string b = a;
    dump(cout,a);
    dump(cout,b);

    char c = b[0];

    dump(cout,a);
    dump(cout,b);
}

GCCでは、これは私が得る出力です:

3c 10 51 00
3c 10 51 00
3c 10 51 00
5c 10 51 00

これは、はい、この場合は読み取り時にコピーされていることを示しているように思われます。

于 2010-11-01T09:07:57.770 に答える