1. 程式人生 > >【轉載】探尋C++最快的讀取檔案的方案

【轉載】探尋C++最快的讀取檔案的方案

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

競賽中讀資料的情況最多的莫過於讀一大堆整數了,於是我寫了一個程式,生成一千萬個隨機數到data.txt中,一共55MB。然後我寫了個程式主幹計算執行時間,程式碼如下:

#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,程式碼如下:

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會怎樣呢?程式碼如下:

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程式執行速度實在令人不敢恭維。

希望此文能對大家有所啟發,歡迎與我繼續討論。

相關推薦

轉載探尋C++讀取檔案方案

在競賽中,遇到大資料時,往往讀檔案成了程式執行速度的瓶頸,需要更快的讀取方式。相信幾乎所有的C++學習者都在cin機器緩慢的速度上栽過跟頭,於是從此以後發誓不用cin讀資料。還有人說Pascal的read語句的速度是C/C++中scanf比不上的,C++選手只能乾著急。難道C++真的低Pascal一等嗎?

[Reprint] 探尋C++讀取文件的方案

linu 基礎 作者 con strong input div stdin 瓶頸 作者:BYVoid(https://www.byvoid.com/zhs/blog/fast-readfile) 在競賽中,遇到大數據時,往往讀文件成了程序運行速度的瓶頸,需要更快的讀取

探尋C++讀取檔案方案 C++ IO優化

 在競賽中,遇到大資料時,往往讀檔案成了程式執行速度的瓶頸,需要更快的讀取方式。相信幾乎所有的C++學習者都在cin機器緩慢的速度上栽過跟頭,於是從此以後發誓不用cin讀資料。還有人說Pascal的read語句的速度是C/C++中scanf比不上的,C++選手只能乾著急。難

轉載史上全:TensorFlow 好玩的技術、應用和你不知道的黑科技

tube map 高性能 知識 seq 出現 執行時間 mes lex 【導讀】TensorFlow 在 2015 年年底一出現就受到了極大的關註,經過一年多的發展,已經成為了在機器學習、深度學習項目中最受歡迎的框架之一。自發布以來,TensorFlow 不斷在完善並增加新

轉載JAVA多線程讀取、操作List集合

線程 nbsp static 一點 stat lang 素數 param 應用 本文轉載自:http://blog.csdn.net/wang1989cs/article/details/47663565 import java.util.ArrayList; impor

轉載C語言中,double、long、unsigned、int、char類型數據所占字節數

src 有關 指針變量 none nbsp ide iso isp cli 和機器字長及編譯器有關系: 所以,int,long int,short int的寬度都可能隨編譯器而異。但有幾條鐵定的原則(ANSI/ISO制訂的): 1 sizeof(short int)&

轉載關於C#中聲音的播放

ise player loop 安裝 lin playsound toolbar api函數 lec 轉自 http://www.cnblogs.com/chenjiahong/articles/2716552.html C#中聲音的播放主要有三種方法: 1.使用API

轉載關於C#靜態構造函數的幾點說明

rst ont row c const line lap 執行順序 pps 引用 一、定義  靜態構造函數是C#的一個新特性,其實好像很少用到。不過當我們想初始化一些靜態變量的時候就需要用到它了。這個構造函數是屬於類的,而不是屬於哪裏實例的,就是說這個構造函數只會被執行一

轉載學習C++ -> 類(Classes)的定義與實現

人的 博文 有一個 能夠 基本數據 ntp 一是 簡單 空間 一、"類" 的介紹 在C++中, 用 "類" 來描述 "對象", 所謂的"對象"是指現實世界中的一切事物。那麽類就可以看做是對相似事物的抽象, 找到這些不同事物間的共同點, 如自行車和摩托車, 首先他們都屬

轉載Python操作Excel的讀取以及寫入

body .sh open 列數 讀取 efault jin rap ring 轉載來源:https://jingyan.baidu.com/article/e2284b2b754ac3e2e7118d41.html #導入包 import xlrd #設置路徑 path

轉載一個c程序在執行main函數之前和main之後都做了那些事情

loss -- text ould 很多 int win 部分 不知道 轉自:https://bbs.csdn.net/topics/300103318#r_78088969 main函數之前--真正的函數執行入口或開始一種解釋實際上,在可執行文件被加載之後,控制權立即交給

轉載C#中主線程和子線程如何實現互相傳遞數據

system generic ack tex href lin threading cti 利用 引用:https://blog.csdn.net/shuaihj/article/details/41316731 一、不帶參數創建Thread using System;

轉載關於C#中Thread.Join()的一點理解

今天是第一次在C#中接觸Thread,自己研究了一下其中Thread.Join()這個方法,下面談談自己的理解。 Thread.Join()在MSDN中的解釋很模糊:Blocks the calling thread until a thread terminates 有兩個主

轉載C#中主執行緒和子執行緒如何實現互相傳遞資料

using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace ATest { class A { public static

轉載hmac_sha1.c原始碼, C語言中的HMAC_SHA1加密方法

感謝原作者,連結如下:http://blog.csdn.net/yanzhibo/article/details/8469608 /****************************************************************/ /* 80

轉載Android外掛化常見衝突解決方案

文章轉自:https://blog.csdn.net/xiangzhihong8/article/details/80278068在Android元件化和外掛化的過程中,經常會遇到狠多的問題,如常見的包依賴衝突,資原始檔依賴衝突等問題,當然,在資原始檔上面,一些元件化框架已為

轉載VirtualBox中實現與主機檔案的共享

VirtualBox是一款開源的虛擬機器軟體,可以在Linux/Mac和Windows主機中執行。支援在虛擬機器中安裝全系列Windows操作 系統(如Win2000、XP、Vista、Windosw 7)。在VirtualBox虛擬機器中安裝作業系統並不複雜,如同在真實電腦

轉載Fonts.css -- 跨平臺中文字型解決方案

font-family: -apple-system, "Helvetica Neue", Helvetica, "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Source Han Sans CN"

Vmware 虛擬機三種網絡模式詳細解說轉載

apt con 實現 not ont 管理 網絡連接模式 sysconf 理解 由於Linux目前很熱門,越來越多的人在學習linux,但是買一臺服務放家裏來學習,實在是很浪費。那麽如何解決這個問題?虛擬機軟件是很好的選擇,常用的虛擬機軟件有vmware workstati

轉載C++自由存儲區是否等價於堆

destroy 對象池 when for default 維護 iat glob delete “free store” VS “heap” 當我問你C++的內存布局時,你大概會回答: “在C++中,內存區分為5個區,分別是堆、棧、自由存儲區、全局/靜態存儲區、常量存儲區