参加过ACM比赛和部分自己做过测评的人想必不难发现,C++的cin和cout虽然比起C的scanf和printf来方便很多,但是速度似乎会打很大折扣。于是不求甚解的人们一拍脑门,认定因为C更“底层”,封装程度更低,所以势必比C++的输入输出执行效率更高。然而如若C++有知,恐怕要苦笑了。因为C++正是为了兼容C的输入输出才要在cin、cout中进行指针同步,降低了执行效率。那么可能挽回这种效率损失吗?答案是可以。
C++中提供了解除指针同步的方法:ios::sync_with_stdio(false)
。如字面意思,这个方法用来设置输入输出流是否与C的stdio同步。那么解除同步之后能有多大效果呢?毕竟C++确实因为封装程度比C高多多少少损失了一些执行效率。我动手进行了一系列测评,下面是源码。
这里我统一对输入输出做了重定向,不论是输入数据来源还是输入数据目标都是“data”文件。所有的输入/输出测试都是读或写10^7次字符串与整型数的组合数据,每种输入/输出测试都进行10次并取平均耗时作为评估值。
首先是C的输入输出。
scanf.cpp:
#include <cstdio>
#include <ctime>
int main()
{
int testCnt = 10;
int caseCnt = 10000000;
double t_sum = 0;
freopen("data", "r", stdin);
for(int j = 0 ; j < testCnt ; j++)
{
long t_start = clock();
char s[20];
for(int i = 0 ; i < caseCnt ; i++)
{
scanf("%s", s);
}
long t_end = clock();
t_sum += (double)(t_end - t_start) / CLOCKS_PER_SEC;
}
printf("average cost: %lfs\n", t_sum/testCnt);
fclose(stdin);
return 0;
}
printf.cpp:
#include <cstdio>
#include <ctime>
int main()
{
int testCnt = 10;
int caseCnt = 10000000;
double t_sum = 0;
freopen("data", "w", stdout);
for(int j = 0 ; j < testCnt ; j++)
{
long t_start = clock();
for(int i = 0 ; i < caseCnt ; i++)
{
printf("string%d\n", i);
}
long t_end = clock();
t_sum += (double)(t_end - t_start) / CLOCKS_PER_SEC;
}
printf("average cost: %lfs\n", t_sum/testCnt);
fclose(stdout);
return 0;
}
然后是不解锁同步的cin、cout方法。
cin.cpp:
#include <iostream>
#include <cstdio>
#include <ctime>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int testCnt = 10;
int caseCnt = 10000000;
double t_sum = 0;
ifstream fin("data");
streambuf *_cin = cin.rdbuf(fin.rdbuf());
for(int j = 0 ; j < testCnt ; j++)
{
long t_start = clock();
string s;
for(int i = 0 ; i < caseCnt ; i++)
{
cin >> s;
}
long t_end = clock();
t_sum += (double)(t_end - t_start) / CLOCKS_PER_SEC;
}
cout << "average cost: " << t_sum/testCnt << "s\n";
cin.rdbuf(_cin);
fin.close();
return 0;
}
cout.cpp:
#include <iostream>
#include <ctime>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int testCnt = 10;
int caseCnt = 10000000;
double t_sum = 0;
ofstream fout("data");
streambuf *_cout = cout.rdbuf(fout.rdbuf());
for(int j = 0 ; j < testCnt ; j++)
{
long t_start = clock();
for(int i = 0 ; i < caseCnt ; i++)
{
cout << "string" << i << '\n';
}
long t_end = clock();
t_sum += (double)(t_end - t_start) / CLOCKS_PER_SEC;
}
cout << "average cost: " << t_sum/testCnt << "s\n";
cout.rdbuf(_cout);
fout.close();
return 0;
}
接下来是解锁同步的cin、cout方法。
optimized_cin.cpp:
#include <iostream>
#include <cstdio>
#include <ctime>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int testCnt = 10;
int caseCnt = 10000000;
double t_sum = 0;
ios::sync_with_stdio(false);
ifstream fin("data");
streambuf *_cin = cin.rdbuf(fin.rdbuf());
for(int j = 0 ; j < testCnt ; j++)
{
long t_start = clock();
string s;
for(int i = 0 ; i < caseCnt ; i++)
{
cin >> s;
}
long t_end = clock();
t_sum += (double)(t_end - t_start) / CLOCKS_PER_SEC;
}
cout << "average cost: " << t_sum/testCnt << "s\n";
cin.rdbuf(_cin);
fin.close();
return 0;
}
optimized_cout.cpp:
#include <iostream>
#include <ctime>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int testCnt = 10;
int caseCnt = 10000000;
double t_sum = 0;
ios::sync_with_stdio(false);
ofstream fout("data");
streambuf *_cout = cout.rdbuf(fout.rdbuf());
for(int j = 0 ; j < testCnt ; j++)
{
long t_start = clock();
for(int i = 0 ; i < caseCnt ; i++)
{
cout << "string" << i << '\n';
}
long t_end = clock();
t_sum += (double)(t_end - t_start) / CLOCKS_PER_SEC;
}
cout << "average cost: " << t_sum/testCnt << "s\n";
cout.rdbuf(_cout);
fout.close();
return 0;
}
同时测试C++直接读写文件的方法。
fin.cpp:
#include <iostream>
#include <cstdio>
#include <ctime>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int testCnt = 10;
int caseCnt = 10000000;
double t_sum = 0;
ifstream fin("data");
for(int j = 0 ; j < testCnt ; j++)
{
long t_start = clock();
string s;
for(int i = 0 ; i < caseCnt ; i++)
{
fin >> s;
}
long t_end = clock();
t_sum += (double)(t_end - t_start) / CLOCKS_PER_SEC;
}
cout << "average cost: " << t_sum/testCnt << "s\n";
fin.close();
return 0;
}
fout.cpp:
#include <iostream>
#include <ctime>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int testCnt = 10;
int caseCnt = 10000000;
double t_sum = 0;
ofstream fout("data");
for(int j = 0 ; j < testCnt ; j++)
{
long t_start = clock();
for(int i = 0 ; i < caseCnt ; i++)
{
fout << "string" << i << '\n';
}
long t_end = clock();
t_sum += (double)(t_end - t_start) / CLOCKS_PER_SEC;
}
cout << "average cost: " << t_sum/testCnt << "s\n";
fout.close();
return 0;
}
下面是测试结果:
scanf与printf | cin与cout | 优化的cin与cout | fin与fout | |
---|---|---|---|---|
输入 | 1.125694s | 2.46949s | 0.950055s | 0.834976s |
输出 | 1.304706s | 1.46059s | 1.41783s | 1.41298s |
可以看到,事实上,解开同步之前,cin耗时是scanf的两倍还多,cout则比printf略慢。解开枷锁的cin执行效率与直接读文件接近,甚至比C的scanf还要快。至于输出,解开同步的cout执行效率略有提高,同样接近直接写文件,但是确实还是比C的printf略慢一点。