Этот тест проверяет две принципиально разные вещи: мелкую копию и глубокую копию. Важно понимать разницу и избегать глубоких копий в C ++, поскольку объект C ++ по умолчанию обеспечивает семантику значений для своих экземпляров (как в случае с простыми старыми типами данных), что означает, что назначение одного другому обычно происходит скопировать.
Я «исправил» ваш тест и получил:
char* loop = 19.921
string = 0.375
slowdown = 0.0188244
Очевидно, что мы должны прекратить использовать строки в стиле C, так как они намного медленнее! На самом деле, я намеренно сделал свой тест таким же некорректным, как и ваш, протестировав поверхностное копирование на стороне строки и strcpy на:
#include <string>
#include <iostream>
#include <ctime>
using namespace std;
#define LIMIT 100000000
char* make_string(const char* src)
{
return strcpy((char*)malloc(strlen(src)+1), src);
}
int main(int argc, char* argv[])
{
clock_t start;
string foo1 = "Hello there buddy";
string foo2 = "Hello there buddy, yeah you too";
start = clock();
for (int i=0; i < LIMIT; i++)
foo1.swap(foo2);
double stl = double(clock() - start) / CLOCKS_PER_SEC;
char* goo1 = make_string("Hello there buddy");
char* goo2 = make_string("Hello there buddy, yeah you too");
char *g;
start = clock();
for (int i=0; i < LIMIT; i++) {
g = make_string(goo1);
free(goo1);
goo1 = make_string(goo2);
free(goo2);
goo2 = g;
}
double charLoop = double(clock() - start) / CLOCKS_PER_SEC;
cout << "char* loop = " << charLoop << "\n";
cout << "string = " << stl << "\n";
cout << "slowdown = " << stl / charLoop << "\n";
string wait;
cin >> wait;
}
Суть в том, и это на самом деле доходит до сути вашего окончательного вопроса, вы должны знать, что вы делаете с кодом. Если вы используете объект C ++, вы должны знать, что при назначении одного другому будет сделана копия этого объекта (если только назначение не отключено, в этом случае вы получите ошибку). Вы также должны знать, когда уместно использовать ссылку, указатель или интеллектуальный указатель на объект, и в C ++ 11 вы также должны понимать разницу между семантикой перемещения и копирования.
Мой настоящий вопрос: почему люди не используют подсчет ссылок?
реализации больше, и означает ли это, что мы все должны быть очень
более осторожно, чтобы избежать распространенных ошибок производительности
станд :: строка?
Люди используют реализации подсчета ссылок. Вот пример одного:
shared_ptr<string> ref_counted = make_shared<string>("test");
shared_ptr<string> shallow_copy = ref_counted; // no deep copies, just
// increase ref count
Разница в том, что строка не делает это внутренне, поскольку это было бы неэффективно для тех, кто в ней не нуждается. Такие вещи, как копирование при записи, как правило, больше не выполняются для строк по тем же причинам (плюс тот факт, что это обычно делает проблему безопасности потоков). Тем не менее, у нас есть все строительные блоки для копирования по записи, если мы хотим сделать это: у нас есть возможность менять строки без какого-либо глубокого копирования, у нас есть возможность создавать на них указатели, ссылки или умные указатели. .
Чтобы эффективно использовать C ++, вы должны привыкнуть к такому мышлению, включающему семантику значений. Если вы этого не сделаете, вы можете наслаждаться дополнительной безопасностью и удобством, но делать это с большими затратами для эффективности вашего кода (ненужные копии, безусловно, являются важной частью того, что делает плохо написанный код C ++ медленнее, чем C). В конце концов, ваш первоначальный тест все еще имеет дело с указателями на строки, а не char[]
массивами. Если вы используете массивы символов, а не указатели на них, вам также потребуется strcpy
, чтобы поменять их местами. В случае со строками у вас даже есть встроенный метод подкачки, позволяющий точно выполнять то, что вы делаете в своем тесте, поэтому я советую потратить немного больше времени на изучение C ++.