在Windows系统上打开文件的内存消耗是多少?

6

简述

在现代Windows系统中,打开一个文件需要多少内存?一些应用程序需要打开“大量”文件。Windows很擅长打开“大量”文件,但是保持单个文件的打开负载如何,以便可以决定何时“太多”是“太多”?

背景

对于在32位进程内顺序处理较大数据集(100MB〜几GB)的情况,我们需要设计一个缓冲区,将其内容存储在磁盘而不是内存中。

我们使用CreateFileFILE_ATTRIBUTE_TEMPORARYFILE_FLAG_DELETE_ON_CLOSE创建了一个小型类,没有太多问题。

问题在于,这些缓冲区的使用方式是每个缓冲区(每个临时文件)可能存储从几个字节到几GB的数据,并且我们希望使缓冲区类本身尽可能简化和通用。

使用情况范围从每个具有约100MB的100个缓冲区到每个仅具有几个字节的数千个缓冲区。 (是的,每个缓冲区在这个意义上都有自己的文件。)

在缓冲类中包含一个缓冲阈值似乎是很自然的,只有当它实际存储的字节数超过创建+引用临时文件的(内存)开销时,才开始创建和使用磁盘上的临时文件 - 在进程中以及在物理机器内存负载上。

问题

在现代Windows系统中,打开一个(临时)文件占用多少字节的内存?

  • 使用CreateFileFILE_ATTRIBUTE_TEMPORARYFILE_FLAG_DELETE_ON_CLOSE
  • 打开文件的(32位)进程的虚拟地址空间的字节数
  • 机器上的物理内存字节数(包括任何内核数据结构)

也就是说,当你开始从将数据存储在内存中转而存储在文件中时,净主内存增益的阈值是多少字节(在进程内部以及物理上)?

注释:

该评论提到 打开文件限制 不适用于 CreateFile,只适用于 MS CRT 文件 API。(通过 CreateFile 打开 10.00s 的文件在我的系统上完全没有问题——这是否是一个好主意是完全不同的问题,并且 不是本问题的一部分

内存映射文件:完全不适合处理 32 位进程中的 GB 级数据,因为您不能可靠地将这么大的数据集映射到 32 位进程的正常 2GB 地址范围内。 对于我的问题来说是完全没用的,与实际问题毫无关系。对于后台问题,普通文件就可以了。

看了一下http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx - 告诉我在64位系统上HANDLE本身占用16个字节,但那只是句柄

看了一下STXXL和它的文档,但这个库不适合我的任务,也没有找到在开始实际使用文件之前有用的阈值的提及。


有用的评论摘要:

Raymond 写道:“答案会因所安装的杀毒软件而异,所以唯一的方法就是在生产配置上进行测试。”

qwm 写道:“我更关心CPU开销。无论如何,回答你的问题最好的方法是测试。我能说的是,_FILE_OBJECT 的大小(包括 _OBJECT_HEADER)约为300字节,它的一些字段是指向其他相关结构的指针。”

Damon writes:“一个正确的答案是:10个字节(在我的Windows 7机器上)。由于似乎没有人觉得值得尝试,所以我尝试了一下(测量了100k次调用中MEMORYSTATUSEX::ullAvailVirtual的差异,没有其他运行程序)。别问我为什么不是8或16个字节,我也不知道。大约需要17秒的内核时间,退出时进程有100,030个句柄打开。私有工作集在运行期间增加了412k,而全局可用VM减少了1M,因此大约60%的内存开销在内核内部。(...)”

更令人惊讶的是,CreateFile 显然消耗了大量的内核时间(即繁忙的 CPU 时间,而不是像等待磁盘那样的时间!)。100k 次调用需要 17 秒,相当于在该计算机上打开一个句柄需要约 450,000 个周期。与此相比,虚拟内存只减少了 10 字节,可以说是微不足道的。

2
你做错了,100,000个缓冲区每个只有几个字节。 - Lightness Races in Orbit
3
“将其内容存储在磁盘上的缓冲区”听起来像是内存映射文件。使用MapViewOfFile映射文件。除非我误解了,否则数十万个缓冲区对我来说听起来很疯狂。 - doug65536
1
@MartinBa 我更关心CPU负载。无论如何,回答你的问题最好的方法就是测试它。我只能说_FILE_OBJECT本身(包括_OBJECT_HEADER)的大小约为300字节,并且它的一些字段是指向其他相关结构体的指针。 - qwm
1
答案会因安装的防病毒软件而异,所以唯一的方法是在生产配置上进行测试。 - Raymond Chen
1
“Since no answer is possible after close” 是错误的;添加细节以缩小所要求的问题,问题将会被重新开放。 - Dour High Arch
显示剩余18条评论
1个回答

5

我现在进行了一些测量:

  • 我设置了一个2G的RAM磁盘,以防止干扰我的普通NTFS文件表。
  • 我创建了1M(1,000,000)个文件,并通过perfmon检查了各种系统性能指标。

创建临时文件的调用(我保留它的句柄直到结束)如下所示:

HANDLE CreateNewTempFile(LPCTSTR filePath) {
    return ::CreateFile(
        filePath, 
        GENERIC_READ | GENERIC_WRITE, // reading and writing
        FILE_SHARE_READ, // Note: FILE_FLAG_DELETE_ON_CLOSE will also block readers, unless they specify FILE_SHARE_DELETE 
        /*Security:*/NULL, 
        CREATE_NEW, // only create if does not exist
        FILE_ATTRIBUTE_TEMPORARY | // optimize access for temporary file
        FILE_FLAG_DELETE_ON_CLOSE, // delete once the last handle has been closed
        NULL);
}

结果如下:
  • 所有临时文件都被删除后,RAM磁盘的使用情况如下:
    • 总共2060 M字节
    • 已使用1063 M字节
    • 空闲997 M字节
  • 比较开始和结束值(中间有一些样本),我得出每个打开的(临时)文件的平均内存消耗如下:
    • Memory/Available Bytes - 每打开一个文件,大约减少4k字节(这个计数器有很多抖动:显然,因为这个测试运行了10分钟)
    • Memory/Pool Paged Bytes - 每打开一个文件,大约减少3k字节
    • Memory/Pool Nonpages Bytes - 每打开一个文件,大约减少2.2k字节
  • 另外有趣的是,进程内存负载并没有显著增加(通过Process/Working Set跟踪)。

请注意,我还跟踪了分页,页面文件根本没有被使用(因为这台机器有16GB的RAM,在最低点我仍然有约4GB的空闲空间,希望如此)。


你有没有测试过创建和打开文件句柄所需的时间通常有多长? - rollsch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接