2014-01-20 66 views
0

我寫了一個程序(使用FFTW)對OpenFOAM中寫入的一些數據文件執行傅立葉變換。C++中的文件的螺紋讀取

程序首先找到每個數據文件的路徑(在我當前的例子中是501個文件),然後拆分線程之間的路徑,例如thread0獲取路徑0-> 61,thread1獲取62-> 123左右等,然後在最後以串行方式運行其餘文件。

我已經在整個代碼中實現了定時器,以查看它的瓶頸位置,因爲每個文件的串行運行時間大約爲3.5s,並行運行時間爲8個文件的時間大約爲21s(從8s的8s減少到28s)。 5(串行時間),但不是那麼多)

我的代碼有問題的部分低於

if (DIAG_timers) {readTimer = timerNow();} 
for (yindex=0; yindex<ycells; yindex++) 
{ 
    for (xindex=0; xindex<xcells; xindex++) 
    { 
     getline(alphaFile, alphaStringValue); 
     convertToNumber(alphaStringValue, alphaValue[xindex][yindex]); 
    } 
} 
if (DIAG_timers) {endTimerP(readTimer, tid, "reading value and converting", false);} 

這裏,timerNow()返回時鐘值,並endTimerP計算,在已過去的時間女士。 (其餘的參數與它在並行線程中運行相關,爲了避免每個循環輸出8行等等,以及定時器測量的內容)。

convertToNumber獲取alphaStringValue上的值,並將其轉換爲double值,然後將其存儲在alphaValue數組中。

alphaFile是一個std :: ifstream對象,而alphaStringValue是一個存儲每行上的文本的std :: string。

要讀取的文件大約爲每個40MB(僅比5120000多幾行,每個文件只包含一個值,介於0和1之間(大多數情況下==(0 || 1)),我有16GB的RAM,所以將所有文件複製到內存當然是可能的,因爲只有8個(每個線程1個)應該一次打開。我不確定mmap是否會更好地完成這項工作?stackoverflow上的幾個線程討論了mmap vs更直接的讀操作,特別是順序訪問,所以我不知道這是否會有好處。

我試着用一個互斥鎖圍繞代碼塊,以便只有一個線程可以立即運行該塊,以防萬一讀取多個文件通過模糊的隨機訪問導致IO速度變慢,但這只是將過程簡化爲粗略y串行速度時間。

任何建議允許我更快地運行此部分,可能通過複製文件,或其他任何事情,將不勝感激。

編輯:

template<class T> inline void convertToNumber(std::string const& s, T &result) 
{ 
    std::istringstream i(s); 
    T x; 
    if (!(i >> x)) 
     throw BadConversion("convertToNumber(\"" + s + "\")"); 
    result = x; 
} 

原來一直緩慢的部分。我認爲這是由於每個文件創建500萬個字符串流,然後在條件下測試500萬個字符串?用TonyD的建議來替代它可能會消除發生錯誤的可能性,但可以節省大量(至少在這種受控情況下)不必要的操作。

+0

您是否使用'time'(殼內置,或'在/ usr/bin中/ time')的基準程序(尤其是單線程的情況下)。你確定它不是I/O或系統CPU綁定的嗎?你願意花幾個小時的工作來改善幾個百分點嗎? –

+0

定時器函數使用'gettimeofday',然後只計算差異,返回'unsigned long long's。程序的其餘部分每個循環的時間爲0.5s,而本節的每個線程爲3.5,所以不要認爲它是CPU的限制。我對IO的不太確定,但看起來我的硬盤驅動器幾乎沒有噪音,'iotop'和'dstat'顯示偶發的io,但主要是輸出,當程序稍後寫入時(此步驟僅需約0.1秒鐘,並受到互斥鎖,因爲進程都寫入一個文件) – chrisb2244

+0

我寫了關於*系統CPU *(即CPU在內核中進行系統調用)。如果您堅持以編程方式計算時間,請使用[times(2)](http://man7.org/linux/man-pages/man2/times.2.html)和[clock_gettime(2)](http:// man7.org/linux/man-pages/man2/clock_gettime.2.html)並閱讀[time(7)](http://man7.org/linux/man-pages/man7/time.7.html)和[時間(1)](http://man7.org/linux/man-pages/man1/time.1.html)。不要忘記內核具有良好的文件系統和磁盤緩存。 –

回答

1

要讀取的文件大約爲40MB(僅比5120000多幾行,每個只包含一個值,介於0和1之間(大多數情況下==(0 || 1)),而I擁有16GB的RAM,所以複製所有文件到內存肯定會成爲可能,

是的,但是加載它們就會對你的進程的掛鐘時間仍然算,除非他們已經被其他進程之前,短閱讀。

因爲只有8個(每個線程1個)應該一次打開。

由於在啓動過程之前沒有加載到內存中的任何文件都必須加載,因此加載將計入進程掛鐘時間,因此無論一次打開多少個文件都沒有關係。任何不是緩存都會減慢這個過程。

我不確定是否會MMAP這樣做比較好?

不,它不會。mmap更快,但因爲它節省了從內核緩衝區拷貝到應用程序緩衝區和一些系統調用的開銷(與讀你做的每一頁一個內核項,而使用mmap頁是與預讀閱讀不會造成更多頁面錯誤)。但它會而不是節省您從磁盤讀取文件的時間,如果它們尚未被緩存。

mmap不加載在內存中的任何。內核將數據從磁盤加載到內部緩衝區,即頁面緩存。 read將數據從那裏複製到您的應用程序緩衝區,而mmap直接在您的地址空間中公開部分頁面緩存。但是在任何一種情況下,數據都是在首次訪問時獲取的,並保留在那裏,直到內存管理器放棄它們以重用內存。頁面緩存是全局的,所以如果一個進程導致一些數據被緩存,下一個進程會讓它們更快。但是如果在更長的時間後首次訪問,數據將不得不被讀取,這將影響readmmap完全相同的方式。

由於並行的過程中並沒有太大改善的時候,似乎大部分時間是實際的I/O。所以你可以優化一點,mmap可以幫助,但不要期望太多。提高I/O時間的唯一方法是獲得更快的磁盤。


你應該能夠讓系統來告訴你有多少時間用在CPU上,並花了多少錢等使用getrusage(2)數據(I/O)(在每個線程結束叫它獲取該線程的數據)。所以你可以確認I/O花了多少時間。

+0

謝謝你 - 我會測試它,並考慮是否可以通過I/O時間找到進一步的改進,但正如你所說,我不會獲得太多(任何東西?),因爲這些文件目前只能讀取一次。 – chrisb2244

+0

將此標記爲答案,因爲我無法將評論標記爲答案,並且它確實描述了要考慮的特徵。 'mmap',以及使用'getrusage(2)'在程序中測量I/O的方法 – chrisb2244

0

mmap無疑是將大量數據存入內存的最有效方式。這裏主要的好處是沒有額外的拷貝。

但它使代碼稍微複雜一些,因爲您不能直接使用文件I/O函數來使用mmap(如果您使用stdio函數的"m"模式,則主要優點是丟失現在至少得到一份)。根據我以前的實驗,mmap以某種程度超過了所有其他文件讀取變體。多少取決於花在等待磁盤上的總時間佔多大比例,以及實際處理文件內容花了多少時間。