探尋C++最快的讀取文件的方案

在競賽中,遇到大數據時,每每讀文件成了程序運行速度的瓶頸,須要更快的讀取方式。相信幾乎全部的C++學習者都在cin機器緩慢的速度上栽過跟頭,因而今後之後發誓不用cin讀數據。還有人說Pascal的read語句的速度是C/C++中scanf比不上的,C++選手只能乾着急。難道C++真的低Pascal一等嗎?答案是不言而喻的。一個進階的方法是把數據一會兒讀進來,而後再轉化字符串,這種方法傳說中很不錯,但具體如何從沒試過,所以今天就索性把能想到的全部的讀數據的方式都測試了一邊,結果是驚人的。 linux

競賽中讀數據的狀況最多的莫過於讀一大堆整數了,因而我寫了一個程序,生成一千萬個隨機數到data.txt中,一共55MB。而後我寫了個程序主幹計算運行時間,代碼以下: ios

#include <ctime> int main()
{ int start = clock(); //DO SOMETHING printf("%.3lf\n",double(clock()-start)/CLOCKS_PER_SEC);
}

最簡單的方法就算寫一個循環scanf了,代碼以下: 數組

const int MAXN = 10000000; int numbers[MAXN]; void scanf_read()
{
    freopen("data.txt","r",stdin); for (int i=0;i<MAXN;i++)
        scanf("%d",&numbers[i]);
}

但是效率如何呢?在個人電腦Linux平臺上測試結果爲2.01秒。接下來是cin,代碼以下 函數

const int MAXN = 10000000; int numbers[MAXN]; void cin_read()
{
    freopen("data.txt","r",stdin); for (int i=0;i<MAXN;i++) std::cin >> numbers[i];
}

出乎個人意料,cin僅僅用了6.38秒,比我想象的要快。cin慢是有緣由的,其實默認的時候,cin與stdin老是保持同步的,也就是說這兩種方法能夠混用,而沒必要擔憂文件指針混亂,同時cout和stdout也同樣,二者混用不會輸出順序錯亂。正由於這個兼容性的特性,致使cin有許多額外的開銷,如何禁用這個特性呢?只需一個語句std::ios::sync_with_stdio(false);,這樣就能夠取消cin於stdin的同步了。程序以下: 學習

const int MAXN = 10000000; int numbers[MAXN]; void cin_read_nosync()
{
    freopen("data.txt","r",stdin); std::ios::sync_with_stdio(false); for (int i=0;i<MAXN;i++) std::cin >> numbers[i];
}

取消同步後效率究竟如何?經測試運行時間銳減到了2.05秒,與scanf效率相差無幾了!有了這個之後能夠放心使用cin和cout了。 測試

接下來讓咱們測試一下讀入整個文件再處理的方法,首先要寫一個字符串轉化爲數組的函數,代碼以下 大數據

const int MAXS = 60*1024*1024; char buf[MAXS]; void analyse(char *buf,int len = MAXS)
{ int i;
    numbers[i=0]=0; for (char *p=buf;*p && p-buf<len;p++) if (*p == ' ')
            numbers[++i]=0; else numbers[i] = numbers[i] * 10 + *p - '0';
}

把整個文件讀入一個字符串最經常使用的方法是用fread,代碼以下: spa

const int MAXN = 10000000; const int MAXS = 60*1024*1024; int numbers[MAXN]; char buf[MAXS]; void fread_analyse()
{
    freopen("data.txt","rb",stdin); int len = fread(buf,1,MAXS,stdin);
    buf[len] = '\0';
    analyse(buf,len);
}

上述代碼有着驚人的效率,經測試讀取這10000000個數只用了0.29秒,效率提升了幾乎10倍!掌握着種方法簡直無敵了,不過,我記得fread是封裝過的read,若是直接使用read,是否是更快呢?代碼以下: 指針

const int MAXN = 10000000; const int MAXS = 60*1024*1024; int numbers[MAXN]; char buf[MAXS]; void read_analyse()
{ int fd = open("data.txt",O_RDONLY); int len = read(fd,buf,MAXS);
    buf[len] = '\0';
    analyse(buf,len);
}

測試發現運行時間仍然是0.29秒,可見read不具有特殊的優點。到此已經結束了嗎?不,我能夠調用Linux的底層函數mmap,這個函數的功能是將文件映射到內存,是全部讀文件方法都要封裝的基礎方法,直接使用mmap會怎樣呢?代碼以下: code

const int MAXN = 10000000; const int MAXS = 60*1024*1024; int numbers[MAXN]; char buf[MAXS]; void mmap_analyse()
{ int fd = open("data.txt",O_RDONLY); int len = lseek(fd,0,SEEK_END); char *mbuf = (char *) mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);    
    analyse(mbuf,len);
}

經測試,運行時間縮短到了0.25秒,效率繼續提升了14%。到此爲止我已經沒有更好的方法繼續提升讀文件的速度了。回頭測一下Pascal的速度如何?結果使人大跌眼鏡,竟然運行了2.16秒之多。程序以下:

const
    MAXN = 10000000;
var
    numbers :array[0..MAXN] of longint;
    i :longint; begin assign(input,'data.txt'); reset(input); for i:=0 to MAXN do read(numbers[i]); end.

爲確保準確性,我又換到Windows平臺上測試了一下。結果以下表:

方法/平臺/時間(秒) Linux gcc Windows mingw Windows VC2008
scanf 2.010 3.704 3.425
cin 6.380 64.003 19.208
cin取消同步 2.050 6.004 19.616
fread 0.290 0.241 0.304
read 0.290 0.398 不支持
mmap 0.250 不支持 不支持
Pascal read 2.160 4.668

從上面能夠看出幾個問題

  1. Linux平臺上運行程序廣泛比Windows上快。
  2. Windows下VC編譯的程序通常運行比MINGW(MINimal Gcc for Windows)快。
  3. VC對cin取消同步與否不敏感,先後效率相同。反過來MINGW則很是敏感,先後效率相差8倍。
  4. read本是linux系統函數,MINGW可能採用了某種模擬方式,read比fread更慢。
  5. Pascal程序運行速度實在使人不敢恭維。
相關文章
相關標籤/搜索