为什么在这种情况下"printf scanf"比"cout cin"慢?

Why do "printf scanf" is slower than "cout cin" in this case?

本文关键字:cin cout scanf 这种情况下 printf 为什么      更新时间:2023-10-16

我试图完成一个UVa问题:11559-事件规划。然而,我发现了一些让我困惑的事情。我用printf和scanf作为IO写了一个代码,但我在法官那里得到了"超过时间限制"。我用cin和cout修改了我的代码,然后我得到了一个"接受"。cin或cout不是比scanf和printf慢吗?这是我的密码。

"stdio.h"版本

#define inf 500000000
using namespace std;
int N, B, H, W;
int main ()
{
    while(scanf("%d %d %d %d", &N, &B, &H, &W)){
        int cost = inf;
        for(int i = 0; i < H; i++){
            int P, k ;
            scanf("%d",&P);
            for(int j = 0; j < W; j++){
                scanf("%d",&k);
                if(k >= N && N*P < cost) cost = N*P;
            }
        }
        if(cost <= B) printf("%dn",cost);
        else{
            printf("stay homen");
        }
    }
    return 0;
}

"iostream"版本

#define inf 500000000
using namespace std;
int N, B, H, W;
int main ()
{
    while(!cin.eof()){
        int cost = inf;
        cin >> N >> B >> H >> W;
        if(cin.eof()) break;
        for(int i = 0; i < H; i++){
            int P, k ;
            cin >> P;
            for(int j = 0; j < W; j++){
                cin >> k;
                if(k >= N && N*P < cost) cost = N*P;
            }
        }
        if(cost <= B) cout << cost << endl;
        else{
            cout << "stay home" << endl;
        }
    }
    return 0;
}

否,cincout与使用printf/scanf没有显著差异。然而,endl将调用ofstream::flush(),这与printf("%dn", cost); fflush(stdout);是一样的,我希望如果你这样做,它在那里也会运行得更慢。

此外,将cinscanf或类似内容混合使用会增加更多时间,因为代码必须始终"同步"两个I/O流。我建议您将scanf输入重写为:

while(scanf("%d %d %d %d", &N, &B, &H, &W) != EOF){
    int cost = inf;
    // remove scanf and cin.eof() line here
    ...
}

为了证明我的观点(至少输出):

#include <iostream>
#include <cstdio>
using namespace std;
static __inline__ unsigned long long rdtsc(void)
{
    unsigned hi, lo;
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
int main(int argc, char **argv)
{
    unsigned long long t = rdtsc();
    if (argc > 1)
    {
        for(int i = 0; i < 1000; i++)
        {
            printf("%d", i);
        }
    }
    else
    {
        for(int i = 0; i < 1000; i++)
        {
            cout << i << "n";
        }
    }
    t = rdtsc() - t; 
    cerr << "Time: " << t << endl;
}

当在没有参数(argc == 1)的情况下运行并且在有参数(argc == 2)的情况中运行时,其输出为:

$ ./a.out > foo.txt
Time: 1672894
$ ./a.out 1 > foo.txt
Time: 1513620

大约10%的差异有利于printf。然而,当我运行任何基准测试时,我的系统都会有很大的变化,所以应该谨慎对待。请注意,使用endl而不是"/n"会产生显著差异!

对于cinscanf,与scanf:的缺点相比有更多的差异

#include <iostream>
#include <cstdio>
using namespace std;
static __inline__ unsigned long long rdtsc(void)
{
    unsigned hi, lo;
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
int main(int argc, char **argv)
{
    unsigned long long t = rdtsc();
    if (argc > 1)
    {
        for(int i = 0; i < 1000; i++)
        {
            int tmp;
            scanf("%d", &tmp);
        }
    }
    else
    {
        for(int i = 0; i < 1000; i++)
        {
            int tmp;
            cin >> tmp;
        }
    }
    t = rdtsc() - t; 
    cerr << "Time: " << t << endl;
}

$ ./a.out < foo.txt
Time: 1990454
$ ./a.out 1 < foo.txt
Time: 4804226

正如你所看到的,scanf的速度几乎慢了2.5倍。。。然而,我也看到过其他情况,它们并没有那么大的区别。我不完全确定为什么这里有这么大的差异。

总之,这是有区别的,但我相信James Kanze的答案更接近于解释发生了什么——代码根本无法完成,因为cin.eof()没有被scanf设置。

有一点是肯定的:scanfstd::cin.eof()没有影响。因此,您的第一个代码有一个无休止的循环。第二个代码实际上从std::cin中读取了一些内容,并且最终导致CCD_ 25返回true。

然而,这两种代码都存在许多其他问题。鉴于您级别,您应该忘记scanf;太危险了复杂。处处使用std::cin。检查一下在输入之后,但在使用结果之前成功(总是)。(std::cin.eof()是否告诉您上一次输入成功,下一次输入是否成功或失败在您知道以前的输入失败之前使用可能是一个错误。)

编写代码的惯用方法会涉及到一些内容类似:

while ( std::cin >> N >> B >> H >> W ) ...

(除了没有一个经验丰富的C++程序员会使用全局变量;也没有一笔资本字母作为名称。)应处理其他输入类似地。

好的。我知道答案就在那里,但我仍然没有看到一个明确的解释,为什么OP会观察到。所以,为了充分利用詹姆斯和马茨的答案,我会努力得到一个简单易懂的解释。

问题是第一个代码本身运行速度并不是较慢的。正如詹姆斯所说,可能存在一个无限循环。

为什么第一个代码运行得较慢因为有一个无休止的循环,自动判断会在时间限制后杀死它。算法和I/O并不慢。

考虑到时差:0.022秒到1秒。由于c和c++之间的I/O实现差异,(在实践中)不可能有这么大的差异。

我没有输入示例。但我敢打赌,如果你从Mats的回答中得到建议,并使用以下代码,第一个代码将正常工作,并给出相同的时间结果。

#include <stdio.h>
#include <stdlib.h>
#define inf 500000000
int N, B, H, W;
int main ()
{
    while(scanf("%d %d %d %d", &N, &B, &H, &W) != EOF){ //Mats' suggestion
        int cost = inf;
        //if(cin.eof()) break; //not needed `while` checks it
        for(int i = 0; i < H; i++){
            int P, k ;
            scanf("%d",&P);
            for(int j = 0; j < W; j++){
                scanf("%d",&k);
                if(k >= N && N*P < cost) cost = N*P;
            }
        }
        if(cost <= B) printf("%d",cost);
        else{
            printf("stay homen");
        }
    }
    return 0;
}