序:
在以前的測試中,咱們比較了四種讀入方式,發現使用讀入優化是最快的選擇,可是咱們知道fread()是比它更快的方法。這一次,咱們對比四種讀入優化,探尋C++讀取速度的極限。
分別是getchar()兩種方式以及fread()兩種方式。c++
測試數據爲1e5,1e6,1e7的大小,每次測試循環5次或7次,力求測試結果的穩定性。(共測試6次)windows
首先是兩種getchar()讀入,因爲在以前的測試中出現過,故只附代碼。markdown
inline void read1(int &curr)
{
static char c;
c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
curr = curr*10+c-'0';
c = getchar();
}
return ;
}
inline void read2()
{
static char c;
input = 0;
c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
input = input*10+c-'0';
c = getchar();
}
return ;
}
void Init1()//傳地址
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
read1(n);
for(unsigned i = 0; i != n; ++i)
{
read1(m[i]);
}
int endTime = clock();
ans[1][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
}
void Init2()//利用全局變量
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
read2();
n = input;
for(unsigned i = 0; i != n; ++i)
{
read2();
m[i] = input;
}
int endTime = clock();
ans[2][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
}
重點是fread在windows下的使用。
因爲windows環境中的換行符是’\r」\n’因此直接讀取int是不現實的(總之並無成功,全是亂碼)。
因而咱們採起讀字符的方式讀取。函數
首先是fread函數的參數:fread(char *pos, int len_per, int num, FILE *fp)。
即讀取的地址,每一個部分的長度(能夠是結構體,長度用sizeof(…)), 個數,文件指針。測試
那麼對於咱們這種狀況,且文件是使用freopen(「test.in」, 「r」, stdin)打開的來講,
就是這樣的:fread(char* pos, 1, int num, stdin);優化
對於字符串(基於目前情況)而言,咱們有三種方式計算長度,
一是根據數據範圍將長度置於足夠大(當心炸空間),這樣最快,一次便能讀取全;
二是經過C語言的feek和ftell函數遍歷整個文件計算出確切大小;
三是設定一個大小,屢次讀取。ui
那麼一的代碼以下:spa
void Get_All()
{
long long file_lenth = maxn*10;//足夠大
fread(Buffer,1, file_lenth, stdin);
X = Buffer;//當前指針
return ;
}
二是這樣的:指針
void Get_All()
{
long long file_lenth;
fseek(stdin, 0, SEEK_END);//從文件頭遍歷到文件尾
file_lenth = ftell(stdin);//文件字節數大小
rewind(stdin);
Buffer = (char*)malloc(1*file_lenth);//分配空間
fread(Buffer,1, file_lenth, stdin);
X = Buffer;//當前指針
return ;
}
三也相似:code
inline char Get_Char()
{
if(S==T)//T是尾指針,S == T用於判斷是否到尾
{
T=(S=buffer)+fread(buffer,1,maxn,stdin);//maxn預先設定好
if(S==T) return EOF;
}
return *S++; //因爲是屢次讀取,故在從中拆除整數時使用
}
int Get_Int()
{
char c;
int re=0;
for(c=Get_Char();c<'0'||c>'9';c=Get_Char());
while(c>='0'&&c<='9')
re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
return re;
}
一二用於獲得整數的函數更爲簡單,不須要判斷字符串是否到尾,由於它讀取了整個文件
int Get_Num()
{
c = *X;
re = 0;
while(c < '0' || c > '9') c = *++X;
while(c >= '0' && c <= '9')
{
re = re*10+c-'0';
c = *++X;
}
return re;
}
那麼準備工做就完成了,下面開始測試。
for(unsigned j = 0; j != 5; ++j)
{
for(unsigned i = 0; i != 7; ++i)//屢次循環
{
k = j;
Init1(); //getchar()-1
Init2();//getchar()-2
Init3();//fread()-3
Init4();//fread()-1
Init5();//scanf()
}
}
利用time.h的函數:
int StartTime = clock();
...
int EndTime = clock();
ans[curr][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;//計算總時長
測試結果以下:
2.961 2.874 2.888 2.914 2.854
2.733 2.764 2.793 2.722 2.733
2.493 2.440 2.446 2.464 2.479
2.113 2.134 2.210 2.135 2.157
18.586 18.552 18.613 18.493 18.529
14.491000 13.745000 12.322000 10.749000 92.773000
4.030 4.018 4.013 4.069 4.723
3.797 3.855 3.945 3.932 4.544
3.366 3.516 3.474 3.476 3.889
3.040 3.005 3.037 3.019 3.390
25.993 25.679 26.097 26.229 30.518
20.853000 20.073000 17.721000 15.491000 134.516000
0.301 0.277 0.303 0.271 0.284
0.284 0.274 0.308 0.267 0.285
0.252 0.237 0.251 0.236 0.242
0.219 0.204 0.219 0.208 0.210
1.872 1.840 1.925 1.802 1.861
1.436000 1.418000 1.218000 1.060000 9.300000
0.443 0.386 0.404 0.395 0.407
0.389 0.390 0.387 0.386 0.391
0.342 0.340 0.347 0.334 0.347
0.287 0.291 0.301 0.294 0.301
2.548 2.549 2.612 2.538 2.598
2.035000 1.943000 1.710000 1.474000 12.845000
0.044 0.038 0.041 0.040 0.040
0.044 0.038 0.040 0.038 0.037
0.033 0.033 0.034 0.031 0.033
0.030 0.030 0.027 0.028 0.030
0.280 0.271 0.258 0.264 0.255
0.203000 0.197000 0.164000 0.145000 1.328000
0.054 0.039 0.042 0.042 0.040 0.000 0.000
0.047 0.038 0.035 0.039 0.038 0.000 0.000
0.038 0.036 0.031 0.035 0.032 0.000 0.000
0.034 0.030 0.030 0.028 0.030 0.000 0.000
0.307 0.271 0.259 0.260 0.263 0.000 0.000
0.217000 0.197000 0.172000 0.152000 1.360000
結果十分穩定:T5 >> T1 > T2 > T3 > T4。
通過計算,getchar相對於scanf()的速度有大概6-7倍的優化,而使用fread()則能夠達到約9倍的優化。
那麼,對於fread(二)吶?
起初我對它不抱有信心:遍歷文件須要時間,我把長度規定好確定比你快啊。
事實證實差很少,速度略慢於fread(一)。
測試1e7*5 *7,三種fread對好比下
19.067000 16.611000 17.084000
完整測試代碼以下:
/* About: read-data-final Auther: kongse_qi date: 2017/04/17 result: Get_Num is nearly 10 times faster than scanf(); */
#include <bits/stdc++.h>
#define maxn 10000050
using namespace std;
int input, k;
int m[maxn];
double ans[10][20];
inline void read1(int &curr)
{
static char c;
c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
curr = curr*10+c-'0';
c = getchar();
}
return ;
}
inline void read2()
{
static char c;
input = 0;
c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
input = input*10+c-'0';
c = getchar();
}
return ;
}
void Init1()//傳地址
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
read1(n);
for(unsigned i = 0; i != n; ++i)
{
read1(m[i]);
}
int endTime = clock();
ans[1][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
}
void Init2()//利用全局變量
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
read2();
n = input;
for(unsigned i = 0; i != n; ++i)
{
read2();
m[i] = input;
}
int endTime = clock();
ans[2][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
}
char buffer[maxn],*S,*T;
inline char Get_Char()
{
if(S==T)
{
T=(S=buffer)+fread(buffer,1,maxn,stdin);
if(S==T) return EOF;
}
return *S++;
}
int Get_Int()
{
char c;
int re=0;
for(c=Get_Char();c<'0'||c>'9';c=Get_Char());
while(c>='0'&&c<='9')
re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
return re;
}
void Init3()
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
n = Get_Int();
for(unsigned i = 0; i != n; ++i)
{
m[i] = Get_Int();
}
int endTime = clock();
ans[3][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
}
char *X, Buffer[maxn*10], c, *buffer_;
int re;
void Get_All()
{
long long file_lenth = maxn*10;//int佔8,還有換行等
fread(Buffer,1, file_lenth, stdin);
X = Buffer;
return ;
}
void Get_All1()
{
long long file_lenth;
fseek(stdin, 0, SEEK_END);
file_lenth = ftell(stdin);
rewind(stdin);
buffer_ = (char*)malloc(1*file_lenth);
fread(buffer_,1, file_lenth, stdin);
X = buffer;
return ;
}
int Get_Num()
{
c = *X;
re = 0;
while(c < '0' || c > '9') c = *++X;
while(c >= '0' && c <= '9')
{
re = re*10+c-'0';
c = *++X;
}
return re;
}
void Init4()
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
Get_All();
n = Get_Num();
for(unsigned i = 0; i != n; ++i)
{
m[i] = Get_Num();
}
int endTime = clock();
ans[4][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
}
void Init5()
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
scanf("%d", &n);
for(unsigned i = 0; i != n; ++i)
{
scanf("%d", &m[i]);
}
int endTime = clock();
ans[5][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
fclose(stdin);
return ;
}
void Init6()
{
freopen("test.in", "r", stdin);
int n = 0, i;
int startTime = clock();
Get_All1();
n = Get_Num();
for(unsigned i = 0; i != n; ++i)
{
m[i] = Get_Num();
}
int endTime = clock();
ans[6][k] += (double)(endTime-startTime)/CLOCKS_PER_SEC;
free(buffer_);
fclose(stdin);
return ;
}
int main()
{
int i, j;
double tot[10] = {0};
for(unsigned j = 0; j != 5; ++j)
{
k = j;
for(unsigned i = 0; i != 7; ++i)
{
Init1();
Init2();
Init3();
Init4();
Init5();
Init6();
}
}
for(unsigned j = 1; j != 7; ++j)
{
for(unsigned i = 0; i != 5; ++i)
{
printf("%.3f ", ans[j][i]);
tot[j] += ans[j][i];
}
printf("\n");
}
for(unsigned i = 1; i != 7; ++i)
{
printf("%lf ", tot[i]);
}
return 0;
}
再次證實其是正確的,將其讀入的數輸出文件test2.out與讀入文件test.in進行比較。
fc test.in test1.out
正在比較文件 test.in 和 TEST1.OUT
FC: 找不到差別
尾:
舒適提示:使用以前也須要計算好空間,不要爲了省讀入的時間而致使MLE。
自此測試結束。
箜瑟_qi 2017.04.17 20:38