2010-06-16 16 views
3

我有一個非常大的二進制文件,我需要根據輸入文件中的id創建單獨的文件。有146個輸出文件,我使用的是cstdlibfopenfwriteFOPEN_MAX是20,所以我不能同時打開所有146個輸出文件。我也想盡量減少打開和關閉輸出文件的次數。寫入多於內核的文件的最佳方式是允許一次打開什麼?

如何有效寫入輸出文件?

由於遺留代碼,我還必須使用cstdlib庫。

可執行文件還必須是UNIX和Windows跨平臺兼容的。

+5

重新編譯籽粒與FOPEN_MAX – 2010-06-16 17:36:22

+1

的放大版本重新編譯內核是不是一種選擇,因爲可執行文件必須是跨平臺兼容 – Elpezmuerto 2010-06-16 18:29:37

回答

5

一對夫婦可能的方法,你可能需要:

  • 保持打開輸出文件的緩存處理這不到FOPEN_MAX - 如果寫入需要在一個文件打開,那麼就去做寫發生。否則,關閉高速緩存中的一個句柄並打開輸出文件。如果您的數據通常按照特定文件集的數據聚集在一起,則應該與輸入文件中的文件句柄高速緩存的LRU策略很好地配合使用。

  • 自己處理輸出緩衝而不是讓庫爲你做:保留你自己的一組146個(或者你可能需要的很多)輸出緩衝區並將輸出緩衝到這些緩衝區,然後執行一個打開/清空/當特定輸出緩衝區被填充時關閉。你甚至可以將這與上面的方法結合起來,真正減少開/關操作。

只要確保您對填充或接近填充輸出緩衝區時可能發生的邊緣條件測試良好。

+0

你可以將兩者結合起來。如果您有一個當前沒有打開的文件的數據,那麼您可以緩衝該文件,直到它達到該文件或所有文件的某個高水位,此時您打開該文件(可能首先關閉另一個文件)。 – nategoose 2010-06-16 21:29:53

1

如果無法以某種方式增加最大FOPEN_MAX,則可以創建一個簡單的請求隊列,然後根據需要關閉並重新打開文件。

您還可以跟蹤每個文件的最後寫入時間,並嘗試保持最近寫入的文件處於打開狀態。

0

解決方案似乎很明顯 - 打開N個文件,其中N略小於FOPEN_MAX。然後通讀輸入文件並提取前N個輸出文件的內容。然後關閉輸出文件,倒回輸入,然後重複。

0

首先,我希望你儘可能平行運行。沒有理由不能同時寫入多個文件。我會推薦做什麼thomask說和隊列請求。然後,您可以使用某個線程同步等待整個隊列被刷新,然後再允許下一輪寫入。

3

它也可能是值得的掃描輸入文件,使每個輸出ID列表和排序,這樣你寫的所有文件1項第一,那麼所有的文件2項等。

+0

或者使用某種形式的二叉搜索樹來保存信息,然後通過樹遍歷在輸出結束時('std :: map'或'std :: unordered_map'會很好)。 – 2010-06-16 18:06:48

+0

如果您在每次傳球中處理了多個文件,您可以減少通行證。您還可以嘗試優化此功能以利用特定ID的最早和最新實例,以便您可以避免每次掃描完整的輸入文件。 – nategoose 2010-06-16 21:32:22

+1

是的,或者你爲大文件中的每個id創建一個開始/結束索引 - 重要的是,如果每行是不同的輸出,那麼146通過一個大的順序文件比多個10000s的文件打開/寫入/關閉要好文件。 重點並不總是找到完美的最快解決方案 - 只是最可接受的。 – 2010-06-17 02:58:57

0

你沒有提到是否「實時」寫入這些輸出至關重要,或者正在寫入多少數據。根據您的限制,一種選擇可能是緩衝所有輸出並在軟件運行結束時寫入它們。

這方面的一個變種是一個固定大小的設置內部緩衝區,一旦你打內部緩衝區限制,打開文件,追加,並關閉,然後清除緩存更多的輸出。這些緩衝區減少了打開/關閉週期的次數,併爲您提供文件系統通常設置爲可以很好處理的突發寫入。這將適用於需要實時寫入和/或數據大於可用內存的情況,並且文件句柄在系統中超過某些最大值。

+0

實時並不是必需的,但可執行文件需要在合理的時間內處理數百GB的數據。因此速度是我最關心的問題。 我最初嘗試了buff軟件運行結束時的所有輸出和寫入,但效率不夠高也不夠快。 – Elpezmuerto 2010-06-16 16:57:31

0

您可以分兩步進行。

1)將第19個ids寫入一個文件,將下一個19 ids寫入下一個文件,依此類推。因此,您需要爲此步驟並行打開8個輸出文件(和輸入文件)。

2)對於每個如此創建的文件,創建19個(最後一個只有13個)新文件並向其中寫入ID。

獨立於輸入文件的大小以及它包含多少個id數據集,您總是需要打開和關閉163個文件。但是你需要兩次寫入數據,所以如果id數據集非常小且隨機分佈的話,它可能是值得的。

我認爲在大多數情況下,更頻繁地打開和關閉文件效率更高。

0

最安全的方法是在寫入後打開文件並刷新,然後關閉,如果沒有更近的寫入將發生。程序控制之外的許多事情可能會破壞文件的內容。在閱讀時請記住這一點。

我建議保留或std::vectorFILE指針。 map允許您通過ID訪問文件指針。如果ID範圍很小,則可以創建一個vector,預留元素,並將該ID用作索引。這將允許您同時打開大量文件。謹防數據損壞的概念。

同時打開文件的限制由操作系統設置。例如,如果您的操作系統最多爲10個,則在請求第11個文件時您將做出安排。

另一個技巧是動態內存中爲每個文件保留的緩衝區。處理完所有數據後,打開一個文件(或多個文件),寫入緩衝區(使用一個fwrite),關閉並繼續。這可能會更快,因爲您在數據處理過程中寫入內存而不是文件。有趣的一面是,您的操作系統也可以將緩衝區分頁到硬盤驅動器。緩衝區的大小和數量是一個與平臺相關的優化問題(您必須進行調整和測試以獲得良好的組合)。如果操作系統將內存分頁到磁盤上,程序將會減速。

0

那麼,如果我用OP中列出的約束來編寫它,我會創建146個緩衝區並將數據放入其中,然後在最後按順序遍歷緩衝區並關閉/打開單個文件句柄。

你在評論中提到速度是一個主要關注點,而且天真的方法太慢了。

有幾件事情可以開始考慮。一種是將二進制文件重組爲順序帶,這將允許並行操作。另一個是對你的文件句柄集合最少使用的方法。另一種方法可能是分成8個不同的進程,每個進程輸出19-20個文件。

根據二進制組織(高度碎片化和高度序列化),其中一些方法或多或少會具有實用性。

一個主要的約束是二進制數據的大小。它比緩存大嗎?比內存大?流出磁帶機?不斷脫離傳感器流,只存在於內存中的「文件」?其中每個提出了不同的優化策略...

另一個問題是使用模式。你是否偶爾會對文件進行高速寫入,或者你只寫了幾次大塊文件?這決定了文件句柄的不同緩存/分頁策略的有效性。

0

假設您在* nix系統上,限制是每個進程而不是系統範圍。所以這意味着你可以啓動多個進程,每個進程負責你篩選的id的子集。每個人都可以保持FOPEN_MAX的流程。

您可以讓一個父進程讀取輸入文件,然後通過管道專用文件將數據發送到各種「寫入」進程。

0

「最少的文件打開」策略:

爲了實現文件的最小數量的打開和關閉,你將不得不通過輸入多次閱讀。每次選擇需要排序的標識符的子集,並且只將這些記錄提取到輸出文件中。

僞每個線程:通過文件

  1. 運行,收集所有的唯一的ID。
  2. fseek()回到輸入的開始處。
  3. 對於每一個組19點的ID:
    1. 打開每個ID的文件。
    2. 運行輸入文件,將匹配記錄附加到相應的輸出文件。
    3. 關閉這組19個輸出文件。
    4. fseek()到輸入的開頭。

此方法不適用於多線程相當的很好地工作,因爲最終的線程將被讀取文件完全不同的部分。發生這種情況時,文件緩存很難高效。你可以使用障礙來保持線程的鎖步或多或少。

「最少的文件操作」策略

您可以使用多個線程和一個大型的緩衝池,以使輸入的唯一一個運行通過。這是以犧牲更多的文件打開和關閉(可能)爲代價的。直到整個文件被排序爲止,每個線程將:

  1. 選擇輸入的下一個未讀頁面。
  2. 將輸入分爲兩頁緩衝區,每個輸出文件一個緩衝區。每當一個緩衝區頁滿時:
    1. 將頁面標記爲不可用。
    2. 如果此頁面的頁面計數器值最低,請使用fwrite()將其附加到文件中。如果沒有,等到它是最低的(希望這不會發生太多)。
    3. 將頁面標記爲可用,併爲其指定下一頁碼。

你可以改變刷新文件輸出到磁盤的單位。也許你有足夠的RAM一次收集200頁,每個輸出文件?

事情要小心:

  • 是您的數據頁對齊?如果沒有,你必須聰明地閱讀「下一頁」。
  • 確保您沒有兩個線程fwrite()同時輸出到同一個輸出文件。如果發生這種情況,您可能會損壞其中一頁。
+0

這個解釋很尷尬,我已經編輯了幾次。讓我知道我可以澄清什麼。 – 2010-06-16 18:39:47

相關問題