2009-05-03 152 views
4

我遇到以下問題。一堆數據分成10k個小文件(每個大約8-16kib)。根據用戶輸入,我必須儘可能快地加載並處理它們。更確切地說,每個數據包可以分成100-100k個文件,並且大約有1k個數據包。雖然他們大多數是較小的。從單個文件並行讀取

現在,我正在使用一個線程池,並在每個文件訪問上,下一個空閒線程打開文件,讀取它並返回準備顯示的數據。隨着未來文件數量的增長,我對這種方法並不感到滿意,特別是如果它最終可能有大約100k或更多的文件(部署這肯定會很有趣)。

所以,這個想法是將所有這些小文件合併成一個大數據包,並從中讀取。我可以保證它將是隻讀的,但是我不知道預先併發訪問一個文件的線程數(我知道最大數目)。這會給我大約1000個大小適中的文件,並且我可以輕鬆添加新的數據包。

問題是:在這種情況下,如何讓1..N個線程從單個文件中有效讀取?我可以在Windows上使用異步I/O,但對於小於64k的讀取,它應該是同步的。內存映射文件不是一種選擇,因爲預期的大小大於1.6吉比特,我仍然需要能夠在x86上運行(除非我能夠有效地映射一些小部分,讀取它,再次取消映射 - 我的經驗內存映射是它比一次讀取帶來了相當多的開銷)。

我想過打開每個數據包N次,並給每個線程一個循環方式的句柄,但問題是,它可以以(數據文件數)x(最大數量線程)打開句柄(可以很容易地變成8-16k),並且我必須在每次訪問數據包時使用同步,或者使用一些無鎖魔術來獲得下一個空閒文件句柄。因爲這似乎不是一個原始問題(我猜,任何數據庫引擎都有類似的問題,在這裏你可以有N行(文件在我的情況下)的M個表(數據包),並且你想要允許儘可能多的線程同時讀取行)。那麼這裏推薦的做法是什麼?順便說一下,它應該在Windows和Linux上運行,所以歡迎使用可移植的方法(或者至少可以在兩種平臺上工作的方法,即使它們使用不同的底層API--只要它們可以被包裝,我很高興)。

[編輯]這不是關於速度,這是關於隱藏延遲。也就是說,我可能每秒鐘讀取100個這樣的小文件,所以我最多隻能達到1 mib/s。我主要關心的是尋找時間(因爲我的訪問模式不可預測),並且我想隱藏它們,通過在向用戶顯示舊數據的同時開始讀取。問題是如何讓幾個線程通過幾個文件發出IO請求,可能有> 1個線程訪問單個文件。

如果其中一個調用需要70 ms左右才能完成,那真的沒有問題,但如果讀取調用阻塞,我無法負擔得起。

+0

您的數據是否存儲在RAID陣列或類似的東西上? – Egwor 2009-05-03 09:58:31

+0

不,完全沒有。同樣,速度不是主要問題,只要它是異步運行的,即使每次讀取都需要100 ms,我也可以。問題是如何讓多個線程同時訪問一個文件:) – Anteru 2009-05-03 10:01:46

+0

我看到映射整個文件集的內存不是一個選項,但也許你可以使用一塊內存作爲訪問文件的大緩存大多數時候(或者它們全部只能被讀取一次?) – schnaader 2009-05-03 10:02:25

回答

2

我不認爲多線程將非常有助於你讀取磁盤。假設文件位於一個磁盤上,您只有一組讀取頭可以訪問它,因此您將在此處進行序列化。

在這種情況下,我想我會有一個磁盤讀取過程,將文件順序讀入緩衝區(這將有利於最大化讀取性能,因爲讀取頭不需要太多移動布特,假設數據相當零碎文件)以及一些讀取緩衝區的處理線程,在完成處理時將其標記爲空閒。

但是你選擇繼續,我可以建議你確保你的代碼是這樣構成的,不同類型的線程數是容易配置,最好是來自可執行命令行。在這種情況下,您需要嘗試不同的線程配置,以找到適合您特定情況的最佳數字。

+0

是的,但我不想每次訪問文件時都會阻止。事情是,我可以在等待磁盤尋道時間的同時繼續顯示當前數據,並且我想盡可能減少失速。 – Anteru 2009-05-03 09:44:46

0

我能想象到讀取數據的一大塊的最快方式是創建一個磁盤分區(主要或邏輯,但沒有LVM),和直接讀取分區裝置(例如/dev/sda5依次,沒有一個文件系統,每個磁盤只使用一個線程。順序訪問原始磁盤很重要,以避免磁盤搜索,這比順序讀取要慢得多。

0

那將傷害你的問題是頭部爭;不管你運行多少個線程,頭部一次只能處於一個位置。你有選擇在多個磁盤上分發文件嗎?

0

MMAP的方法派上用場。你並不需要爲每個讀做MMAP /取消映射週期,而是有一個線程處理所有這些映射,並交給指針(實際上的偏移和長度)。當線程訪問映射到文件的虛擬內存時,真正的讀取部分將由操作系統調度。

請記住,過多的線程將不會提高閱讀速度。數據庫引擎通常具有相當有限的I/O線程數量,可滿足應用程序線程的所有I/O需求。

1

Linux根本沒有可用的異步IO(是的,這裏有aio_ *,但它只適用於O_DIRECT並具有各種奇怪的限制),所以如果你想要便攜的東西,你只需要使用正常的讀取調用。 mmap可以工作,但如果您每次只讀取少量數據,則更改映射的成本可能會高一點。現在

,我不知道Windows,但在Linux上有一個PREAD()函數,可以讓你從一個文件描述符讀取在一個給定的偏移,而不會影響文件描述符的查找指針。有了這個,你可以有任意數量的線程從同一個文件中讀取數據,而不需要鎖定文件描述符或任何愚蠢的東西。