在 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可能 scanf 比使用流要快一些。尽管流提供了很多类型安全性,并且不必在运行时解析格式字符串,但它通常具有不需要过多内存分配的优点(这取决于您的编译器和运行时)。也就是说,除非性能是您唯一的最终目标并且您处于关键路径中,否则您应该真正偏爱更安全(更慢)的方法。
Herb Sutter "The String Formatters of Manor Farm" 在这里写了一篇非常美味的文章,他详细介绍了字符串格式化程序(如 sscanf
和 lexical_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】:一般使用的语句cin
和cout
似乎比C++ 中的scanf
和printf
慢,但实际上它们更快!
问题是:在 C++ 中,每当您使用 cin
和 cout
时,默认情况下都会发生同步过程,以确保如果您在程序中同时使用 scanf
和 cin
,那么它们都会彼此同步工作。此同步过程需要时间。因此cin
和cout
看起来要慢一些。
但是,如果将同步过程设置为不发生,则cin
比scanf
快。
要跳过同步过程,请在您的程序中在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】:即使scanf
比cin
快,也没关系。大多数情况下,您将从硬盘驱动器或键盘读取。将原始数据导入您的应用程序所花费的时间比 scanf
或 cin
处理它所花费的时间要多几个数量级。
【讨论】:
IPC通过管道呢?您认为那里的性能可能会受到明显影响吗? 即使使用 IPC 通过管道,进出内核的时间也比仅使用 scanf/cin 解析要多得多。 我在这方面做了测试,当然 cout 和 cin 的性能很差。虽然对于用户输入来说可以忽略不计,但对于性能很重要的事情肯定不是这样。不过,还有其他更快的 c++ 框架。 问题是iostream
是比硬盘慢。是的,这太糟糕了。以上是关于在 C++ 程序中使用 scanf() 比使用 cin 更快?的主要内容,如果未能解决你的问题,请参考以下文章