Разница в производительности между файлами стилей C и С++ IO
Я всегда слышал, что операции ввода-вывода файлов на С++ намного медленнее, чем C-стиль ввода-вывода. Но я не нашел практических ссылок относительно того, насколько медленны они на самом деле, поэтому я решил проверить его на своей машине (Ubuntu 12.04, GCC 4.6.3, формат раздела ext4).
Сначала я написал файл размером ~ 900 МБ на диске.
С++ (ofstream
): 163s
ofstream file("test.txt");
for(register int i = 0; i < 100000000; i++)
file << i << endl;
C (fprintf
): 12s
FILE *fp = fopen("test.txt", "w");
for(register int i = 0; i < 100000000; i++)
fprintf(fp, "%d\n", i);
Я ожидал такой вывод, он показывает, что запись в файл намного медленнее в С++ C. Затем я читаю тот же файл, используя C и С++ I/O. Что заставило меня воскликнуть, что при чтении из файла практически нет разницы в производительности.
С++ (ifstream
): 12s
int n;
ifstream file("test.txt");
for(register int i = 0; i < 100000000; i++)
file >> n;
C (fscanf
): 12s
FILE *fp = fopen("test.txt", "r");
for(register int i = 0; i < 100000000; i++)
fscanf(fp, "%d", &n);
Итак, почему так долго приходится выполнять запись с использованием потока? Или, почему чтение с использованием потока настолько быстро по сравнению с написанием?
Вывод: Преступник - это std::endl
, как указывали ответы и комментарии. Изменение линии
file << i << endl;
в
file << i << '\n';
сократил время работы до 16 с от 163 с.
Ответы
Ответ 1
Вы используете endl
для печати новой строки. В этом проблема, потому что это больше, чем просто печать новой строки — endl
также очищает буфер, который является дорогостоящей операцией (если вы делаете это на каждой итерации).
Используйте \n
, если вы имеете в виду так:
file << i << '\n';
Кроме того, необходимо скомпилировать ваш код в режиме выпуска (например, включить оптимизацию).
Ответ 2
Нет, ввод/вывод С++ не существенно медленнее, чем Cs. Если что-либо, современная реализация должна быть немного быстрее на форматированном вводе/выводе, так как не нужно разбирать строку формата, а форматирование вместо этого определяется во время компиляции через цепочку операторов потока.
Вот несколько предостережений, которые следует учитывать в тесте:
- Скомпилируйте с полной оптимизацией (
-O3
), чтобы получить справедливое сравнение.
- Правильный тест должен оценивать предубеждения - на практике это означает, что вам нужно повторять свои тесты и чередовать их. На данный момент ваш код не устойчив к нарушениям фоновых процессов. Вы также должны сообщить сводную статистику повторных прогонов, чтобы поймать выбросы, искажающие оценки.
- Отключить синхронизацию потока С++ с потоками C (
std::ios_base::sync_with_stdio(false);
)
- Используйте
'\n'
вместо (промывки) std::endl
- Не используйте объявления
register
- это просто не имеет значения, и современные компиляторы, вероятно, все равно игнорируют его.
Ответ 3
При работе с большими файлами с fstream
убедитесь, что установить буфер потокa > 0.
В отличие от этого, отключение буферизации потоков значительно снижает производительность. По меньшей мере, реализация MSVC 2015 копирует 1 char за раз до filebuf
, когда буфер не был установлен (см. streambuf::xsputn
), что может сделать ваше приложение привязанным к процессору, что приведет к снижению ставок ввода-вывода.
const size_t bufsize = 256*1024;
char buf[bufsize];
mystream.rdbuf()->pubsetbuf(buf, bufsize);
Здесь вы можете найти полное примерное приложение .