作者EdisonX (卡卡獸)
看板C_and_CPP
標題Re: [問題] C++寫檔案速度很慢要怎麼樣加速
時間Tue Aug 13 23:12:00 2013
※ 引述《Lepton (輕子)》之銘言:
: 我在做流體力學的時域有限差分的計算
: 需要把每一個時間點的資料存起來之後再做分析
: 但是我的計算副程式跟寫檔案花的時間幾乎是一樣長
: 我原本想說是因為硬碟讀寫速度太慢了
: 但是後來我弄了ramdisk把資料寫進去也是一樣慢
: 所以我懷疑是程式碼有問題,想問說該怎麼做改進
: 以下是我的程式碼
: 因為我存的資料是2維矩陣所以要2個迴圈然後讀資料寫到硬碟
: void writefile(float* data,string filename)
: {
: ofstream dataFile(filename.c_str());
: for (int i = 0; i < N; i++)
: {
: for (int j = 0; j < Nx; j++)
: {
: dataFile << data[j +i*Nx] << "\t";
: }
: dataFile << '\n';
: }
: dataFile.close();
: }
拋磚引玉。
首先以 io library 而言 , 正常而言 c library 比 c++ library 還快 ,
這是前人寫 acm / zerojudge 經驗得來的結果 ,
這結果是否有因實務上 compiler 更新而有所變更,我不再確定,
有興趣可自己再驗證。
binary 寫入永遠是第一優先,速度肯定比較快。
我提的 string buffer 概念實作起來初步像這樣 (純 C 示例)
#define BUF_SIZE 20000
const char * fname = "rst.txt";
void writef(const float * pNum , const size_t nX , const size_t nY)
{
char szBuf[BUF_SIZE] ;
char * pBeg = (char*)szBuf;
size_t i, j, nChars;
FILE * fp = NULL;
nChars = 0;
for(i = 0 ; i < nX ; ++i) {
for(j=0; j < nY ; ++j) {
nChars = sprintf(pBeg, "%f\t", pNum[i*nY+j]);
pBeg+=nChars;
}
*pBeg++ = '\n';
}
*pBeg = '\0';
fp = fopen(fname, "w");
fputs(fp, szBuf);
fclose(fp);
}
掌握幾個要點
[1] 避開檔案上的操作
cache speed >> memory speed >> disk speed(io operator) ,
所以盡可能將檔案操作的次數壓到最低去。
[2] 效能上和一開始的 "可能" 沒太大改善?
事實上這方法要在極大量的時候才看得出效果出來,
上百 K 時效能應看得出來。
[3] 注意這份只是 present code
一個重要的防呆我沒做,便是假設一開始的 szBuf 會容納所有輸出,
但這個在檔案真的大的時候基本上是不可能會成立的,所以必須要做一些修改,
重點的虛碼大概如下 (我沒實際跑過,可能會有 bug )
#define BUF_SIZE 2000 // szBuf 大小
#define WRI_SIZE 1500 // szBuf 存到 WRI_SIZE 時就先寫入,避開 OV
int nChars = 0, nLen = 0;
pBeg = (char*)szBuf;
for( i = 0 ; i < nX ; ++i) {
for( j = 0 ; j < nY ; ++j) {
nLen += nChars = sprintf(pBeg, "%f\t", pNum[i*nY+j]);
if( nLen >= WRI_SIZE) { // 寫入檔案
*pBeg = '\0';
fprintf(fp, "%s", szBuf);
pBeg = (char*)szBuf;
nLen = 0;
}
}
..... /* whatever */
}
這裡的寫入策略我不是採用一般的 "直到 OV" 時才寫入,而是設一門檻值 WRI_SIZE,
超過時就先寫到檔案去,但這樣 if 判斷又變多,會不會因分支預測指令使得速度又
拖慢,這就也沒再驗證了。
一次寫入的策略有許多種,是我的話我會想把 if 往外放一層回圈,
意思是一個 raw 才判斷一次要不要寫入 file 動作,
這些細節我想光用想的就很多種,再討論下去意義應已不大,
因整體概念都只是一個 : 先寫入 string buffer,再一次寫入檔案中。
有沒有再更快的方式,這個我不知道了,
但若您的 IO 次數確定有那麼頻繁、多次的話,可以的話考量架構上的修正?
至少目前 IO 瓶頸一直都是被確定的,要做加速實在有限。
--
就算把新鮮的肝拿回去,還是一樣寫碼到禿頭,加班到天亮,
永遠當老闆的傀儡 你是不是想這麼做?
是的話你就拿回去~ 拿啊!!
九世宅男 : 下輩子不要再讓我幹工程師了 ~
< Kuso 星爺語錄 >
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 180.177.73.182
→ GNUGCC:其實作者有提到已用過 RamDisk 的方式速度無明顯提升,這個 08/13 23:35
→ GNUGCC:例子當中的 writefile() 函式只是把記憶體中的資料寫入磁碟 08/13 23:36
→ GNUGCC:內,假如 data 指向的資料已排列好只要用 fputs() 整筆寫入 08/13 23:43
→ GNUGCC:即可,可以另外增加一個排列記憶體中資料的函式,再呼叫 08/13 23:46
→ GNUGCC:writefile() 函式做磁碟寫入. 08/13 23:47
→ MOONRAKER:我認為他需要profiling。 08/14 01:59
→ kdjf:ramdisk還是有I/O system call, 是要減少這部分 08/14 10:42
→ kdjf:要不然linux的也是會自動處理syscall的IO buffer 08/14 10:43
推 yvb:速度瓶頸在 sprintf(..."%f\t"...) 浮點數轉字串那一行. 08/14 17:13
→ yvb:如果不做轉換, 寫檔就算 1byte 1byte 寫, 寫到一樣大小, 08/14 17:16
→ yvb:應該還是快很多... 08/14 17:16
推 Thelink:和 yvb 的結果一樣, 測試結果整個在 float 轉 string. 08/14 19:30
→ Thelink:如果換成 sprintf(..."%d\t", 1234567890 ) 測試就快很多 08/14 19:32
→ Thelink:建議直接存二進位的float到檔案吧, 浮點數太消耗時間了 08/14 19:34
→ EdisonX:原來如此,謝謝樓上各位 :D 08/14 23:07
→ iHakka:請問要用什麼東西去分析程式碼的效率我都用VC express寫 08/14 23:10
→ EdisonX:vc express 的話那就用免費的 profiler 吧.像 very sleepy 08/14 23:11