2008-12-05 76 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之前结束。

可以将索引文件读入内存中的结构数组中(比字典更少的开销和更多可预测的内存消耗),也可以直接在磁盘上执行搜索。