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



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




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




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

851   13  

13 ответов:

вот быстрый тест простого случая: программа для чтения списка чисел из стандартного ввода и 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

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

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


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

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

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

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

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

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

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

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

std::ios::sync_with_stdio(false);

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

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

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

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

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

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

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

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

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

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

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

Если вы заботитесь о производительности и форматировании строк, посмотрите на Фастформат Мэтью Уилсона библиотека.

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

да 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;
}

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

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

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

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

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

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

std::ios::sync_with_stdio(false);

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

#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сек код с заняла 2 сек.

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

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

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

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

  • используйте c++ и используйте всю его силу (и тяжесть/медлительность), чтобы решить проблему
  • если вы получаете ограниченное время, то измените cins и couts для printfs и scanfs (если вы напортачили с помощью строки класса, выполните печать следующим образом: printf(%s,mystr.c_str ());
  • если вы все еще получаете ограниченное время, то попробуйте сделать некоторые очевидные оптимизации (например, избегая слишком много встроенных для/while / dowhiles или рекурсивные функции). Также не забудьте пройти мимо ссылочных объектов, которые слишком велики...
  • если вы все еще получаете ограниченное время, то попробуйте изменить std::векторы и наборы для C-массивов.
  • если вы все еще получаете ограниченное время, а затем перейти к следующей проблеме...

Comments

    Ничего не найдено.