从作者开始学习C++到现在,经常听说C++中cin/cout的效率比C语言的scanf/printf差很多,也偶尔会在一些其他博主的博客上看到过一些相关的实验和分析,由于一些个人原因也未曾亲自测试过。这两天又重新思考了一下这个问题,于是就动手写了一些代码来做实验,但结果却出乎我的意料。

本文就C++中的cin和scanf做效率对比实验,为了少说废话,就不介绍它们的背景和用途了,想必打开这篇文章的读者对它们都已经很熟悉了。

实验过程

本实验分为三组,分别为“整数”、“浮点数”以及“字符串”。每组实验分别测试“与stdio同步的cin”、“未与stdio同步的cin”以及“scanf”。

用于生成数据的代码data.cpp如下:

#include <iostream>
#include <fstream>
#include <cstdio>
using namespace std;
 
const int N = 10000000;
const double pi = 3.14159265358979;
int main()
{
    ofstream ofs;
    ofs.open("int.dat");
    for(int i = 0; i < N; ++i)
        ofs << i + N << '\n';
    ofs.close();
 
    ofs.open("double.dat");
    for(int i = 0; i < N; ++i)
        ofs << (i+1) * pi << '\n';
    ofs.close();
 
    ofs.open("string.dat");
    char c[] = "abcdefgh";
    for(int i = 0; i < N; ++i)
        ofs << c << '\n';
    ofs.close();
    return 0;
}

测试过程中使用std::chrono中提供的相关工具来度量时间,也曾在C++高精度计时器中用到。测试代码test.cpp如下所示,编译时请增加c++11选项。

#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>
#include <chrono>
#include <cstring>
using namespace std;
using namespace chrono;
using timepoint = time_point<high_resolution_clock>;
auto now = high_resolution_clock::now;
 
template<typename T>
void test_cin(T& t)
{
    timepoint tp = now();
    while(cin >> t);
    duration<double> span = duration_cast<duration<double>>(now() - tp);
    cout << span.count() << " seconds.\n";
}
 
void test_scanf(const char *format, void *data)
{
    timepoint tp = now();
    while(~scanf(format, data));
    duration<double> span = duration_cast<duration<double>>(now() - tp);
    cout << span.count() << " seconds.\n";
}
 
int main(int argc, char *argv[])
{
    int int_tmp;
    double double_tmp;
    string string_tmp;
    string_tmp.reserve(20);
    char chars_tmp[20];
 
    if(argc != 3) return 0;
    if(strcmp(argv[1], "int")==0)
    {
        if(strcmp(argv[2], "cin")==0)
        {
            ios::sync_with_stdio(false);
            test_cin(int_tmp);
        }
        else if(strcmp(argv[2], "cin_sync")==0)
        {
            ios::sync_with_stdio(true);
            test_cin(int_tmp);
        }
        else if(strcmp(argv[2], "scanf")==0)
            test_scanf("%d", &int_tmp);
    }
    else if(strcmp(argv[1], "double")==0)
    {
        if(strcmp(argv[2], "cin")==0)
        {
            ios::sync_with_stdio(false);
            test_cin(double_tmp);
        }
        else if(strcmp(argv[2], "cin_sync")==0)
        {
            ios::sync_with_stdio(true);
            test_cin(double_tmp);
        }
        else if(strcmp(argv[2], "scanf")==0)
            test_scanf("%lf", &double_tmp); 
    }
    else if(strcmp(argv[1], "string")==0)
    {
        if(strcmp(argv[2], "cin")==0)
        {
            ios::sync_with_stdio(false);
            test_cin(string_tmp);
        }
        else if(strcmp(argv[2], "cin_sync")==0)
        {
            ios::sync_with_stdio(true);
            test_cin(string_tmp);
        }
        else if(strcmp(argv[2], "scanf")==0)
            test_scanf("%s", chars_tmp);
    }
    return 0;
}

再写一个程序来运行所有的test,运行代码run.cpp如下,编译时请添加c++11选项:

#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
using namespace std;
 
int main(int argc, char *argv[])
{
    if(argc != 2) return 0;
    string cmd;
 
    for(auto type1 : {" int", " double", " string"})
        for(auto type2 : {"\tcin_sync", "\tcin\t", "\tscanf\t"})
        {
            cmd = string(argv[1]) + type1 + type2 + "\t<" + type1 + ".dat ";
            cout << cmd << ":\t";
            cout.flush();
            system(cmd.c_str());
            cout << endl;
        }
    return 0;
}

以g++编译器为例,执行以下命令进行测试:

g++ data.cpp -o data.out
g++ -std=c++11 -O2 test.cpp -o test.out
g++ -std=c++11 -O2 run.cpp -o run.out
./data.out
./run.out ./test.out

实验结果

MinGW-g++ 4.9:

cin同步cin未同步scanf
int24.158.172.15
double77.5954.095.97
string20.394.702.21

g++5.4(Ubuntu 16.04):

cin同步cin未同步scanf
int3.610.901.13
double6.682.861.73
string3.560.600.72

作者的笔记本配置为i5-5200U 2.2GHz,由于暂未安装Visual Studio,故暂时没有测试数据。

分析与总结

本实验本着客观公正的态度进行,但未免会因为作者个人知识不足或测试代码出现的差错而对实验结果产生影响,也可能因平台或硬件不同而数据有些许出入。如有错误,欢迎指出。

从实验数据很容易获得我们想要知道的东西,但作者认为自己暂时还不具有分析该实验结果的能力,因此暂不分析。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注