Re: [問題] C++寫檔案速度很慢要怎麼樣加速

看板C_and_CPP作者 (卡卡獸)時間12年前 (2013/08/13 15:12), 編輯推噓2(2015)
留言17則, 7人參與, 最新討論串2/2 (看更多)
※ 引述《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

08/13 23:35, , 1F
其實作者有提到已用過 RamDisk 的方式速度無明顯提升,這個
08/13 23:35, 1F

08/13 23:36, , 2F
例子當中的 writefile() 函式只是把記憶體中的資料寫入磁碟
08/13 23:36, 2F

08/13 23:43, , 3F
內,假如 data 指向的資料已排列好只要用 fputs() 整筆寫入
08/13 23:43, 3F

08/13 23:46, , 4F
即可,可以另外增加一個排列記憶體中資料的函式,再呼叫
08/13 23:46, 4F

08/13 23:47, , 5F
writefile() 函式做磁碟寫入.
08/13 23:47, 5F

08/14 01:59, , 6F
我認為他需要profiling。
08/14 01:59, 6F

08/14 10:42, , 7F
ramdisk還是有I/O system call, 是要減少這部分
08/14 10:42, 7F

08/14 10:43, , 8F
要不然linux的也是會自動處理syscall的IO buffer
08/14 10:43, 8F

08/14 17:13, , 9F
速度瓶頸在 sprintf(..."%f\t"...) 浮點數轉字串那一行.
08/14 17:13, 9F

08/14 17:16, , 10F
如果不做轉換, 寫檔就算 1byte 1byte 寫, 寫到一樣大小,
08/14 17:16, 10F

08/14 17:16, , 11F
應該還是快很多...
08/14 17:16, 11F

08/14 19:30, , 12F
和 yvb 的結果一樣, 測試結果整個在 float 轉 string.
08/14 19:30, 12F

08/14 19:32, , 13F
如果換成 sprintf(..."%d\t", 1234567890 ) 測試就快很多
08/14 19:32, 13F

08/14 19:34, , 14F
建議直接存二進位的float到檔案吧, 浮點數太消耗時間了
08/14 19:34, 14F

08/14 23:07, , 15F
原來如此,謝謝樓上各位 :D
08/14 23:07, 15F

08/14 23:10, , 16F
請問要用什麼東西去分析程式碼的效率我都用VC express寫
08/14 23:10, 16F

08/14 23:11, , 17F
vc express 的話那就用免費的 profiler 吧.像 very sleepy
08/14 23:11, 17F
文章代碼(AID): #1I2ap5Hb (C_and_CPP)
文章代碼(AID): #1I2ap5Hb (C_and_CPP)