Насколько эффективен std :: string по сравнению со строками с нулевым символом в конце? - PullRequest
13 голосов
/ 12 марта 2009

Я обнаружил, что std::string очень медленные по сравнению со старомодными строками с нулевым символом в конце, настолько медленные, что они значительно замедляют мою общую программу в 2 раза.

Я ожидал, что STL будет медленнее, я не знал, что это будет намного медленнее.

Я использую Visual Studio 2008, режим выпуска. Он показывает, что назначение строки в 100-1000 раз медленнее, чем назначение char* (очень сложно проверить время выполнения назначения char*). Я знаю, что это несправедливое сравнение, присвоение указателя и строковое копирование, но моя программа имеет много строковых назначений, и я не уверен, что мог бы использовать трюк " const reference " во всех местах. С реализацией подсчета ссылок моя программа была бы в порядке, но эти реализации, кажется, больше не существуют.

Мой реальный вопрос: почему люди больше не используют реализации для подсчета ссылок, и означает ли это, что всем нам нужно быть гораздо более осторожными, чтобы избежать распространенных ошибок производительности std :: string?

Мой полный код ниже.

#include <string>
#include <iostream>
#include <time.h>

using std::cout;

void stop()
{
}

int main(int argc, char* argv[])
{
    #define LIMIT 100000000
    clock_t start;
    std::string foo1 = "Hello there buddy";
    std::string foo2 = "Hello there buddy, yeah you too";
    std::string f;
    start = clock();
    for (int i=0; i < LIMIT; i++) {
        stop();
        f = foo1;
        foo1 = foo2;
        foo2 = f;
    }
    double stl = double(clock() - start) / CLOCKS\_PER\_SEC;

    start = clock();
    for (int i=0; i < LIMIT; i++) {
        stop();
    }
    double emptyLoop = double(clock() - start) / CLOCKS_PER_SEC;

    char* goo1 = "Hello there buddy";
    char* goo2 = "Hello there buddy, yeah you too";
    char *g;
    start = clock();
    for (int i=0; i < LIMIT; i++) {
        stop();
        g = goo1;
        goo1 = goo2;
        goo2 = g;
    }
    double charLoop = double(clock() - start) / CLOCKS_PER_SEC;
    cout << "Empty loop = " << emptyLoop << "\n";
    cout << "char* loop = " << charLoop << "\n";
    cout << "std::string = " << stl << "\n";
    cout << "slowdown = " << (stl - emptyLoop) / (charLoop - emptyLoop) << "\n";
    std::string wait;
    std::cin >> wait;
    return 0;
}

Ответы [ 14 ]

0 голосов
/ 12 марта 2009

Я бы сказал, что реализации STL лучше, чем традиционные реализации. Также вы пытались использовать список вместо вектора, потому что вектор эффективен для какой-то цели, а список эффективен для других

0 голосов
/ 12 марта 2009

Большая часть причины может заключаться в том, что подсчет ссылок больше не используется в современных реализациях STL.

Вот история (кто-то исправит меня, если я ошибаюсь): вначале реализации STL использовали подсчет ссылок и были быстрыми, но не поточнобезопасными - разработчики ожидали, что разработчики приложений вставят свои собственные механизмы блокировки на более высокие уровни , чтобы сделать их потокобезопасными, потому что, если блокировка выполнялась на 2 уровнях, это замедляло бы работу вдвое.

Однако программисты всего мира были слишком невежественны или ленивы, чтобы повсюду вставлять блокировки. Например, если рабочему потоку в многопоточной программе необходимо прочитать параметр командной строки std :: string, потребуется блокировка даже для чтения строки, в противном случае может произойти сбой. (2 потока увеличивают счетчик ссылок одновременно на разных процессорах (+1), но уменьшают его отдельно (-2), поэтому счетчик ссылок уменьшается до нуля, и память освобождается.)

Таким образом, разработчики прекратили подсчет ссылок и вместо этого каждый std :: string всегда имел собственную копию строки. Работало больше программ, но все они были медленнее.

Так что теперь даже скромное присвоение одной std :: string другой (или, что то же самое, передача std :: string в качестве параметра функции), требует около 400 инструкций машинного кода вместо 2, которые требуются для назначить символ *, замедление в 200 раз.

Я проверил величину неэффективности std :: string в одной основной программе, общее замедление которой составило около 100% по сравнению со строкой с нулевым символом в конце. Я также протестировал необработанное назначение std :: string, используя следующий код, в котором говорилось, что назначение std :: string было в 100-900 раз медленнее (У меня были проблемы с измерением скорости присваивания char *). Я также отлаживал в функции std :: string operator = () - я оказался в стеке по колено, около 7 слоев, прежде чем нажать «memcpy ()».

Я не уверен, что есть какое-то решение. Возможно, если вам нужна ваша программа, чтобы быть быстрой, использовать старый добрый C ++, и если вы больше беспокоитесь о своей производительности, вам следует использовать Java.

#define LIMIT 800000000
clock_t start;
std::string foo1 = "Hello there buddy";
std::string foo2 = "Hello there buddy, yeah you too";
std::string f;

start = clock();
for (int i=0; i < LIMIT; i++) {
    stop();
    f    = foo1;
    foo1 = foo2;
    foo2 = f;
}
double stl = double(clock() - start) / CLOCKS_PER_SEC;

start = clock();
for (int i=0; i < LIMIT; i++) {
    stop();
}
double emptyLoop = double(clock() - start) / CLOCKS_PER_SEC;

char* goo1 = "Hello there buddy";
char* goo2 = "Hello there buddy, yeah you too";
char *g;

start = clock();
for (int i=0; i < LIMIT; i++) {
    stop();
    g = goo1;
    goo1 = goo2;
    goo2 = g;
}
double charLoop = double(clock() - start) / CLOCKS_PER_SEC;

TfcMessage("done", 'i', "Empty loop = %1.3f s\n"
                        "char* loop = %1.3f s\n"
                        "std::string loop = %1.3f s\n\n"
                        "slowdown = %f", 
                        emptyLoop, charLoop, stl, 
                        (stl - emptyLoop) / (charLoop - emptyLoop));
0 голосов
/ 12 марта 2009

При правильном использовании std :: string так же эффективен, как char *, но с дополнительной защитой.

Если у вас проблемы с производительностью в STL, скорее всего, вы делаете что-то не так.

Кроме того, реализации STL не являются стандартными для всех компиляторов. Я знаю, что STI и STLPort SGI в целом работают хорошо.

Тем не менее, и я очень серьезно, вы могли бы быть гением C ++ и разработать код, который гораздо более сложный, чем STL. Это маловероятно, но кто знает, вы могли бы быть Леброном Джеймсом C ++.

0 голосов
/ 12 марта 2009

Они не ошиблись. Реализация STL, как правило, лучше вашей.

Я уверен, что вы можете написать что-то лучше для очень конкретного случая, но фактор 2 - это слишком много ... вы действительно должны делать что-то не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...