смешивание cout и printf для более быстрого вывода

после выполнения некоторых тестов я заметил, что printf намного быстрее, чем cout. Я знаю, что это зависит от реализации, но на моем Linux box printf в 8 раз быстрее. Поэтому моя идея состоит в том, чтобы смешать два метода печати: я хочу использовать cout для простых отпечатков, и я планирую использовать printf для производства огромных выходов (обычно в цикле). Я думаю, что это безопасно делать, пока я не забуду смыть перед переключением на другой метод:

cout << "Hello" << endl;
cout.flush();

for (int i=0; i<1000000; ++i) {
    printf("World!n");
}
fflush(stdout);

cout << "last line" << endl;
cout << flush;

это нормально, как это?

обновление: Спасибо за драгоценные отзывы. Резюме ответов: если вы хотите избежать сложных решений, просто не используйте endl С cout так как он сбрасывает буфер неявно. Использовать . Это может быть интересно, если вы производите большой выходов.

9 ответов


прямой ответ: да, все в порядке.

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

#include <iostream>
#include <string>
#include <sstream>
#include <time.h>
#include <iomanip>
#include <algorithm>
#include <iterator>
#include <stdio.h>

char fmt[] = "%s\n";
static const int count = 3000000;
static char const *const string = "This is a string.";
static std::string s = std::string(string) + "\n";

void show_time(void (*f)(), char const *caption) { 
    clock_t start = clock();
    f();
    clock_t ticks = clock()-start;
    std::cerr << std::setw(30) << caption 
        << ": " 
        << (double)ticks/CLOCKS_PER_SEC << "\n";
}

void use_printf() {
    for (int i=0; i<count; i++)
        printf(fmt, string);
}

void use_puts() {
    for (int i=0; i<count; i++) 
        puts(string);        
}

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

void use_cout_unsync() { 
    std::cout.sync_with_stdio(false);
    for (int i=0; i<count; i++)
        std::cout << string << "\n";
    std::cout.sync_with_stdio(true);
}

void use_stringstream() { 
    std::stringstream temp;
    for (int i=0; i<count; i++)
        temp << string << "\n";
    std::cout << temp.str();
}

void use_endl() { 
    for (int i=0; i<count; i++)
        std::cout << string << std::endl;
}

void use_fill_n() { 
    std::fill_n(std::ostream_iterator<char const *>(std::cout, "\n"), count, string);
}

void use_write() {
    for (int i = 0; i < count; i++)
        std::cout.write(s.data(), s.size());
}

int main() { 
    show_time(use_printf, "Time using printf");
    show_time(use_puts, "Time using puts");
    show_time(use_cout, "Time using cout (synced)");
    show_time(use_cout_unsync, "Time using cout (un-synced)");
    show_time(use_stringstream, "Time using stringstream");
    show_time(use_endl, "Time using endl");
    show_time(use_fill_n, "Time using fill_n");
    show_time(use_write, "Time using write");
    return 0;
}

я запустил это в Windows после компиляции с VC++ 2013 (как x86, так и x64 версии). Вывод из одного запуска (с перенаправлением вывода на дисковый файл) выглядело это так:

          Time using printf: 0.953
            Time using puts: 0.567
   Time using cout (synced): 0.736
Time using cout (un-synced): 0.714
    Time using stringstream: 0.725
            Time using endl: 20.097
          Time using fill_n: 0.749
           Time using write: 0.499

как и ожидалось, результаты различаются, но есть несколько моментов, которые я нашел интересными:

  1. printf / puts намного быстрее, чем cout при записи на устройство NUL
  • но cout отлично держится при записи в реальный файл
  • довольно много предложенных оптимизаций мало
    • в моем тестировании, fill_n примерно так же быстро, как и все иначе!--23-->
  • крупнейший оптимизация избегая епси
  • cout.запись дала самое быстрое время (хотя, вероятно, не со значительным отрывом
  • недавно я отредактировал код, чтобы заставить вызов printf. Kaseorg Андерс был достаточно любезен, чтобы указать, что--g++ распознает определенную последовательность printf("%s\n", foo); эквивалентно puts(foo);, и генерирует код соответственно (т. е. генерирует код для вызова puts вместо printf). Перемещение format string в глобальный массив и передача его в качестве строки формата производит идентичный вывод, но заставляет его быть произведенным через printf вместо puts. Конечно, возможно, что они могут оптимизировать это когда-нибудь, но, по крайней мере, сейчас (g++ 5.1) тест с g++ -O3 -S подтверждает, что он на самом деле вызывает printf (где предыдущий код скомпилирован для вызова puts).


    отправка std::endl к потоку добавляется newline и смывает поток. Последующий вызов cout.flush() это лишнее. Если это было сделано при синхронизации cout и printf тогда вы не сравниваете яблоки с яблоками.


    по умолчанию стандартные выходные потоки C и c++ синхронизируются, так что запись в один вызывает приток другого, поэтому явные приливы не нужны.


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

    еще одна вещь, чтобы отметить, чтобы убедиться, что вы очистить потоков равное количество. Если вы постоянно промываете поток на одной системе, а не на другой, это определенно повлияет на скорость тестов.

    прежде чем предположить, что один быстрее, чем другой, вы должны:

    • un-sync C++ I / O от ввода-вывода C (см. sync_with_stdio() ).
    • убедитесь, что количество притоков сопоставимо.

    вы можете дополнительно улучшить производительность printf путем увеличения размера буфера для stdout:

    setvbuf (stdout, NULL, _IOFBF, 32768);  // any value larger than 512 and also a
                      // a multiple of the system i/o buffer size is an improvement
    

    количество вызовов операционной системы для выполнения ввода-вывода почти всегда является самым дорогим компонентом и ограничителем производительности.

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


    можно использовать sync_with_stdio чтобы сделать C++ IO быстрее.

    cout.sync_with_stdio(false);
    

    должен улучшить производительность вывода с cout.


    не беспокойтесь о производительности между printf и cout. Если вы хотите повысить производительность, отделите форматированный вывод от не форматированного вывода.

    puts("Hello World\n") гораздо быстрее, чем printf("%s", "Hellow World\n"). (в основном из-за накладных расходов на форматирование). после того, как вы изолировали форматированный от обычного текста, вы можете делать такие трюки, как:

    const char hello[] = "Hello World\n";
    cout.write(hello, sizeof(hello) - sizeof(''));
    

    чтобы ускорить форматированный вывод, трюк состоит в том, чтобы выполнить все форматирование строки, а затем использовать блок вывода с строка (или буфер):

    const unsigned int MAX_BUFFER_SIZE = 256;
    char buffer[MAX_BUFFER_SIZE];
    sprintf(buffer, "%d times is a charm.\n", 5);
    unsigned int text_length = strlen(buffer) - sizeof('');
    fwrite(buffer, 1, text_length, stdout);
    

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


    Ну, я не могу придумать никакой причины использовать cout, чтобы быть честным. Совершенно безумно иметь огромный громоздкий шаблон, чтобы сделать что-то настолько простое, что будет в каждом файле. Кроме того, это похоже на то, что он предназначен для как можно медленного ввода, и после миллионного времени вводаvariableName>>> случайно я никогда не хочу делать это снова.

    Не говоря уже если включить пространства имен std мир в конце концов, взорвется, и если вы этого не сделаете, ваша нагрузка на печать станет еще более смешной.

    однако мне тоже не нравится printf. Для меня решение состоит в том, чтобы создать свой собственный конкретный класс, а затем вызвать все, что необходимо в нем. Тогда вы можете иметь действительно простой io любым способом и с любой реализацией, которую вы хотите, независимо от форматирования и т. д. (Как правило, вы хотите, чтобы поплавки всегда были одним из способов, например, не форматировать их 800 способами без причины, поэтому ввод форматирования с каждым вызовом-это шутка).

    поэтому все, что я печатаю, это что-то вроде dout+ "это более разумно, чем" +cPlusPlusMethod+ "из"+debugIoType+". IMO по крайней мере"; dout++;

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

    кроме того, нет ничего плохого в смешивании C и C++, это должно быть сделано просто справедливо, и если вы используете вещи, которые вызывают проблемы с использованием C во-первых, можно с уверенностью сказать, что меньше всего вас беспокоит проблема смешивания C и c++.


    смешивание C++ и C iomethods было рекомендовано моими книгами на C++, FYI. Я уверен, что функции C попирают состояние, ожидаемое / удерживаемое C++.