Строка GNU STL: здесь используется copy-on-write?
(Отказ от ответственности: я не знаю, что может сказать об этом стандарт С++. Я знаю, я ужасен)
во время работы на очень больших строках я заметил, что std::string использует copy-on-write. Мне удалось написать наименьший цикл, который воспроизводит наблюдаемое поведение, а следующий, например, работает подозрительно быстро:
#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 используют copy-on-read (?).
#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!
}
}
После того, как я немного разобрался в своем открытии, я узнал, что чтение (с оператором []) из базовой строки также занимает 0,3 секунды для всей игрушечной программы. Я не на 100% комфортно с этим. Строки STL действительно копируются на чтение, или они позволяют копировать на запись вообще? Я убежден, что оператор [] имеет некоторые гарантии против того, кто сохранит ссылку, которую он вернет, а затем напишет на нее; это действительно так? Если нет, что на самом деле происходит? Если кто-то может указать на какой-то соответствующий раздел в стандарте С++, это также будет оценено.
Для справки, я использую g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
и GNU STL.
Ответы
Ответ 1
С++ не различает operator[]
для чтения и записи, а только operator[]
для объекта const и изменчивого (не const const) объекта. Поскольку a_copy
является изменяемым, выбирается изменяемый operator[]
, который заставляет копировать, потому что этот оператор возвращает (изменяемую) ссылку.
Если эффективность является проблемой, вы можете применить a_copy
к const string
, чтобы принудительно использовать версию const
operator[]
, которая не будет делать копию внутреннего буфера.
char f = static_cast<const string>(a_copy)[99];
Ответ 2
Стандарт С++ не запрещает или не предусматривает копирование на запись или любые другие детали реализации для std::string
. До тех пор, пока выполняются требования к семантике и сложности, реализация может выбрать любую стратегию реализации, которая ему нравится.
Обратите внимание, что operator[]
в строке не const
фактически является операцией "запись", поскольку она возвращает ссылку, которая может использоваться для изменения строки в любой точке до следующей операции, которая мутирует строку. Такая копия не должна подвергаться никаким копиям.
Вы пробовали профилировать один из этих двух?
const string a_copy = basestr;
a_copy[99];
или
string a_copy = basestr;
const std::string& a_copy_ref = a_copy;
a_copy_ref[99];
Ответ 3
Попробуйте этот код:
#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
Что бы показалось, что да, они копируются при чтении в этом случае.