Использование scanf () в программах на C ++ быстрее, чем использование cin? - PullRequest
106 голосов
/ 25 июня 2009

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

Проверьте ваши методы ввода / вывода. В C ++ использование cin и cout слишком медленное. Используйте их, и вы гарантированно не сможете решить ни одной проблемы с приличным количеством ввода или вывода. Вместо этого используйте printf и scanf.

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

Ответы [ 12 ]

195 голосов
/ 06 октября 2012

Вот быстрый тест простого случая: программа для чтения списка чисел со стандартного ввода и XOR всех чисел.

версия iostream:

#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:

#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;
}

Результаты

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

iostream version:  24.3 seconds
scanf version:      6.4 seconds

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

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


РЕДАКТИРОВАТЬ: Пользователь clyfish указывает ниже , что разница в скорости в значительной степени обусловлена ​​функциями ввода-вывода iostream, поддерживающими синхронизацию с функциями C I / O. Мы можем отключить это с помощью звонка на 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;
}

Новые результаты:

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

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

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

61 голосов
/ 11 апреля 2013

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 ++ документах .

42 голосов
/ 25 июня 2009

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

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

16 голосов
/ 12 октября 2009

Я только что провел вечер, работая над проблемой на 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 >> n >> m" на "scanf ("% d% d ", & n, & m)", а несколько крошечных "couts" на "printfs", и мой TLE превратился в "Accepted «!

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

6 голосов
/ 25 июня 2009

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

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

2 голосов
/ 26 апреля 2011

Да, iostream медленнее, чем cstdio.
Да, вы, вероятно, не должны использовать 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;
}
2 голосов
/ 25 июня 2009

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

1 голос
/ 25 июня 2009

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

0 голосов
/ 04 мая 2017
#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 ++.

paradox@scorpion 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 секунды.

0 голосов
/ 19 октября 2014

Операторы cin и cout в общем использовании кажутся медленнее, чем scanf и printf в C ++, но на самом деле они БЫСТРЕЕ!

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

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

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

std::ios::sync_with_stdio(false);

Посетите этот сайт для получения дополнительной информации.

...