Смешение cout и printf для более быстрого вывода
После выполнения некоторых тестов я заметил, что printf
намного быстрее, чем cout
. Я знаю, что это зависит от реализации, но на моем Linux-сервере 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
, так как он неявно очищает буфер. Вместо этого используйте "\n"
. Может быть интересно, если вы производите большие выходы.
Ответы
Ответ 1
Прямой ответ: да, это нормально.
Многие люди бросили вокруг различных идей о том, как улучшить скорость, но, похоже, довольно много разногласий по поводу того, что наиболее эффективно. Я решил написать программу быстрого тестирования, чтобы получить хотя бы некоторое представление о том, какие методы сделали что.
#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 после компиляции с VС++ 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
Как и ожидалось, результаты меняются, но есть несколько моментов, которые я нашел интересными:
- printf/puts намного быстрее, чем cout при записи на устройство NUL
- но cout отлично переносится при записи в реальный файл
Довольно много предлагаемых оптимизаций мало
- В моем тестировании fill_n примерно так же быстро, как и все остальное
Самой большой оптимизацией является исключение endl
cout.write дал самое быстрое время (хотя, вероятно, не с существенным запасом
Недавно я редактировал код, чтобы вызвать вызов printf
. Андерс Касеорг был достаточно любезен, чтобы указать на то, что g++
распознает, что определенная последовательность printf("%s\n", foo);
эквивалентна puts(foo);
, и соответственно генерирует код (т.е. Генерирует код для вызова puts
вместо printf
). Перемещение строки формата в глобальный массив и передача этого значения в виде строки форматирования с идентичным выводом, но заставляет ее создаваться с помощью printf
вместо puts
. Разумеется, возможно, что и в этот день они могут оптимизироваться, но по крайней мере на данный момент (g++ 5.1) тест с g++ -O3 -S
подтверждает, что он фактически вызывает printf
(где предыдущий код скомпилирован для вызова puts
).
Ответ 2
Отправка std::endl
в поток добавляет newline
и очищает поток. Последующее обращение cout.flush()
является излишним. Если это было сделано при выборе времени cout
против printf
, вы не сравнивали яблоки с яблоками.
Ответ 3
По умолчанию стандартные выходные потоки C и С++ синхронизируются, так что запись на один вызывает сброс другой, поэтому явные сбросы не нужны.
Ответ 4
Также обратите внимание, что поток С++ синхронизируется с потоком C.
Таким образом, он выполняет дополнительную работу, чтобы оставаться в синхронизации.
Еще одна вещь, которую нужно отметить, - убедиться, что вы очищаете потоки равной суммы. Если вы постоянно очищаете поток от одной системы, а не от другой, которая будет определенно влиять на скорость тестов.
Прежде чем считать, что один быстрее, чем другой, вы должны:
- un-sync С++ I/O из C I/O (см. sync_with_stdio()).
- Убедитесь, что количество флешей сопоставимо.
Ответ 5
Вы можете повысить производительность 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
, буфер сбросит цель, увеличив размер буфера.
Ответ 6
Вы можете использовать sync_with_stdio
, чтобы сделать С++ IO быстрее.
cout.sync_with_stdio(false);
Чтобы улучшить производительность с помощью cout
.
Ответ 7
Не беспокойтесь о производительности между printf
и cout
. Если вы хотите повысить производительность, отформатируйте выходной файл из неформатированного вывода.
puts("Hello World\n")
намного быстрее, чем printf("%s", "Hellow World\n")
. (В первую очередь из-за форматирования служебных данных). После того как вы выделили форматированный текст, вы можете делать трюки, например:
const char hello[] = "Hello World\n";
cout.write(hello, sizeof(hello) - sizeof('\0'));
Чтобы ускорить форматированный вывод, трюк состоит в том, чтобы выполнить все форматирование в строке, а затем использовать вывод блока со строкой (или буфером):
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('\0');
fwrite(buffer, 1, text_length, stdout);
Чтобы повысить производительность вашей программы, уменьшите количество выходных данных. Чем меньше вы выведете, тем быстрее будет ваша программа. Побочным эффектом будет то, что размер исполняемого файла также уменьшится.
Ответ 8
Ну, я не могу придумать никаких причин действительно использовать cout, чтобы быть честным. Совершенно безумно иметь огромный громоздкий шаблон, чтобы сделать что-то настолько простое, что будет в каждом файле. Кроме того, он, как и он, был сконструирован так медленно, насколько возможно, и после миллионного времени ввода < < < а затем набрав значение между ними и получив что-то lik > variableName → > по поводу аварии, я больше не хочу этого делать.
Не говоря уже о том, что вы включаете пространство имен std, мир в конце концов взорвется, и если вы не наберете свою нагрузку, становится еще более смешной.
Однако мне тоже не нравится printf. Для меня решение состоит в том, чтобы создать мой собственный конкретный класс, а затем вызвать все необходимое для этого. Тогда вы можете иметь действительно простой io любым способом, который вы хотите, и с любой реализацией, какой хотите, любым форматом, который вы хотите, и т.д. (Как правило, вы хотите, чтобы поплавки всегда были одним из способов, например, не форматировать их без каких-либо причин, поэтому в форматировании с каждым вызовом - шутка).
Итак, все, что я типа, это что-то вроде
dout + "Это более разумно, чем" + cPlusPlusMethod + "из" + debugIoType + ". ИМО как минимум";
DOUT ++;
но вы можете иметь все, что хотите. С большим количеством файлов это удивительно, насколько это улучшает время компиляции.
Кроме того, нет ничего плохого в смешении C и С++, это должно быть сделано просто смешно, и если вы используете то, что вызывает проблемы с использованием C, в первую очередь безопасно сказать, что меньше всего беспокоиться - это проблема с смешивание C и С++.
Ответ 9
Смешивание С++ и C iomethods было рекомендовано против моих книг на С++, FYI. Я уверен, что функции C преткнут в ожидании/сохранении С++.