在C ++程序中使用scanf()比使用cin更快?


126

我不知道这是不是真的,但是当我在一个提供问题的网站上阅读常见问题解答时,我发现了一些东西,引起了我的注意:

检查您的输入/输出方法。在C ++中,使用cin和cout太慢。使用这些,您将保证无法用大量输入或输出解决任何问题。请改用printf和scanf。

有人可以澄清一下吗?在C ++程序中使用scanf()真的比使用cin >>更快吗?如果是,那么在C ++程序中使用它是否是一个好习惯?我以为这是C特定的,尽管我只是在学习C ++。


14
我的猜测:糟糕的程序员将性能不佳归咎于标准库。有点像总是幽默的“我认为我在GCC中发现了一个错误”的哭声。
约翰·库格曼

11
@eclipse:我在比赛中处理过的ACM问题有大量的输入/输出,您的程序必须在60秒之内解决问题……在这里变成一个真正的问题。
mpen

19
---就是说,如果您需要依靠scanf()来提高性能,那么您将以错误的方式解决问题:)
mpen

4
就像观察到的一样-我在处理第二个问题(PRIME1)-使用相同的算法,两次,一次使用cin / cout,一次使用scanf / printf,第一个版本比第二个版本快(但是足够接近,以至于在统计上是不相关的)。这是标记为输入/输出密集型的问题之一,并且输入/输出的方法没有任何统计上的差异。

4
@Eclipse-感谢您提供有关测试这两种方法的信息。不过,我很伤心-我曾尝试怪罪于cin和cout,但现在我知道我的算法很烂:)
zeroDivisible 2009年

Answers:


209

这是对一个简单案例的快速测试:一个从标准输入中读取数字列表并对所有数字进行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 I / O功能与CI / 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


6
我认为使用“ endl”可能会减慢执行速度。
克里希纳·莫汉

2
std :: endl的使用不在循环中。
nibot

打开或关闭同步没有影响。怪的libc ++。它只会增强libstdc ++
iBug

您认为<cstdio>和<stdio.h>之间会有什么区别吗?
Chandrahas Aroori

iostream在一次scanf调用中解析多个整数时丢失。
Maxim Egorushkin

68

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

cin/的性能cout可能很慢,因为它们需要与基础C库保持同步。如果要同时使用C IO和C ++ IO,这是必不可少的。

但是,如果仅打算使用C ++ IO,则只需在执行任何IO操作之前使用以下行。

std::ios::sync_with_stdio(false);

有关更多信息,请参见相应的libstdc ++文档


刚刚检查了上面的行(std :: ios :: sync_with_stdio(false);),它的确使iostream几乎与cstdio一样快
gabrielhidasy

还使用cin.tie(static_cast <ostream *>(0)); 以获得更好的性能
Mohamed El-Nakib 2014年

42

可能scanf比使用流更快。尽管流提供了很多类型安全性,并且不必在运行时解析格式字符串,但它通常具有不需要过多内存分配的优点(这取决于编译器和运行时)。就是说,除非性能是您唯一的最终目标并且您处于关键位置,否则您应该真正偏爱更安全(较慢)的方法。

Herb Sutter在这里写了一篇非常美味的文章“ 庄园农场的字符串格式化程序 ”,他详细介绍了字符串格式化程序的性能,sscanf以及lexical_cast使它们缓慢或快速运行的原因是什么。这有点类似,很可能会影响C风格的IO和C ++风格之间的性能。与格式化程序的主要区别在于类型安全性和内存分配数量。


19

我只是花了一个晚上在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”!

因此,是的,它可以带来很大的不同,尤其是在时间限制较短时。


同意。同样的事情发生在我的UVA在线法官问题:陆军伙伴uva.onlinejudge.org/...
穆罕默德·埃尔- Nakib


2

有stdio实现(libio),它将FILE *实现为C ++ streambuf,将fprintf实现为运行时格式解析器。IOstream不需要运行时格式解析,所有这些都在编译时完成。因此,在共享后端的情况下,可以合理地预期iostream在运行时会更快。


我不这么认为。我认为GNU的libc是纯C和汇编语言。
克里斯·卢兹

2

是的,iostream比cstdio慢。
是的,如果您使用C ++开发,则可能不应该使用cstdio。
话虽如此,如果您不关心格式,类型安全,等等,等等,等等,还有比scanf更快的获取I / O的方法...

例如,这是一个自定义例程,用于从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;
}

1
getchar_unlocked()是非标准的,可用于gcc而非Visual Studio
Mohamed El-Nakib 2014年

1

问题在于cin涉及很多开销,因为它在scanf()调用之上为您提供了一个抽象层。你不应该使用scanf()cin,如果你正在写C ++软件,因为这是想cin是。如果您想提高性能,您可能根本不会用C ++编写I / O。


2
是否cin真的比“抽象”(在运行时)scanf?我不这么认为... scanf必须在运行时解释格式字符串,而iostream在编译时就知道格式。
nibot 2014年

1
@nibot:类型在编译时是已知的,但不是format。例如,是否期望输入为十六进制完全取决于std::istream运行时如何配置(通过I / O操纵器或通过在istream对象本身上设置标志)。一FILE*对另一方面对象有没有这样的状态,所以一个电话scanf在这方面更稳定。
dreamlax

1

语句cincout一般用法似乎比C ++ scanf和慢printf,但实际上它们更快!

关键是:在C ++中,每当您使用cin和时cout,默认情况下都会进行一个同步过程,以确保如果在程序中同时使用scanfcin,则它们两者将彼此同步。此同步过程需要时间。因此cincout出现速度较慢。

但是,如果将同步过程设置为不发生,cin则速度比快scanf

要跳过同步过程,请在以下代码的开头,在程序中包含以下代码段main()

std::ios::sync_with_stdio(false);

访问此站点以获取更多信息。


+1为您提供有关同步的说明。我刚刚关闭了同步,并在某些代码中同时使用了scanf和cin。现在我知道这是怎么回事。谢谢!
Dariush

1
#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秒。


-1

当然,在iostream上使用cstdio是荒谬的。至少当您开发软件时(如果您已经在c之上使用c ++,那么请一路使用并利用它的好处,而不是仅仅因为它的缺点而受苦)。

但是在线判断您不是在开发软件,而是在创建一个程序,该程序应该能够在3秒钟内完成Microsoft软件的工作,而这需要60秒钟才能完成!!!

因此,在这种情况下,黄金法则就像(当然,如果您不使用Java陷入更多麻烦)

  • 使用c ++并利用其所有功能(以及繁琐/缓慢)解决问题
  • 如果时间有限,请更改printfs和scanfs的cins和couts(如果使用类字符串搞砸了,请按以下方式打印:printf(%s,mystr.c_str());
  • 如果仍然有时间限制,请尝试进行一些明显的优化(例如避免为for / while / dowhiles或递归函数嵌入太多)。还请确保通过太大的引用对象...
  • 如果仍然受时间限制,请尝试更改c-arrays的std :: vectors和sets。
  • 如果您仍然受时间限制,请继续下一个问题...

-2

即使scanf比快cin,也没关系。在绝大多数情况下,您将在硬盘或键盘上进行阅读。获取原始数据到你的应用需要数量级更多的时间比它采取scanfcin对其进行处理。


通过管道的IPC呢?您认为那里可能会有明显的表现下降吗?
dreamlax

即使通过管道进行IPC,与仅使用scanf / cin进行解析相比,进出内核所花费的时间也更多。
杰伊·康罗德

8
我在这方面做过测试,当然cout和cin很烂。尽管对于用户输入而言,这可以忽略不计,但是对于性能至关重要的事情,肯定不是这样。但是,存在其他更快的c ++框架。
Johannes Schaub-litb

问题是,iostream 不是硬盘慢。是的,太烂了。
polkovnikov.ph
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.