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

Posted

技术标签:

【中文标题】在 C++ 程序中使用 scanf() 比使用 cin 更快?【英文标题】:Using scanf() in C++ programs is faster than using cin? 【发布时间】:2010-11-05 17:46:09 【问题描述】:

我不知道这是否属实,但是当我阅读有关提供问题的网站之一的常见问题解答时,我发现了一些引起我注意的东西:

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

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

【问题讨论】:

我的猜测:糟糕的程序员将性能不佳归咎于标准库。有点像总是幽默的“我想我在 GCC 中发现了一个错误”的哭声。 @eclipse:我为比赛处理的 ACM 问题有大量的输入/输出,你的程序必须在 60 秒内解决问题......问题在这里。 --- 也就是说,如果您需要依赖 scanf() 来获得额外的性能提升,那么您解决问题的方式是错误的 :) 作为一个观察 - 我玩过它,在第二个问题 (PRIME1) - 使用相同的算法,两次,一次使用 cin/cout,一次使用 scanf/printf 和第一个版本比第二个更快(但足够接近以至于在统计上不相关)。这是被标记为输入/输出密集型的问题之一,输入/输出的方法没有任何统计差异。 @Eclipse - 感谢您提供有关测试这两种方法的信息。不过我很难过 - 我试图责怪 cin 和 cout,但现在我知道我的算法很烂:) 【参考方案1】:

下面是一个简单案例的快速测试:一个从标准输入读取数字列表并对所有数字进行异或运算的程序。

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 points out below 速度差异主要是由于 iostream I/O 函数与 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

【讨论】:

我认为使用 'endl' 可能会减慢执行速度。 std::endl的使用不在循环中。 开启或关闭同步没有区别。这要怪 libc++。它只会提升 libstdc++ 你认为 之间会有什么区别吗?? iostream 在一个 scanf 调用中解析多个整数时会丢失。【参考方案2】:

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++ docs。

【讨论】:

刚刚检查了上面的行 (std::ios::sync_with_stdio(false);) 它确实使 iostream 几乎和 cstdio 一样快 也使用 cin.tie(static_cast(0));为了更好的性能【参考方案3】:

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

Herb Sutter "The String Formatters of Manor Farm" 在这里写了一篇非常美味的文章,他详细介绍了字符串格式化程序(如 sscanflexical_cast)的性能,以及是什么导致它们运行缓慢或很快。这有点类似,可能类似于会影响 C 风格 IO 和 C++ 风格之间性能的那种事情。与格式化程序的主要区别往往是类型安全和内存分配的数量。

【讨论】:

【参考方案4】:

我刚刚花了一个晚上在 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 Online Judge 问题中我也遇到了同样的情况:Army Buddies uva.onlinejudge.org/…【参考方案5】:

如果您同时关心性能和字符串格式,请查看Matthew Wilson's FastFormat 库。

edit -- 指向该库上 accu 出版物的链接:http://accu.org/index.php/journals/1539

【讨论】:

完全同意。但您需要注意 FastFormat 仅用于输出。它没有输入/读取功能。 (反正还没有) 不幸的是,该链接似乎已失效。这是一个 Wayback 机器副本:web.archive.org/web/20081222164527/http://fastformat.org【参考方案6】:

一般使用的语句cincout 似乎比C++ 中的scanfprintf 慢,但实际上它们更快!

问题是:在 C++ 中,每当您使用 cincout 时,默认情况下都会发生同步过程,以确保如果您在程序中同时使用 scanfcin,那么它们都会彼此同步工作。此同步过程需要时间。因此cincout 看起来要慢一些。

但是,如果将同步过程设置为不发生,则cinscanf 快。

要跳过同步过程,请在您的程序中在main() 的开头添加以下代码sn-p:

std::ios::sync_with_stdio(false);

访问this site了解更多信息。

【讨论】:

+1 用于您对同步的解释。 I had just turned sync off and used both scanf and cin in some code。现在我知道它出了什么问题。谢谢!【参考方案7】:

有 stdio 实现 (libio) 将 FILE* 实现为 C++ 流缓冲区,并将 fprintf 实现为运行时格式解析器。 IOstreams 不需要运行时格式解析,这一切都在编译时完成。因此,通过共享后端,可以合理地预期 iostreams 在运行时更快。

【讨论】:

我不这么认为。我认为 GNU 的 libc 是纯 C 和汇编。 这并没有让它变得更好。【参考方案8】:

是的,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;

【讨论】:

getchar_unlocked() 是非标准的,适用于 gcc 而不是 Visual Studio【参考方案9】:
#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 秒。

【讨论】:

【参考方案10】:

问题在于cin 涉及大量开销,因为它在scanf() 调用之上为您提供了一个抽象层。如果您正在编写 C++ 软件,则不应使用 scanf() 而不是 cin,因为这是需要 cin 的。如果你想要性能,你可能无论如何都不会用 C++ 编写 I/O。

【讨论】:

cin 真的比scanf 更“抽象”(在运行时)吗?我不这么认为...scanf 必须在运行时解释格式字符串,而 iostream 在编译时知道格式。 @nibot:type 在编译时是已知的,而不是 format。例如,输入是否应为十六进制完全取决于std::istream运行时 的配置方式(通过I/O 操纵器或通过在istream 对象本身上设置标志) .另一方面,FILE* 对象没有这种状态,因此在这方面对 scanf 的调用更加稳定。【参考方案11】:

当然,在 iostream 上使用 cstdio 是很荒谬的。至少在你开发软件的时候(如果你已经在使用 c++ 而不是 c,那么就一直使用它的好处,而不是只受它的缺点困扰)。

但是在网上判断你不是在开发软件,你是在创建一个程序应该可以做微软软件需要60秒才能在3秒内完成的事情!!!

所以,在这种情况下,黄金法则是这样的(当然,如果你不使用 java 遇到更多麻烦)

使用 c++ 并使用它的所有力量(以及沉重/缓慢)来解决问题 如果时间有限,请更改 printfs 和 scanfs 的 cins 和 couts (如果你使用类字符串搞砸了,打印如下: printf(%s,mystr.c_str()); 如果您仍然受到时间限制,那么请尝试进行一些明显的优化(例如避免嵌入过多的 for/while/dowhile 或递归函数)。还要确保通过太大的引用对象... 如果时间仍然有限,请尝试更改 c 数组的 std::vectors 和集合。 如果你仍然有时间限制,那么继续下一个问题......

【讨论】:

【参考方案12】:

即使scanfcin 快,也没关系。大多数情况下,您将从硬盘驱动器或键盘读取。将原始数据导入您的应用程序所花费的时间比 scanfcin 处理它所花费的时间要多几个数量级。

【讨论】:

IPC通过管道呢?您认为那里的性能可能会受到明显影响吗? 即使使用 IPC 通过管道,进出内核的时间也比仅使用 scanf/cin 解析要多得多。 我在这方面做了测试,当然 cout 和 cin 的性能很差。虽然对于用户输入来说可以忽略不计,但对于性能很重要的事情肯定不是这样。不过,还有其他更快的 c++ 框架。 问题是iostream 比硬盘慢。是的,这太糟糕了。

以上是关于在 C++ 程序中使用 scanf() 比使用 cin 更快?的主要内容,如果未能解决你的问题,请参考以下文章

C++ cin速度比scanf的解决办法:关流同步

c++接收问题为何cin能接收,scanf接收就出错?

C语言中++c与c++有啥区别?

c++中scanf和cout有啥区别

加快cin的读入速度

c++算法学习笔记1 语法