Медленная печать C ++ - PullRequest
       48

Медленная печать C ++

7 голосов
/ 15 ноября 2009

Я заметил, что если я печатаю длинную строку (char *) с помощью cout, то, по-видимому, выводит по 1 символу за раз на экран в Windows 7, Vista и Linux (с использованием замазки), используя Visual C ++ 2008 на Windows и G ++ в линуксе Printf работает намного быстрее, я фактически переключился с cout на printf для большей части печати в моем проекте. Это сбивает с толку меня, потому что этот вопрос создает впечатление, что я единственный, у кого есть эта проблема.

Я даже написал замену cout, которая выглядит так, как будто она выбивает штаны из cout на моем компе -

class rcout
{
public:
    char buff[4096];
    unsigned int size;
    unsigned int length;

    rcout()
    {
        size = 4096;
        length = 0;
        buff[0] = '\0';
    }

    ~rcout()
    {
        printf("%s", buff);
    }

    rcout &operator<<(char *b)
    {
        strncpy(buff+length, b, size-length);
        unsigned int i = strlen(b);
        if(i+length >= size)
        {
            buff[size-1] = '\0';
            printf("%s", buff);
            b += (size-length) -1;
            length = 0;
            return (*this) << b;
        }
        else
            length += i;
        return (*this);
    }

    rcout &operator<<(int i)
    {
        char b[32];
        _itoa_s(i, b, 10);
        return (*this)<<b;
    }

    rcout &operator<<(float f)
    {
        char b[32];
        sprintf_s(b, 32, "%f", f);
        return (*this)<<b;
    }
};

int main()
{
    char buff[65536];
    memset(buff, 0, 65536);

    for(int i=0;i<3000;i++)
        buff[i] = rand()%26 + 'A';

    rcout() << buff << buff <<"\n---"<< 121 <<"---" << 1.21f <<"---\n";
    Sleep(1000);
    cout << "\n\nOk, now cout....\n\n";
    cout << buff << buff <<"\n---"<< 121 <<"---" << 1.21f <<"---\n";
    Sleep(1000);
    cout << "\n\nOk, now me again....\n\n";
    rcout() << buff << buff <<"\n---"<< 121 <<"---" << 1.21f <<"---\n";
    Sleep(1000);

    return 0;
}

Есть идеи, почему cout печатает для меня так медленно?

Ответы [ 8 ]

12 голосов
/ 15 ноября 2009

ПРИМЕЧАНИЕ : Этот экспериментальный результат действителен для MSVC. В некоторых других реализациях библиотеки результат будет другим.

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

Номер статической инструкции

Как правило, cout генерирует больше кода, чем printf. Скажем, у нас есть следующий cout код для печати в некоторых форматах.

os << setw(width) << dec << "0x" << hex << addr << ": " << rtnname <<
  ": " << srccode << "(" << dec << lineno << ")" << endl;

В компиляторе VC ++ с оптимизацией он генерирует около 188 байтов кода. Но при замене кода на printf требуются только 42 байтов.

Количество динамически исполняемых инструкций

Номер статической инструкции просто говорит о разнице статического двоичного кода. Что более важно, так это фактическое количество инструкций, которые динамически выполняются во время выполнения. Я также провел простой эксперимент:

Тестовый код:

int a = 1999;
char b = 'a';
unsigned int c = 4200000000;
long long int d = 987654321098765;
long long unsigned int e = 1234567890123456789;
float f = 3123.4578f;
double g = 3.141592654;

void Test1()
{
    cout 
        << "a:" << a << “\n”
        << "a:" << setfill('0') << setw(8) << a << “\n”
        << "b:" << b << “\n”
        << "c:" << c << “\n”
        << "d:" << d << “\n”
        << "e:" << e << “\n”
        << "f:" << setprecision(6) << f << “\n”
        << "g:" << setprecision(10) << g << endl;
}

void Test2()
{
    fprintf(stdout,
        "a:%d\n"
        "a:%08d\n"
        "b:%c\n"
        "c:%u\n"
        "d:%I64d\n"
        "e:%I64u\n"
        "f:%.2f\n"
        "g:%.9lf\n",
        a, a, b, c, d, e, f, g);
    fflush(stdout);
}

int main()
{
    DWORD A, B;
    DWORD start = GetTickCount();
    for (int i = 0; i < 10000; ++i)
        Test1();
    A = GetTickCount() - start;

    start = GetTickCount();
    for (int i = 0; i < 10000; ++i)
        Test2();
    B = GetTickCount() - start;

    cerr << A << endl;
    cerr << B << endl;
    return 0;
}

Вот результат Test1 (cout):

  • выполненной инструкции: 423 234 439

  • памяти загружается / хранится: прибл. 320 000 и 980 000

  • Истекшее время: 52 секунд

Тогда как насчет printf? Это результат Test2:

  • выполненной инструкции: 164 800 800

  • памяти загружается / хранится: прибл. 70 000 и 180 000

  • Истекшее время: 13 секунд

В этой машине и компиляторе printf был намного быстрее cout. Как в количестве выполненных инструкций, так и в количестве загрузок / хранилищ (указывает на количество пропущенных кешей) разница в 3 ~ 4 раза.

Я знаю, что это крайний случай. Также следует отметить, что cout намного проще, когда вы обрабатываете 32/64-битные данные и вам требуется независимость от 32/64-платформ. Всегда есть компромисс. Я использую cout при проверке типа очень сложно.

Хорошо, cout в MSVS просто отстой :)

8 голосов
/ 15 ноября 2009

Я бы посоветовал вам попробовать этот же тест на другом компьютере. У меня нет хорошего ответа на вопрос, почему это может происходить; все, что я могу сказать, я никогда не замечал разницу в скорости между cout и printf. Я также протестировал ваш код, используя gcc 4.3.2 для Linux, и не было никакой разницы.

При этом вы не можете легко заменить cout своей собственной реализацией. Дело в том, что cout является экземпляром std :: ostream, в котором есть много встроенных функций, необходимых для взаимодействия с другими классами, которые перегружают операторы iostream.

Edit:

Любой, кто говорит, что printf всегда быстрее, чем std::cout, просто ошибается. Я только что выполнил тестовый код, отправленный minjang, с gcc 4.3.2 и флагом -O2 на 64- немного AMD Athlon X2, и cout был на самом деле быстрее .

Я получил следующие результаты:

printf: 00:00:12.024
cout:   00:00:04.144

Всегда ли cout быстрее printf? Возможно нет. Особенно не со старыми реализациями. Но в более новых реализациях iostreams, вероятно, будут быстрее, чем stdio, потому что вместо синтаксического анализа строки формата во время выполнения компилятор знает во время компиляции, какие функции ему нужно вызвать для преобразования целых чисел / чисел с плавающей запятой / объектов в строки.

Но, что более важно, скорость printf по сравнению с cout зависит от реализации , поэтому проблема, описанная в OP, не легко объяснить.

4 голосов
/ 15 ноября 2009

Попробуйте позвонить ios::sync_with_stdio(false); перед использованием std :: cout / cin, если, конечно, вы не смешиваете stdio и iostream в своей программе, что нехорошо.

1 голос
/ 15 ноября 2009

Исходя из моего опыта в соревнованиях по программированию, printf работает быстрее, чем cout.

Я помню много раз, когда мое решение не достигло предела времени только из-за cin / cout, в то время как printf / scanf работало.

Кроме того, кажется нормальным (по крайней мере для меня), что cout медленнее, чем printf, потому что он выполняет больше операций.

0 голосов
/ 18 апреля 2015

Попробуйте использовать ios::sync_with_stdio(false);. Упомяните это перед использованием std :: cin / cout. Он не смешивает stdio или iostream, но синхронизирует стандартные потоки iostream с соответствующими им стандартными потоками c. например - std :: cin / wcin iostream синхронизируется со стандартным потоком потока c

0 голосов
/ 04 февраля 2014

Вы должны попытаться записать все свои данные сначала в ostringstream, а затем использовать cout на ostringstream str(). Я на 64-битной Windows 7 и Test1 был уже значительно быстрее, чем Test2 (ваш пробег может отличаться). Использование ostringstream для создания сначала одной строки, а затем использование cout для этого дополнительно уменьшило время выполнения Test1 примерно в 3–4 раза. Обязательно #include <sstream>.

То есть, заменить

void Test1()
{
    cout
        << "a:" << a << "\n"
        << "a:" << setfill('0') << setw(8) << a << "\n"
        << "b:" << b << "\n"
        << "c:" << c << "\n"
        << "d:" << d << "\n"
        << "e:" << e << "\n"
        << "f:" << setprecision(6) << f << "\n"
        << "g:" << setprecision(10) << g << endl;
}

с:

void Test1()
{
    ostringstream oss;
    oss
        << "a:" << a << "\n"
        << "a:" << setfill('0') << setw(8) << a << "\n"
        << "b:" << b << "\n"
        << "c:" << c << "\n"
        << "d:" << d << "\n"
        << "e:" << e << "\n"
        << "f:" << setprecision(6) << f << "\n"
        << "g:" << setprecision(10) << g << endl;
    cout << oss.str();
}

Я подозреваю, что ostringstream делает это намного быстрее из-за того, что вы не пытаетесь записывать на экран каждый раз, когда вы вызываете operator<< на cout. По опыту я также заметил, что уменьшение количества раз, когда вы пишете на экран (записывая больше сразу), увеличивает производительность (опять же, ваш пробег может отличаться).

например.,

void Foo1()
{
    for(int i = 0; i < 10000; ++i) {
        cout << "Foo1\n";
    }
}

void Foo2()
{
    std::string s;
    for(int i = 0; i < 10000; ++i) {
        s += "Foo2\n";
    }
    cout << s;
}

void Foo3()
{
    std::ostringstream oss;
    for(int i = 0; i < 10000; ++i) {
        oss << "Foo3\n";
    }
    cout << oss.str();
}

В моем случае Foo1 заняло 1092 мс, Foo2 заняло 234 мс, а Foo3 заняло 218 мс. ostingstream s ваш друг. Очевидно, что Foo2 и Foo3 требуют (тривиально) больше памяти. Чтобы сравнить это с функцией в стиле C, попробуйте sprintf в буфере, а затем запишите этот буфер, используя fprintf, и вы увидите еще большую эффективность по сравнению с Test2 (хотя для меня это только улучшило производительность Test2 около 10% или около того; cout и printf действительно разные звери под капюшоном).

Компилятор: MinGW64 (TDM и связанные библиотеки).

0 голосов
/ 15 ноября 2009

Вот хак, который должен создавать потоки c ++ так же быстро, как c printf. Я никогда не проверял это, но я верю, что это работает.

ios_base::sync_with_stdio(0);
0 голосов
/ 15 ноября 2009

Попробуйте использовать некоторые endl s или flush es, поскольку они очистят буфер cout, если ОС по какой-либо причине кэширует вывод вашей программы , Но, как говорит Чарльз, нет хорошего объяснения этому поведению, поэтому, если это не поможет, то, скорее всего, это проблема, специфичная для вашей машины.

...