2016-02-09 28 views
4

這可能是一個簡單的問題,但我一直無法以可讀的格式查找關於此信息或至少信息的具體信息。我發現的大部分信息都與從.csv中讀取數據有關。C++寫入csv,性能

我有一個功能,必須將數據保存到.csv文件。從性能的角度來看,這不是一個理想的格式,但讓我們假設這不能改變。我的數據存儲在r x c x s數據結構中,並且必須以r,c,s的形式輸出,並且值並保存到.csv。目前我有:

char delimiter = ','; 
ofstream ofs(file, ofstream::out); 

for (int r = 0; r < P.n_rows; r++) 
{ 
    for (int c = 0; c < P.n_cols; c++) 
    { 
     for (int s = 0; s < P.n_slices; s++) 
     { 
      ofs << r + 1 << delimiter << c + 1 << delimiter << s + 1 << delimiter << P(c, s, s) << endl; 
      count++; 
     } 
    } 
} 
ofs.close(); 

對於大小爲100×100×50的數據結構的這大約需要6秒,這是我下降是必要長的時間。如果你能提供一些關於如何加快速度的信息,我將非常感激。

+0

必須有許多csv編寫程序庫。如果它給你更好的結果,你有沒有嘗試過? P的運行時間份額有多大? – kay

+8

[首先,溝'endl'。你真的不需要刷新每一行。](https://kuhllib.com/2012/01/14/stop-excessive-use-of-stdendl/) – BoBTFish

+0

不,我還沒有嘗試過任何csv庫,但我會檢查它,看看我是否找到有用的東西。 –

回答

11

你應該注意,endl是超過一個換行符 - 它實際上是數據刷新到磁盤。

插入換行符到輸出序列OS,並通過調用os.put(os.widen( '\ n')),隨後加入os.flush()刷新它彷彿。

這可能會顯着減慢速度。你應該嘗試用換行符替換它。

+0

感謝您的回覆,最終用'\ n'替換'endl'有助於在此特定情況下將prerformane提高約50-60%。 –

+0

非常歡迎。祝你好運,讓它更快! –

+3

要深入瞭解爲什麼速度更快:刷新意味着立即將數據發送到目標流,但寫入硬驅動非常慢,因此文件流使用緩衝來首先收集RAM中的數據,並將更大的塊推入更少的次數。這些塊甚至可以調整到適合硬盤使用的尺寸。甚至清除緩衝區可能會花費一點點。最好讓流使用完整的緩衝區刷新並關閉。標準數據流刷新始終是因爲用戶期望在沒有進一步等待的情況下也能看到數據 – Youka

1

寫入字符串流,然後再完全寫入到輸出文件:

char delimiter = ','; 
stringstream ss; 

for (int r = 0; r < P.n_rows; r++) 
{ 
    for (int c = 0; c < P.n_cols; c++) 
    { 
     for (int s = 0; s < P.n_slices; s++) 
     { 
      ss << r + 1 << delimiter << c + 1 << delimiter << s + 1 << delimiter << P(r, c, s) << endl; 
     } 
    } 
} 
ofstream ofs(file, ofstream::out); 
ofs << ss.str(); 
ofs.close(); 

與在實際更換ENDL「\ n」解決了這個問題已經:

char delimiter = ','; 
ofstream ofs(file, ofstream::out); 

for (int r = 0; r < P.n_rows; r++) 
{ 
    for (int c = 0; c < P.n_cols; c++) 
    { 
     for (int s = 0; s < P.n_slices; s++) 
     { 
      ofs << r + 1 << delimiter << c + 1 << delimiter << s + 1 << delimiter << P(r, c, s) << "\n"; 
     } 
    } 
} 
ofs.close(); 

這給了約2倍在我的機器上加速:

string delimiter = ","; 
string ss; 
ss.reserve(P.n_rows * P.n_cols * P.n_slices * 20); 

int max_idx = max(P.n_rows, max(P.n_cols, P.n_slices)); 
vector<string> idx_str(max_idx); 
for(int i=0;i<max_idx;++i) idx_str[i] = std::to_string(i+1); 

for (int r = 0; r < P.n_rows; r++) 
{ 
    auto& rstr = idx_str[r]; 
    for (int c = 0; c < P.n_cols; c++) 
    { 
     auto& cstr = idx_str[c]; 
     string thisline = rstr + delimiter + cstr + delimiter; 

     for (int s = 0; s < P.n_slices; s++) 
     { 
      auto& sstr = idx_str[s]; 
      ss += thisline 
       + sstr + delimiter 
       + std::to_string(data[r][c][s]) + "\n"; 
     } 
    } 
} 
ofstream ofs(file, ofstream::out); 
ofs.write(ss.c_str(), sizeof(char)*ss.size()); 
ofs.close(); 
+1

什麼讓你覺得這會更快? (不一定是不同意,但一個好的答案應該解釋,幷包括一些證據)。有更多的分配開銷。另外,爲什麼你使用'std :: stringstream'而不是'std :: ostringstream',你爲什麼要刷新它,那根本沒有用處? – BoBTFish

+1

基本上只是將大量的磁盤IO操作聚合爲一個。根據我的經驗,速度可能是5倍到10倍。 – phg1024

+0

一個好的觀點,應該是答案的一部分,而不是評論。而且,通過簡單地消除原始中的多餘沖刷,解決得更簡單。 – BoBTFish

1

如上所述(並且被接受),減少endl減少了50-60%的時間(在我的情況下 - 從7秒到2秒,超過70%)。

但是,仍有改進的空間:一般的流格式。以下代碼將運行時間進一步縮短了75%,達到500 ms:

int a[100][100][50]; 
int main(int argc, char** argv) 
{ 
    char buff[64]; 
    memset(a, 1, 100 * 100 * 50 * sizeof(int)); 
    int count(0); 
    char delimiter = ','; 
    auto start = std::chrono::steady_clock::now(); 
    std::ofstream ofs("test.csv", std::ofstream::out); 
    for (int r = 0; r < 100; r++) 
    { 
     for (int c = 0; c < 100; c++) 
     { 
      for (int s = 0; s < 50; s++) 
      { 
       sprintf_s(buff, "%d,%d,%d,%d\n", r, c, s, a[c][r][s]); 
       ofs << buff; 
       //ofs << r + 1 << delimiter << c + 1 << delimiter << s + 1 << delimiter << a[c][r][s] << '\n'; 
       count++; 
      } 
     } 
    } 
    ofs.close(); 
    auto end = std::chrono::steady_clock::now(); 
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " ms" << endl; 
    return count; 
}