Вот уже не ожидал, что решение задачи будет настолько зависеть от способа вывода. Сразу говорю, что в этом посте я не планирую разбирать различные аспекты языка, сравнивать что-то аналитически, утверждать что лучше, а что хуже, потому что я в этом практически не разбираюсь, а просто поделюсь впечатлениями, если это можно так назвать.
Как-то уже привык использовать только C++'шные cin и cout, ибо еще когда только учил язык только им уделялось внимание; были замечания, что использовать старые C'шные scanf-printf не стоит, лучше отдать предпочтение их "наследникам". Точнее говоря, я не просто привык их использовать, кроме них-то, я никогда и не пользовался ничем другим, потому что они (cin-cout) были, как минимум, проще для понимания мне, как начинающему программисту (хотя и сейчас я не слишком-то далеко ушел).
Собственно, сегодня решил дорешать некоторые задачи, и вот вспомнил про одну. Когда-то уже пытался сделать её, но был TL на большей части тестов, а в тот момент в голове никаких идей больше не было, ну и забил на неё временно. Сегодня вот решил вернуться и дорешать наконец.
В общем-то говоря, весь смысл этого поста в паре предложений ниже.
Сделал около полусотни попыток с разными мелкими оптимизациями (ну, насколько это можно так назвать) и вводом-выводом через cin-cout — Time Limit. Почти наугад решил попробовать C'шные функции ввода-вывода, потому видел здесь некоторые обсуждения на тему скорости их работы по сравнению с C++'шными потоками вывода. И каково же было моё удивление, когда задача залетела после этого с первой попытки, да еще и время ускорилось почти двукратно.
P.S. Пытался написать два слова через слеш \
как простой текст, но он затирается, почему-то. Например, эти два слова написаны через слэш: cin\cout. Если об этой проблеме уже где-то было написано, то извиняюсь, не видел.
Говорят,
cin.sync_with_stdio(0);
в начале программы здорово ускоряет ввод/вывод черезcin/cout
.Но только в GNU C++ — в MSVS не ускоряет.
А что в MSVS ускоряет?
Я не знаю, но этот факт экспериментально проверен. С ios_base::sync_with_stdio(false); — GNU, MSVS Без ios_base::sync_with_stdio(false); — GNU, MSVS
С ним есть несколько подстав. Например нельзя использовать endl потому что он сбрасывает флаги. И не только. Тормазит cout как раз из-за endl. Если его заменить на
"\n"
cout и printf одинаково примерно работают.В коде выше нет endl, разница в 2 раза, при чем там считывание не главная часть задачи (считывается 4*10^5 интов).
А я про вывод. Не понимаю я людей, которые принципиально не пользуются scanf. C сin тоже явно есть какая-то подстава почему он недобуферезованный.
Главная фишка endl что он делает fflush. т.е. все что записано в буфер сбрасывает на диск.
Да. И именно поэтому он тормозит. И еще он заодно все флаги сбрасывает.
Можно писать ios_base::sync_with_stdio(false), но scant/printf все равно быстрее.
Самое время научиться использовать scanf/printf. Тем более, когда-то же надо начинать. И там нет ничего сложного, а программу они и правда ускоряют по сравнению с cin/cout.
Интересно услышать мнение людей, имеющих большой опыт и олимпиадного, и промышленного программирования, насчёт такой стороны вопроса.
Насколько я понимаю, использование scanf/printf приводит к потенциальным проблемам не только по причинам
%lld
versus%I64d
. А ещё и потому, что возникает зависимость от типа в таких местах, где по современным методологиям программирования никаких зависимостей быть не должно. Писать template-ом потребует о-о-очень больших извращений, при ручной замене типа возникает лишнее место где можно налажать, и т дРазумеется, это не отменяет факта, что действительно бывают ситуации, где scanf/printf таки заметно ускоряет.
Так что, IMHO, scanf/printf должны быть исключением (а не правилом), ориентированным только на случаи: (а) ввод/вывод критично влияют на время работы программы в целом; (б) есть достаточные гарантии, что типы не будут меняться, и при этом нужно форматирование, которое printf-ом делается таки проще, например
printf("%d %2d:%02d:%02d.%03d",days,hours,minutes,seconds,milliseconds)
.Кстати, ещё одна ситуация, где scanf/printf-ами реально можно налажать — написать в Visual Studio
%lf
для long double, а потом удивляться, почему не работает под другими компиляторами.Я считаю, что промышленное и олимпиадное программирование очень сильно отличаются, поэтому мне кажется нормальным сделать различные правила. И так как для олимпиад я стараюсь следовать логике "меньше случаев => меньше думать, меньше кода => меньше шанс ошибиться", то считаю правильным в олимпиадах всегда использовать scanf/printf. Они не только быстрее, но и позволяют легко выводить/вводить в каком-нибудь отвратительном формате, что с cin/cout потребует несколько больше телодвижений. Особенно это критично при debug output'е.
Что же до совместимости разных компиляторов и переиспользования — в олимпиадах время жизни кода редко превышает 24 часа, так что об этом я вообще не беспокоюсь. Какая мне разница, что кто-то когда-нибудь не сможет заставить работать решение участника? Мне гораздо важнее сдать быстро и безбажно в одних конкретных условиях.
С точки зрения промышленного программирования я ничего сказать не могу.
Мне кажется, что ни scanf/printf, ни cin/cout к промышленному программированию практически не имеют отношения. Промышленная разработка консольных приложений — это для меня что-то труднопредставимое. Работа с файлами более вероятна, но и там это скорее двоичные файлы, а для стуктурированных данных — базы данных.
Речь, скорее, о C-style IO (printf/scanf) против C++-style IO (istream/ostream). Это не только консоль и файлы, это еще и сериализация объектов в строковое/текстовое представление. Довольно часто это нужно и в промышленном программировании.
Хм...
Разработку ядра linux Вы не считаете промышленным программированием?
А ничего, что это С, а не С++?
P.S. Извиняюсь, думал, что это ответ на другой комментарий :)
Это был комментарий к утверждению, что "ни scanf/printf, ни cin/cout к промышленному программированию практически не имеют отношения". ;)
P.S. Или по-вашему программирование на C по определению промышленным программированием быть не может, а предназначен только для студентов, которые на лавочках opensource ваяют?
Я не говорил, что нельзя привести примеры использования этих функций или потоков в промышленном программировании. Просто процент таких случаев очень мал, по-моему, по отношению ко всему промышленному программированию на С/С++.
"Процент таких случаев" в UNIX-овых прогах очень велик. Про вендовые проги я точно сказать не могу, т.к. большая часть из них распространяется без исходников и с лицензией, запрещающей анализ бинарника.
Под линукс часто делаются консольные приложения, ибо легко перенаправить вывод одной в ввод другой, или всяким утилитам типа grep, awk. Это довольно удобно, и используется повсеместно. Еще такие программы можно использовать в скриптах, запускать и получать вывод в несколько строк кода, затем снова выводить в стандартный вывод. Из нагромождения таких скриптов и программ получаются например CGI скрипты строящие веб страницу.
Есть и другие причины использования iostream в production, кроме buffer overflow и отсутствия типобезопасности. Перегрузив operator<< и operator>> в классе вы получаете средство сериализации объектов, которое можно использовать, например, вот так:
сin и cout используют потоки, должны ещё определить тип переменной с которой работают. В scanf и printf мы указываем тип переменной и поэтому scanf и printf не тратят время на определение типа пременной. Они не используют потоки. Засчёт всего этого scanf и printf выигрывают время. Но у scanf есть недостаток. Если нужно ввести массив букв известного размера, элементы которого разделены пробелом то scanf считает пробел за элемент массива и нужно писать проверку на пробел в таком случае. ( а у cin такой проблемы нет) . Зато с помощью scanf и printf можно легко узнать код почти любого символа (даже пробела и клавиши Enter).
' #include ' ' #include '
' using namespace std; ' ' int main()
' { ' ' char c; ' ' scanf("%c",&c); ' ' printf("%d",c); ' ' return 0; ' ' } '
как раз таки cin/cout оопределяет тип на этапе компиляции(1 раз), а вот scanf/printf в рантайме.
скипать/не скипать пробелы тоже можно и там и там.
scanf,printf не определяют тип. Они берут указатель со стека и записывают столько байт сколько сказали. А если ты им подсунул что-то не то — это твои проблемы.
При этом он должен распарсить строку формат, не? (Хотя не удивлюсь, если сейчас это для простых форматов уже оптимизируется)
Ну это не называется распознать тип. Хотя если формат какой-нибудь
printf("%+028I64x")
, то да это может быть не тривиально.Ну да, формально это не распознавание типа, но я имел ввиду, что нужно в рантайме выполнить некие действия, чтобы понять, каким образом следует читать/писать.
scanf(" %c")
прекрасно пропускает пробельные символы.operator>>(istream&,T)
А для более чем однострочного кода отбивайте его от края несколькими пробелами(4 точно хватает)
+1 AlexDmitriev (хороший совет) и +1 kuniavski. Спасибо за информацию. Пункт №4, №3 — для меня открытие. Спасибо за пункт №1 и особенно за пункт №2 ( проверил только что, работает ).
А еще, если нажать кнопку "Ред." в блоке вашего сообщения, то его можно изменить для лучшей читаемости:)
+1 спасибо, но iostream и cstdio так и не отобразились, к сожалению, не смотря на 4 пробела после и до строчек.
Раз уже эта извечная тема не утихает, спрошу здесь заодно. Влияет ли на скорость считывания/записи изменение размера внутреннего буфера потока в iostreams? Теоретически, если мы знаем, что будет много данных, мы можем пробовать считать больше за один системный вызов и таким образом экономить время.
Как-то так:
У себя (GCC, Linux) я пробовал разные значения от 1 до 10M и не обнаружил никакой разницы на больших массивах данных, что представляется весьма странным (
pubsetbuf
возвращает ОК).