Прямой ответ: да, ничего страшного.
Многие люди разбросаны по различным идеям о том, как улучшить скорость, но, похоже, довольно много разногласий по поводу того, что наиболее эффективно. Я решил написать быструю тестовую программу, чтобы получить хоть какое-то представление о том, какие методики и для чего нужны.
#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
Как и следовало ожидать, результаты меняются, но есть несколько моментов, которые я нашел интересными:
- printf / put намного быстрее, чем cout, при записи на устройство NUL
- но cout отлично справляется с записью в реальный файл
- Довольно много предложенных оптимизаций мало что дают
- В моем тестировании fill_n примерно так же быстр, как и все остальные
- Самая большая оптимизация избегает endl
- cout.write показал самое быстрое время (хотя, вероятно, не со значительным отрывом
Я недавно отредактировал код для принудительного вызова printf
. Anders Kaseorg был достаточно любезен, чтобы указать - что g++
распознает конкретную последовательность printf("%s\n", foo);
эквивалентно puts(foo);
и генерирует код соответственно (то есть генерирует код для вызова puts
вместо printf
). Перемещение строки формата в глобальный массив и передача ее в качестве строки формата дает идентичный вывод, но вынуждает ее создавать через printf
вместо puts
. Конечно, возможно, что когда-нибудь они тоже могут оптимизироваться, но, по крайней мере, сейчас (g ++ 5.1) тест с g++ -O3 -S
подтверждает, что он на самом деле вызывает printf
(где предыдущий код скомпилирован для вызова * 1039). *).