看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《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