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

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

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

有人能澄清一下吗?在 C + + 程序中使用 Scanf ()真的比使用 某物快吗?如果是,那么在 C + + 程序中使用它是一个好的实践吗?我认为它是 C 特有的,虽然我只是在学习 C + + ..。

98222 次浏览

即使 scanfcin快,也没关系。绝大多数时候,你都是通过硬盘或者键盘来阅读。将原始数据输入应用程序所需的时间数量级超过了 scanfcin处理这些数据所需的时间。

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

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

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

有一些 stdio 实现(利比奥)将 FILE * 实现为一个 C + + stream buf,将 fprintf 实现为一个运行时格式解析器。IOstream 不需要运行时格式解析,这些都是在编译时完成的。因此,在共享后端的情况下,可以合理地期望 iostream 在运行时更快。

如果您同时关心性能和字符串格式设置,请查看 Matthew Wilson 的快速格式库。

Edit ——链接到该库上的 accu 出版物: http://accu.org/index.php/journals/1539

我刚花了一个晚上在 UVA Online 上解决一个问题(Factovisors,一个非常有趣的问题,看看吧) :

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

我提交的文件超过了时间限制。在这些解决问题的在线法官网站上,你有大约2-3秒的时间限制来处理潜在的数千个用于评估你的解决方案的测试案例。对于像这样的计算密集型问题,每一微秒都很重要。

我正在使用建议的算法(在网站的论坛中阅读) ,但仍然得到 TLEs。

我只是将“ cin > > n > m”更改为“ Scanf (”% d% d“ ,& n,& m)”,将少量的“ couts”更改为“ printfs”,我的 TLE 变为“ Accepted”!

所以,是的,它可以带来很大的不同,特别是当时间限制很短的时候。

用 cstdio 而不用 iostream 当然很荒谬。至少在你开发软件的时候(如果你已经在使用 c + + 而不是 c,那么就尽量使用它的好处,而不是只受它的坏处的影响)。

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

因此,在这种情况下,黄金法则是这样的(当然,如果你不会因为使用 java 而陷入更多麻烦的话)

  • 使用 c + + 并使用它所有的能量(以及沉重/缓慢)来解决问题
  • 如果时间有限,那么更改 printfs 和 Scanfs 的 cins 和 couts (如果因为使用类字符串而出错,可以这样打印: printf (% s,mystr.c _ str ()) ;
  • 如果仍然有时间限制,那么尝试进行一些明显的优化(比如避免过多嵌入/while/dowhile 或递归函数)。还要确保通过太大的引用对象..。
  • 如果仍然有时间限制,那么尝试更改 c-array 的 std: : 向量和集合。
  • 如果你仍然有时间限制,那么继续下一个问题..。

是的,我的速度比 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;
}

下面是对一个简单情况的快速测试: 一个从标准输入读取数字列表的程序,以及异或所有数字。

Istream 版本:

#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 函数与 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 + + istream 赢了!事实证明,这种内部同步/刷新正是通常减慢 iostream i/o 的原因。如果我们不混合 stdio 和 iostream,我们可以关闭它,然后 iostream 是最快的。

密码是 https://gist.github.com/3845568

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

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

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

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

要跳过同步过程,请在程序的 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;
}

文件末尾有一个 bug,但是这段 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秒。