Вопрос по c++, c, io, performance – Использование scanf () в программах на C ++ быстрее, чем использование cin?

105

Я не знаю, правда ли это, но когда я читал FAQ на одном из сайтов, посвященных проблеме, я обнаружил кое-что, что привлекло мое внимание:

Check your input/output methods. In C++, using cin and cout is too slow. Use these, and you will guarantee not being able to solve any problem with a decent amount of input or output. Use printf and scanf instead.

Может кто-нибудь уточнить, пожалуйста? Действительно используетscanf() в программах на C ++ быстрее, чем при использованииcin >> something ? Если да, то стоит ли использовать его в программах на C ++? Я думал, что это специфично для C, хотя я только изучаю C ++ ...

Мое предположение: плохой программист обвиняет стандартные библиотеки в низкой производительности. Вроде как всегда с юмором "Я думаю, что нашел ошибку в GCC" плач. John Kugelman
--- при этом если вам нужно полагаться на scanf () для дополнительного повышения производительности, вы решаете проблему неправильно :) mpen
Так же, как наблюдение - я поиграл с этим и со вторыми задачами (PRIME1) - используя один и тот же алгоритм, оба раза, один раз с использованием cin / cout и один раз с scanf / printf, и первая версия была быстрее второй (но достаточно близко, что статистически не имеет значения). Это одна из проблем, которая помечена как интенсивная для ввода / вывода, и метод ввода / вывода не имел никакой статистической разницы вообще. Eclipse
@eclipse: проблемы ACM, над которыми я работал для соревнований, имеют значительный объем ввода-вывода, и ваша программа должна решать вопросы менее чем за 60 секунд ... это становится реальной проблемой здесь. mpen
@Eclipse - спасибо за информацию о тестировании обоих методов. Мне, правда, грустно - я пытался обвинить cin и cout, но теперь я знаю, что мой алгоритм отстой :) zeroDivisible

Ваш Ответ

13   ответов
9

поговорим о преждевременной оптимизации. Если не смешная оптимизация. I / O собирается ограничить вашу программу задолго доcin >> x макс ваш четырехъядерный процессор.

Хорошо, отвращение в стороне: нет, это не хорошая практика, чтобы поменять<iostream> за<cstdio>, Находясь в C ++, используйте библиотеки C ++. Не использоватьscanf, не звониmalloc, не проходи иди, не собирай 200 долларов.

Ответ не обращает внимания на вопрос или понять его контекст. Мнения, как ..., ну, вы знаете. Измерение является королем, как и знание проблемной области. На сайтах по программированию, иногда меняя cin - & gt; Scanf являетсяsingle фактор, который перемещает ваше решение от времени до успеха.
bool $ 200 = правда;
Пожалуйста, дайте мне функцию C для сбора 200 долларов, я мог бы действительно использовать это прямо сейчас.
Почему мы не можем использовать функции C в C ++? Разве это не весь смысл взаимодействия с языком?scanf а такжеprintf избегайте выделения памяти и дорогостоящего построения объектов, что может быть критично для высокопроизводительных приложений C ++.
@John: сайт, о котором он говорит, является конкурентным программным сайтом.goal это микро-оптимизация.
1

cin имеет много накладных расходов, потому что это дает вам уровень абстракции вышеscanf() звонки. Вы не должны использоватьscanf() надcin если вы пишете программное обеспечение C ++, потому что это хотитеcin для. Если вам нужна производительность, вы, вероятно, в любом случае не будете писать ввод-вывод на C ++.

Являетсяcin действительно более "абстрактно" (во время выполнения) чемscanf? Я так не думаю ...scanf должен интерпретировать строку формата во время выполнения, тогда какiostream знает формат во время компиляции.
@nibot: Thetype известен во время компиляции, но неformat, Например, ожидается, что ввод будет шестнадцатеричным или нет, полностью зависит от того, какstd::istream настроен наruntime (с помощью манипуляторов ввода / вывода или путем установки флагов наistream сам объект).FILE* объект с другой стороны не имеет такого состояния, поэтому вызовscanf в этом плане гораздо стабильнее.
3


Да, вы, вероятно, не должны использовать cstdio, если вы разрабатываете на C ++.
Сказав это, существуют даже более быстрые способы ввода / вывода, чем scanf, если вы не заботитесь о форматировании, безопасности типов, бла, бла, бла ...

Например, это пользовательская процедура для получения числа из STDIN:

inline int get_number()
{
    int c;        
    int n = 0;

    while ((c = getchar_unlocked()) >= '0' && c <= '9')
    {
        // n = 10 * n + (c - '0');
        n = (n << 3) + ( n << 1 ) + c - '0';
    }
    return n;
}
getchar_unlocked () не является стандартным и доступен для gcc не для Visual Studio
0

cin а такжеcout в целом использование кажется медленнее, чемscanf а такжеprintf в C ++, но на самом деле они быстрее!

Дело в том, что в C ++ всякий раз, когда вы используетеcin а такжеcoutпо умолчанию происходит процесс синхронизации, который гарантирует, что если вы используете обаscanf а такжеcin в вашей программе, то они оба работают синхронно друг с другом. Этот процесс синхронизации занимает время. следовательноcin а такжеcout Похоже, будет медленнее.

Тем не менее, если процесс синхронизации не выполняется,cin быстрее чемscanf.

Чтобы пропустить процесс синхронизации, включите следующий фрагмент кода в вашу программу в самом началеmain():

std::ios::sync_with_stdio(false);

Визитэтот сайт для дополнительной информации.

Вы читали другие ответы?
0
#include <stdio.h>
#include <unistd.h>

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

static int scanuint(unsigned int* x)
{
  char c;
  *x = 0;

  do
  {
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while(c<'0' || c>'9');

  do
  {
      //*x = (*x<<3)+(*x<<1) + c - '0';
      *x = 10 * (*x) + c - '0';
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while ((c>='0' && c<='9'));

  return 0;
}

int main(int argc, char **argv) {

  int parity = 0;
  unsigned int x;

  while (1 != (scanuint(&x))) {
    parity ^= x;
  }
  parity ^=x;
  printf("%d\n", parity);

  return 0;
}

но этот код C значительно быстрее, чем более быстрая версия C ++.

[email protected] 3845568-78602a3f95902f3f3ac63b6beecaa9719e28a6d6 ▶ make test        
time ./xor-c < rand.txt
360589110

real    0m11,336s
user    0m11,157s
sys 0m0,179s
time ./xor2-c < rand.txt
360589110

real    0m2,104s
user    0m1,959s
sys 0m0,144s
time ./xor-cpp < rand.txt
360589110

real    0m29,948s
user    0m29,809s
sys 0m0,140s
time ./xor-cpp-noflush < rand.txt
360589110

real    0m7,604s
user    0m7,480s
sys 0m0,123s

Оригинальный C ++ занял 30 секунд, код C - 2 секунды.

6

так и о форматировании строк, взгляните наFastFormat Мэтью Уилсона библиотека.

изменить - ссылка на публикацию в этой библиотеке:http://accu.org/index.php/journals/1539

Согласитесь полностью. Но вы должны знать, что FastFormat предназначен только для вывода. У него нет средств ввода / чтения. (Пока нет, во всяком случае)
К сожалению, эта ссылка кажется мертвой. Вот копия Wayback Machine:web.archive.org/web/20081222164527/http://fastformat.org
42

scanf несколько быстрее, чем использование потоков. Хотя потоки обеспечивают большую безопасность типов и не требуют синтаксического анализа строк форматирования во время выполнения, обычно они имеют то преимущество, что не требуют чрезмерного выделения памяти (это зависит от вашего компилятора и времени выполнения). Тем не менее, если производительность не является вашей единственной конечной целью и вы находитесь на критическом пути, тогда вы должны действительно отдавать предпочтение более безопасным (более медленным) методам.

Здесь очень вкусная статья, написанная Хербом Саттером & quot;Струнные форматеры усадебного хозяйства& Quot; кто вдавается в подробности о производительности форматирования строк, таких какsscanf а такжеlexical_cast и какие вещи заставляли их бегать медленно или быстро. Это похоже на то, что может повлиять на производительность между стилем ввода-вывода в стиле C и стилем C ++. Основное различие с форматерами, как правило, заключается в безопасности типов и количестве выделенных памяти.

2

libio) который реализует FILE * как потоковый буфер C ++, а fprintf как анализатор формата времени выполнения. IOstream'ы не нуждаются в разборе формата во время выполнения, что все делается во время компиляции. Таким образом, при использовании общих серверных частей разумно ожидать, что iostreams быстрее во время выполнения.

Вы правы. Это либио, о котором я думал.
Я так не думаю. Я думаю, что GNU libc - это чистый C и сборка.
-2

смешно использовать cstdio вместо iostream. По крайней мере, когда вы разрабатываете программное обеспечение (если вы уже используете c ++ вместо c, то проделайте весь путь и используйте его преимущества, а не страдайте только от его недостатков).

Но, судя по интернету, вы не разрабатываете программное обеспечение, вы создаете программу, которая должна уметь делать то, что программное обеспечение Microsoft занимает за 60 секунд за 3 секунды !!!

Так что, в этом случае, золотое правило выглядит так (конечно, если вы не столкнетесь с еще большими трудностями при использовании Java)

Use c++ and use all of it's power (and heaviness/slowness) to solve the problem If you get time limited, then change the cins and couts for printfs and scanfs (if you get screwed up by using the class string, print like this: printf(%s,mystr.c_str()); If you still get time limited, then try to make some obvious optimizations (like avoiding too many embedded for/while/dowhiles or recursive functions). Also make sure to pass by reference objects that are too big... If you still get time limited, then try changing std::vectors and sets for c-arrays. If you still get time limited, then go on to the next problem...
16

работая над проблемой на UVa Online (Factovisors, очень интересная проблема, проверьте ее):

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=35&page=show_problem&problem=1080

Я получал TLE (превышено ограничение по времени) в моих представлениях. На этих веб-сайтах, посвященных решению проблем в режиме онлайн, у вас есть примерно 2-3 секунды для обработки потенциально тысяч тестовых случаев, используемых для оценки вашего решения. Для таких сложных вычислительных задач каждая микросекунда имеет значение.

Я использовал предложенный алгоритм (читайте об этом на форумах для обсуждения на сайте), но все еще получал TLE.

Я изменил только "cin & gt; & gt;" n & gt; & gt; м & Quot; to "scanf ("% d% d ", & amp; n, & amp; m)"; и несколько крошечных "кушеток" в "printfs", и мой TLE превратился в "Accepted"!

Так что да, это может иметь большое значение, особенно когда сроки невелики.

Согласен. То же самое случилось со мной в задаче UVA Online Judge: Army Buddiesuva.onlinejudge.org/…
-2

scanf были быстрее чемcinэто не имеет значения. В подавляющем большинстве случаев вы будете читать с жесткого диска или клавиатуры. Загрузка необработанных данных в ваше приложение занимает на порядки больше времени, чем занимаетscanf или жеcin обработать это.

Даже при использовании IPC по каналам на вход и выход из ядра тратится гораздо больше времени, чем на его разбор с помощью scanf / cin.
Я сделал тесты в этой области, и, конечно, Cout & amp; Чин сосать производительность. Хотя для пользовательского ввода это незначительно, это, конечно, не так для вещей, где производительность имеет значение. Существуют и другие фреймворки с ++, которые работают быстрее.
А как насчет IPC через трубы? Как вы думаете, там может быть заметное снижение производительности?
Проблема в том, чтоiostream is медленнее, чем жесткий диск. Да, это отстой.
-1: умозрительный
60

http://www.quora.com/Is-cin-cout-slower-than-scanf-printf/answer/Aditya-Vishwakarma

Производительностьcin/cout может быть медленным, потому что они должны держать себя в синхронизации с базовой библиотекой C. Это важно, если будут использоваться как C IO, так и C ++ IO.

Однако, если вы собираетесь использовать C ++ IO, просто используйте строку ниже перед любыми операциями ввода-вывода.

std::ios::sync_with_stdio(false);

Для получения дополнительной информации об этом, посмотрите на соответствующийlibstdc ++ документы.

Только что проверил строку выше (std :: ios :: sync_with_stdio (false);) И это действительно делает iostream почти так же быстро, как cstdio
также используйте cin.tie (static_cast & lt; ostream * & gt; (0)); для лучшей производительности
193

программа для считывания списка чисел из стандартного ввода и XOR всех чисел.

iostream version:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

scanf version:

#include <stdio.h>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (1 == scanf("%d", &x))
    parity ^= x;
  printf("%d\n", parity);

  return 0;
}

Results

Используя третью программу, я сгенерировал текстовый файл, содержащий 33 280 276 случайных чисел. Время выполнения:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

Изменение параметров оптимизации компилятора, по-видимому, не сильно меняет результаты.

Таким образом: разница в скорости действительно есть.

EDIT: Пользователь clyfishуказывает ниже что разница в скорости в значительной степени обусловлена функциями ввода-вывода iostream, поддерживающими синхронизацию с функциями ввода-вывода C. Мы можем отключить это с помощью звонкаstd::ios::sync_with_stdio(false);:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  std::ios::sync_with_stdio(false);

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

New results:

iostream version:                       21.9 seconds
scanf version:                           6.8 seconds
iostream with sync_with_stdio(false):    5.5 seconds

C++ iostream wins!  Оказывается, что эта внутренняя синхронизация / очистка - это то, что обычно замедляет Iostream I / O. Если мы не смешиваем stdio и iostream, мы можем отключить его, и тогда iostream будет самым быстрым.

Код:https://gist.github.com/3845568

Как вы думаете, будет ли разница между & lt; cstdio & gt; и & lt; stdio.h & gt; ??
Не имеет значения при включенной или выключенной синхронизации. В этом виноват libc ++. Это только повышает libstdc ++
Я думаю, что использование 'endl' может замедлить выполнение.
Использование std :: endl не в цикле.

Похожие вопросы