在Windows上打开文件的内存开销是多少?

Mar*_* Ba 5 c++ windows file-io winapi createfile

TL; DR

打开文件需要多少内存才能在现代Windows系统上占用?某些应用程序加载需要打开"大量"文件.Windows能够打开"大量"文件,但是保持单个文件打开的负担是什么,以便人们可以决定"很多"是"太多"?

背景

为了在32位进程内顺序处理大型数据集(100s MB~几GB),我们需要提供一个缓冲区,将其内容存储在磁盘而不是内存中.

我们已经(使用充实了一点类没有太多的问题CreateFileFILE_ATTRIBUTE_TEMPORARYFILE_FLAG_DELETE_ON_CLOSE).

问题是,这些缓冲区的使用方式是每个缓冲区(每个临时文件)可以存储从几个字节到几GB的数据,并且我们希望将缓冲区类本身保持为最小和一般尽可能.

用例范围从100个缓冲区到每个~100MB到100.000s缓冲区,每个缓冲区只有几个字节.(是的,重要的是这个意义上的每个缓冲区都有它自己的文件.)

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

在现代Windows系统上打开(临时)文件需要多少内存(以字节为单位)?

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

也就是说,当您开始在文件中而不是在内存中存储数据时,看到净主内存增益(进程内和物理上)的阈值(以字节为单位)是多少?

笔记:

提到的开放文件限制的评论不适用于CreateFileMS CRT文件API.(通过CreateFile打开10.00s的文件在我的系统上完全没问题 - 这是一个好主意是一个完全不同的问题而不是这个问题的一部分.

内存映射文件:完全不适合在32位进程中处理GB数据,因为您无法将这些大型数据集可靠地映射到32位进程的正常2GB地址范围.完全针对我的问题无用的,不以任何方式在所有的,涉及到实际的问题.普通文件适合背景问题.

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

看看STXXL和它的文档,但是这个lib也不适合我的任务,也没有在开始实际使用文件之前发现任何有用的阈值.


有用的评论摘要:

Raymond 写道:"答案将根据安装的防病毒软件而有所不同,因此唯一知道的方法是在生产配置上对其进行测试."

qwm 写道:"我更关心cpu开销.无论如何,回答你问题的最好方法就是测试它.我只能说_FILE_OBJECT单独(包括_OBJECT_HEADER)的大小是〜300b,其中一些字段是指向其他相关结构."

Damon wri tes:"一个正确的答案是:10个字节(在我的Windows 7机器上).由于没有其他人似乎值得实际尝试,我做了(测量MEMORYSTATUSEX::ullAvailVirtual超过100k的电话差异,没有其他运行).不要问我为什么它不是8或16字节,我不知道.大约17秒的内核时间,进程在退出时有100,030个句柄打开.私有工作集在运行期间上升了412k而全局可用VM下降了1M,所以大约60%的内存开销都在内核中.(...)"

"更令人震惊的是内存时间(这是繁忙的CPU时间,而不是在磁盘上等待!),这CreateFile显然是消耗的.10万次通话需要17秒才能打开这个机器上的一个手柄,大约需要450,000个周期.到那时,仅仅10个字节的虚拟内存就可以忽略不计了."

Mar*_* Ba 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);
}
Run Code Online (Sandbox Code Playgroud)

结果是:

  • 再次删除所有临时文件后,RAM磁盘使用情况如下:
    • 总计2060 M字节
    • 使用1063 M字节
    • 免费997 M字节
  • 比较开始值和结束值(中间有几个样本)我得出以下每个打开(临时)文件的平均内存消耗:
    • 内存/可用字节 - 约.每个打开的文件减少4k字节(这个计数器上的抖动很多:显然,这个测试运行了10分钟
    • 内存/池分页字节 - 约.每个打开文件3k字节
    • 内存/池非页面字节 - 约.每个打开文件2,2k字节
  • 同样有趣的是,进程内存负载并没有真正以任何显着的方式增加(由进程/工作集跟踪).

请注意,我还跟踪了分页,并且根本没有使用页面文件(正如我希望的那样,因为这台机器有16GB的RAM,而在最低点我仍然有~4GB免费).