Мне захотелось наконец разобраться в способах ввода-вывода в C++, какой из них быстрее и удобнее. Поэтому я написал прогу, которая проверит скорость работы основных способов ввода/вывода в C++. Вывод программы
Итак, разберем все тесты по порядку:
Вывод программы
Test: (printf, 10000000 ints) 2.50cTest: (cout, 10000000 ints) 3.02cTest: (write, 10000000 ints, 104827485 chars) 2.88c + 0.25cTest: (scanf, 10000000 ints) 2.61cTest: (fwrite, 10000000 ints, 104827485 chars) 2.88c + 0.26cTest: (cin, 10000000 ints) 8.32cTest: (printf, 100000000 chars) 1.87cTest: (scanf, 100000000 chars) 8.44cTest: (cout, 100000000 chars) 6.98cTest: (cin, 100000000 chars) 12.51cTest: (putchar, 100000000 chars) 2.26cTest: (getchar, 100000000 chars) 1.99cTest: (read, 100000000 chars) 0.08c + 0.57cTest: (fread, 100000000 chars) 0.08c + 0.59c
Разбор тестов
- printf вывел 107 целых чисел. Относительно быстро и удобно. Вполне можно использовать.
- Тоже самое сделал cout. Немного медленнее, но это когда как. Иногда cout работает довольно медленно.
- write, как функция ostream. Работает быстро. Очень быстро. Похоже, что в основе лежит fwrite. Первое время - преобразование чисел в строку. Второе - вывод. Примечательно, что вывод куда быстрее.
- scanf. Работает ровно также как и printf. Сносно и удобно в использовании.
- fwrite. Функция из C. Работает идентично write и сравнимо с ней по сложности использования. Ну может чуть чуть сложнее.
- Вот и первый fail. cin считывает числа медленно. Раза в 4 медленнее, чем все остальное. Если вам в программе требуется считать миллион чисел, готовьтесь потратить на это 0.8 секунд cin-а. А возможно и больше.
- Переходим к символам. printf выводит символы чуть быстрее, чем числа. Видимо, из-за отсутствия необходимости преобразовывать число.
- А вот это стало неожиданностью. scanf на редкость медленно читает символы. Файлик был размером около 100Мб, но все равно мог бы и побыстрее.
- cout выводит символы медленно. В 3 раза медленнее, чем printf.
- cin считывает символы совсем медленно. У меня уходило 1.2 секунды на каждые 10Мб. Есть и еще один сюрприз. Он выкидывает whitespace. Даже когда считывает один символ. Про это надо не забывать. Лечится так: cin >> noskipws;
- putchar. Предназначен для вывода одного символа. И это у него выходит прекрасно.
- getchar. Предназначен для ввода одного символа. И также он это делает очень быстро.
- Наконец, блочный ввод-вывод. Опять указано два параметра. Первый - время считывания. Второй - проверка на правильность считывания. Второй нужен только для сравнения. Данные предсказуемо совпадают.
- См. выше
Вывод
- Для чисел лучше использовать cin(если время не очень критично), или printf(если критично).
- Для символов лучше использовать getchar и putchar.
- Что-то мне не верится в возможность считать 100Мб данных в оперативку за 0.08 секунд. Есть версия, что файл был кеширован в оперативке. Кто знает, как это проверить и как исправить - пишите в комменты.
- Надо доделать тесты для строк и дробного типа. Но это уже попозже.
Тем, кто хочет проверить это на своем компьютере
Пожалуйста. Вот программа: http://pastebin.com/DpmjHF7C. Учтите, что она заменит файл temp.txt на свой, а также ей надо 100Мб свободного места и 200Мб оперативки. И пришлите мне вывод программы в комменты или в личку. Под влиянием этих данных, я возможно, изменю разбор тестов. Большое спасибо всем, кто запустит прогу у себя и пришлет мне результаты, а также тем, кто укажет мне на неточности в программе или предложит другие тесты.
Да, правильно понял) Я просто гуглил и нашёл выражение "левый пустой символ")
Спасибо, сейчас попробую.
Меня вот это удивило:
Почему же, интересно, printf обошел putchar...
2)
Быстрее (не медленнее) scanfа и printfа числа читает и пишет вот эта жесть:
Время действительно с 0.062с уменьшилось до рекордных 0.046с.
Это было 30 июля. А сейчас с новым компилятором что будет? Очень интересно. :) Мои шаман-решения теперь работают в шесть раз дольше того, чем было. :(
o.O
Test 1: (10000000 ints, printf) 2.50c
Тогда будет проще ссылаться на номер теста и, возможно, проще смотреть.
А сдать обязательно попробую. :)
char buffer[100500];
gets(buffer);
НЕ РЕКОМЕНДУЕТСЯ писать scanf("\n"); так как он прочитает все пробельные символы (т.е. может сразу несколько пустых строчек зохавать).
2)
while ( scanf("%d", &n) == 1 ) {
...
}
Сканф возвращает количество удачно прочитанных аргументов.
Если он не может прочитать то, что его просят (если там один аргумент), то он возвращает 0.
Если и прочитать ничего не удалось, и настал конец файла – сканф будет выдавать -1.
ПОЭТОМУ не стоит писать так:
while ( scanf("%d", &n) )
Много раз сам валился на этом. :)
А потом в Сазанке у нас упала программа вот с таким тестом.
3
a b c
d e f
scanf("\n") прочитал аж до начала третей строчки. Потом три gets() в цикле выдали 3ю, 4ю и 4ю (!) строчки у нас был стабильно WA.
2) Вот теперь я неправильно прочитал))
Привожу рабочий код sscanf (спасибо Foton за по сути его написание :)
char buffer[10000], *iter, s[1000];
int main(){
gets(buffer);
iter = buffer;
while ( *iter == ' ' )
iter++;
while ( sscanf(iter, "%s", &s) == 1 )
{
sscanf(s, "%d", &a);
printf("%d ", a);
iter += strlen(s);
while ( *iter == ' ' )
iter++;
}
return 0;
}
#include <cstdio>
#include <string.h>
int a;
то оно скомпилируется.
вот:
а что такое release mode, тут немного погуглил(оказывается есть debug mode) нашел на англ яз с которым у меня не сложились отношения, можно в двух слова в чем отличие?
спс за инфу
у меня codeblocks 8.02 не нашел там ничего такого:(
Test: (printf, 10000000 ints) 11.34c
Test: (cout, 10000000 ints) 27.69c
Test: (write, 10000000 ints, 56609008 chars) 6.67c + 1.55c
Test: (scanf, 10000000 ints) 7.17c
Test: (fwrite, 10000000 ints, 56609008 chars) 6.67c + 1.31c
Test: (cin, 10000000 ints) 31.34c
Test: (printf, 100000000 chars) 41.64c
Test: (scanf, 100000000 chars) 52.09c
Test: (cout, 100000000 chars) 25.36c
Test: (cin, 100000000 chars) 69.89c
Test: (putchar, 100000000 chars) 14.25c
Test: (getchar, 100000000 chars) 18.23c
Test: (read, 100000000 chars) 1.55c + 0.53c
Test: (fread, 100000000 chars) 1.17c + 0.47c
scanf:
всё про сканф
printf:
всё про принтф
cin:
...
cout:
...
gectchar:
...
putchar:
...
gets:
...
puts:
...
read:
...
fread:
...
И одинаковые тесты (по возможности) для каждого метода ввода и вывода.
А вообще да, у меня медлене машинка.
Кстати, это release...
var = get();
inline int get()
{
for (C = getchar(); (C < 48 || C > 57) && C != '-'; C = getchar());
if (C == '-')
neg = 1,
C = getchar();
else
neg = 0;
x = 0;
for (; 47 < C && C < 58; C = getchar())
x = x*10 + C - 48;
if (neg)
return -x;
else
return x;
}
Функция get() читает по одному символу и преобразовывает эти символы в число.
Тэстил. Заметно ускоряет время.
Если говорить об олимпиадах, то на контесте как будто больше делать нечего... обычно авторы олимпиадных задач вполне нормальные люди и считывание данных уложится в адекватное время.
Писал долго SRMы в NetBeans на Java и был доволен, но сегодня меня Java по некоторым причинам разочаровала и не в первый раз, так что я решил вернуться к С++.
Теперь вопрос: как сделать, чтобы в режиме отладки окно консоли не закрывалось мгновенно? Я использую плагин moj, но не знаю, как его модифицировать, чтоб вписать туда что-то типа system("pause");
Второй вопрос. Где нужно поставить галочку в настройках, если это вообще возможно, чтобы закрывающиеся скобки (и обычные, и фигурные) добавлялись автоматически? Особо хочется добавления фигурных скобок - в NetBeans привык и теперь совсем непривычно их самому ставить.
Третий вопрос. Какой самый правильный способ перевода строки в число и обратно и чтения чисел из строки?
А если в строке всякие там запятые, то мне надо эти чары считывать?
1) Чтение чисел из строки без тормозов:
char str[mxx]="1 2";
sscanf(str,"%d %d",&n,&m);
2) Строка в число для 64-битных чисел без тормозов:
char str[mxx]="12345678910111213";
long long big;
sscanf(str,"%I64d",&big);
Про консоль не очень понял. Если в режиме отладки, то ставишь breakpoint на последний return 0. А если просто запустить и посмотреть что вывелось на консоль, то как вариант написать _getch() //#include <conio.h> . Не забыть его потом убрать!!! :)
Ладно, и sscanf как-нибудь попробую.
Ctrl + F5?
> cin считывает символы совсем медленно. У меня уходило 1.2 секунды на каждые 10Мб.
Это потому что вы неправильно используете потоки для считывания... Попробуйте так:
ну и, соответственно так:
Результат:
Просто использовать небуферизированный ввод для чтения 100МБ - это как-то не по-программистски. ;)
===
Test: (printf, 10000000 ints) 2.45c
Test: (cout, 10000000 ints) 3.08c
Test: (write, 10000000 ints, 104827485 chars) 3.13c + 0.31c
Fail with scanf
===
Строка компиляции: g++ -Wall q.cpp -o q
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Никто не в курсе почему такое происходит и как с этим бороться?
Если закомментить падающие методы, то, как и ожидалось, потоки показывают примерно такую же производительность, что и scanf/printf.
std::ios_base::sync_with_stdio(false), как видно из названия, отключает синхронизацию со стандартными потоками.
С другой стороны printf() у меня работает нормально даже с отключенной синхронизацией.
В итоге, чтобы тест прошел с std::ios_base::sync_with_stdio(false), пришлось закомментить test_scanf_int(); и test_cout_char();
Есть предположения почему?
Главный вопрос: как безопасно ускорять потоки, чтобы как минимум можно было использовать printf().
http://pastie.org/2102887
У меня есть практическая статья на эту тему, правда больше для новичков и про текстовые файлы. Советы и приёмы использования C++ I/O
Раз уж поднялась эта древняя тема.. По советам по ссылке выше
Вот такая вот конструкция
while (!std::getline(inf, name, '|').eof())
обычно не используется. Короче, да и корректнееwhile (std::getline(inf, name, '|'))
И чем же это корректней?
Тем, что читает, пока может прочитать, а не пока не конец файла.
getline() возращает поток, а не bool. То что C++ даёт возможность приводить всё что угодно к bool радует, в основном, любителей писать "магический код", вроде такого
if (w) { // CHip and DAle }
Заставляющий читающего вспоминать все слова что он знает на 'w', такие что могут быть true/false, вместо того что написать нормально
if (thread_worker != NULL) { // banana banana }
Что еще хуже, ваша замена меняет смысл. Проверяя eof() автор соглашается выйти из цикла только в случае eof(). Если произойдет другая ошибка, её можно обработать в теле цикла, и продолжить чтение, т.к. ошибка может быть в конкретных данных (а не во всех) или во временных обстоятельствах.
Есть вещи, которые становятся в языке идиомами, и их использование для профессиональных программистов не выглядит чем-то магическим.
В приведенном по ссылке коде в теле цикла нет никаких проверок. Поэтому, если произойдет другая ошибка, то он останется в цикле навсегда.
http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.5
Частично соглашусь с Petrosian.
В идеале мои собственные функции работы с потоками возвращали бы количество считанных байт/символов. Если это неприменимо (например, для чисел), пусть результатом будет bool успешности считывания.
Почему приходится вызывать 2 функции вместо одной (например, gcount после getline) для меня не очень понятно.
Но приходится работать с тем, что есть. И eof действительно недостаточно надёжная функция.
Тогда бы вы не смогли писать stream >>var1>>var2
Test: (printf, 10000000 ints) 2.15c Test: (cout, 10000000 ints) 2.36c Test: (write, 10000000 ints, 104827485 chars) 3.14c + 0.30c Test: (scanf, 10000000 ints) 3.22c Test: (fwrite, 10000000 ints, 104827485 chars) 3.14c + 0.41c Test: (cin, 10000000 ints) 8.65c Test: (printf, 100000000 chars) 2.04c Test: (scanf, 100000000 chars) 7.30c Test: (cout, 100000000 chars) 6.61c Test: (cin, 100000000 chars) 9.88c Test: (putchar, 100000000 chars) 2.10c Test: (getchar, 100000000 chars) 1.66c Test: (read, 100000000 chars) 0.04c + 0.79c Test: (fread, 100000000 chars) 0.04c + 0.79c
Hi, I found that by adding ios_base::sync_with_stdio(false); cin.tie( static_cast<ostream*>(0) );
cin performance boosts drastically. However, it makes it work wrong when used along stdio.
ios_base::sync_with_stdio(false)
turns off synchornization between stdio and iostream, iostream standard stream objects may operate independently of standard C streams, and this leads to unexpectedly interleaved characters.Try to turn on and turn off synchronization between calling tests.
sync_with_stdio()
can only be called before any input/output takes place.Checked.. Yes, you are right
Если не ошибаюсь, можно сделать такую штуку: cin.sync_with_stdio(false); cout.sync_with_stdio(false); Тогда cin и cout будут работать гараздо быстрее.
Да ладно?
Можно заменить на одну строчку
Я бы рекомендовал не воспринимать какой-либо исходный код как шаманство, которое нужно запомнить и читать документацию по тому, что вы используете.
Тогда бы вы, например, узнали, что ваши 2 строчки делают одно и тоже 2 раза. Что эквивалентно тому что сделать 1 раз. Собственно, вызывают статический метод, объект которому не нужен