2008-12-05 185 views
3

我有一個1GB的文件,其中包含字符串和long對。 什麼是將它讀入字典的最佳方式,以及您說它需要多少內存?將大文件讀入字典

文件有6200萬行。 我已經設法使用5.5GB的RAM讀取它。

說每個字典條目22個字節開銷,即1.5GB。 長爲8個字節,即500MB。 平均字符串長度爲15個字符,每個字符2個字節,即2GB。 總共約爲4GB,額外的1.5 GB會在哪裏?

初始字典分配需要256MB。 我注意到,每讀取1000萬行,消耗大約580MB,這與上面的計算非常吻合,但是在第6000行的某處,內存使用量從260MB增加到1.7GB,這是我缺少的1.5GB,它走了?

謝謝。

回答

2

你需要指定文件格式,但如果它只是像名稱=值,我會做:

Dictionary<string,long> dictionary = new Dictionary<string,long>(); 
using (TextReader reader = File.OpenText(filename)) 
{ 
    string line; 
    while ((line = reader.ReadLine()) != null) 
    { 
     string[] bits = line.Split('='); 
     // Error checking would go here 
     long value = long.Parse(bits[1]); 
     dictionary[bits[0]] = value; 
    } 
} 

現在,如果不行,我們需要知道關於文件的更多信息 - 有多少行,等等?

您使用的是64位Windows嗎? (如果沒有,你將不能夠使用比每個進程3GB以上反正IIRC)

的將依賴於字符串的長度所需的內存量,條目等

+0

32位窗口上3.5GB。 – UnkwnTech 2008-12-05 14:57:09

+0

我以爲3。5GB是整個系統使用的物理內存量,但每個進程限制爲3GB。無論哪種方式,它都小於5 :) – 2008-12-05 15:00:45

+0

而您的應用程序需要設置爲任何cpu或x64以利用64位系統。 – 2008-12-05 15:55:36

4

思維的數關於這一點,我想知道爲什麼你需要這樣做......(我知道,我知道......我不應該爲什麼,但聽我說......)

主要問題是有大量的數據需要快速訪問......問題是,它基本上是隨機訪問嗎,還是有一些模式可以被利用來預測訪問?

在任何情況下,我都會將此作爲滑動緩存來實現。例如。我會盡可能多地加載到內存中(以儘可能多地根據我預期的訪問模式加載的內容),然後跟蹤上次訪問時訪問的元素。 如果我碰到了不在緩存中的東西,那麼它將被加載並替換緩存中最舊的項目。

這將導致最常用的內容在內存中可訪問,但會招致額外的緩存未命中的工作。

在任何情況下,在不知道多一點有關的問題,這僅僅是一個「通用的解決方案」。

這可能是隻是保存在一個SQL數據庫的本地實例就足夠了:)

0

在內存中加載1 GB的文件一次聽起來並不像一個好主意,我。只有當需要特定的塊時,我纔會通過將文件加載到較小的塊中來虛擬化對文件的訪問。當然,這將是比在內存中的整個文件慢,但1 GB是一個真正的乳齒象...

5

也許你可以轉換1 GB的文件到SQLite數據庫有兩列鍵和值。然後在關鍵列上創建一個索引。之後,您可以查詢該數據庫以獲取您提供的密鑰的值。

9

這裏的每個人似乎都同意,處理這個問題的最好方法是一次只將文件的一部分讀入內存。當然,速度取決於內存中的哪個部分以及在需要特定信息時必須從磁盤讀取哪些部分。

有一個簡單的方法來處理決定什麼是最好的部分保留在內存中:

把數據存入數據庫。

一個真實的,像MSSQL快遞,或MySQL或Oracle XE(全部是免費的)。

數據庫緩存最常用的信息,所以它就像從內存中讀取一樣。他們爲您提供內存或磁盤數據的單一訪問方法。

0

即使擁有8 GB的物理內存,也不要將1GB的文件讀入內存,但仍然會遇到如此多的問題。 - 基於個人經驗 -

我不知道你需要做什麼,但找到一個解決方法,部分閱讀和處理。如果它不起作用,那麼就考慮使用數據庫。

1

我不熟悉C#,但如果你遇到內存問題,您可能需要推出自己的存儲容器,用於這項任務。

既然你想存儲在字典中,我認爲你需要它來快速查找? 你還沒有澄清哪一個應該是關鍵,但。

讓我們希望你想使用長鍵值。然後試試這個:

分配一個與文件一樣大的緩衝區。將文件讀入該緩衝區。

然後創建具有長的值(32個值,我想?)作爲鍵的字典,以及它們的值是一個32位的值也是如此。

現在瀏覽的數據在這樣的緩衝: 查找下一個鍵值對。計算它在緩衝區中的值的偏移量。現在將這些信息添加到字典中,只要鍵和偏移量作爲它的值。

這樣的話,你最終可能採取每紀錄也許10-20字節,它包含所有的文本數據一個更大的緩衝區的字典。我認爲,至少在C++中,這將是一種相當有效的內存方式。

12

瞭解當你填充一個Hashtable時發生了什麼很重要。 (辭典使用Hashtable作爲其底層的數據結構。)

當創建一個新的哈希表,.NET使含有11個水桶,其被鏈接字典條目的列表的陣列。當您添加一個條目時,其密鑰被散列,哈希代碼被映射到11個桶中的一個,並且條目(鍵+值+散列代碼)被附加到鏈接列表。

在某個點(這取決於首次構建Hashtable時使用的加載因子),Hashtable在Add操作期間確定它遇到太多衝突,並且最初的11個桶不是足夠。因此它會創建一個新的桶陣列,其大小是原來的兩倍(不完全是這樣;桶的數量總是總數),然後從舊的表中填充新的表格。

所以有兩件事情就內存利用率而言起作用。

首先,每隔一段時間,Hashtable需要使用兩倍於當前使用的內存量,以便它可以在調整大小時複製表。所以,如果你有一個使用1.8GB內存的Hashtable,並且需要調整大小,那麼需要使用3.6GB,現在你有問題了。

第二個是每個哈希表條目都有大約12個字節的開銷:指向鍵值,列表中的下一個條目的指針,加上哈希碼。對於大多數用途來說,這種開銷是微不足道的,但如果你正在構建一個包含1億個條目的Hashtable,那麼這大約有1.2GB的開銷。

您可以通過使用允許您提供初始容量的Dictionary構造函數的重載來克服第一個問題。如果您指定的容量足夠容納所有要添加的條目,則在填充Hashtable時不需要重建它。關於第二個,你幾乎沒有什麼可以做的。

1

您可以將1G文件轉換爲更高效的索引格式,但將其保留爲磁盤上的文件?然後,您可以根據需要訪問它並進行高效的查找。

也許你可以在內存中映射這個(更高效的格式)文件的內容,然後使用最小的內存使用率和需求負載,這可能是一個直接在光盤上直接訪問文件和加載整個事情變成了一個大字節數組。

0

如果您選擇使用數據庫,則最好使用dbm樣式的工具,如Berkeley DB for .NET。它們專門用來表示基於磁盤的哈希表。

或者,您可以使用某些數據庫技術推出自己的解決方案。

假設你的原始數據文件看起來像這樣(點表示字符串長度會有所變化):

[key2][value2...][key1][value1..][key3][value3....] 

拆分成索引文件和值文件。

值文件:

[value1..][value2...][value3....] 

索引文件:

[key1][value1-offset] 
[key2][value2-offset] 
[key3][value3-offset] 

記錄索引文件的大​​小固定的key->value-offset對和關鍵是有序的。 值文件中的字符串也按鍵排序。

要獲得key(N)你會二進制搜索在索引key(N)記錄中的值,然後讀取字符串值,從文件開始在value(N)-offsetvalue(N+1)-offset之前結束。

可以將索引文件讀入內存中的結構數組中(比字典更少的開銷和更多可預測的內存消耗),也可以直接在磁盤上執行搜索。