4

我注意到这种奇怪的效果,即内存在被触摸之前没有注册为由 Windows 任务管理器分配,而不是在malloc-ed 或new-ed 时。该效果同时出现在debug和优化的release版本中。

以下是一个结构示例,尽管在我的代码中分配和利用发生在不同的线程上,所以我不认为它是优化器,尽管我不确定如何检查。

for (int i = 0 ;i < 1000;i++)
{
buffer[i]=malloc(buffersize);
}
_sleep(1000*60)
for (int i=0;i<1000;i++)
{
memset(buffer[i],0,buffersize);//Only shows up the in the resource manager here
}

我的问题是 Windows 如何知道我使用了内存?是监视内存的首次使用还是一些编译时优化。

我的好奇心是由我正在编写的实时采集所激发的,它需要我触摸内存两次 -> 一次是在分配时,一次是在实际填充数据时。因此,按下一个按钮(“aquire!”)需要我一次写入 64 GB 的内存,而不是随着时间的推移,这会增加相当多的延迟。如果我malloc这样做的话,这会增加太多的延迟。

- 编辑 -

我还禁用了 Windows 页面文件...

4

4 回答 4

7

这是 Windows 等按需页面虚拟内存操作系统的标准行为。malloc() 调用仅分配虚拟内存地址空间。在您访问内存之前,您实际上并没有开始使用 RAM 。这会产生页面错误,迫使操作系统将您访问的内存页面映射到 RAM。

这通常是一个软页面错误,通过从空闲列表中抓取一个 RAM 页面并对其进行映射来快速处理。与硬页面错误相反,当其中一些 RAM 页面再次换出到交换文件时,您会遇到这种情况,因为另一个进程需要 RAM。从磁盘重新加载页面需要更多时间。

禁用页面文件有助于避免那些硬页面错误。它不会消除它们,您的代码页也可能会被换出。当您强制操作系统回退到它们时,很可能会发生这种情况,因为它不能再换出页面文件。这样的页面在换出时被丢弃,在页面错误返回时从可执行文件中重新加载。

如果您有软实时要求,那么最好的策略是尽早分配内存并有意访问它,然后再开始承诺快速响应。可以简单地用 calloc() 而不是 malloc() 来完成。

于 2013-11-03T16:59:40.953 回答
3

分配给您的进程的虚拟地址空间不一定由物理 RAM 支持。一个示例是页面文件,如果物理 RAM 运行不足,可能会将正在使用的内存移动到该文件中。但是,如果您从未写入过任何物理 RAM 或磁盘空间,则根本不需要分配任何物理 RAM 或磁盘空间。因此,作为一种优化,一些操作系统为您提供虚拟地址空间,但实际上尚未为其分配空间。在某些情况下,这是非常有益的。在其他情况下,例如您的情况,这有点烦人。

也就是说,任务管理器是一个非常简单的工具,用于调查内存使用情况。您能够观察到这一点更像是一个幸运的巧合,仅使用任务管理器无法(可靠地)观察到许多问题。

至于如何在不触及所有内存的情况下避免延迟分配......我不知道有什么办法。甚至没有VirtualAlloc提供解决方法。您可以通过每页只写入一个字节来稍微降低成本,这仍然会导致物理 RAM 分配。但请记住,即使您可以避免编写任何内容,Windows 仍然必须分配 1600 万页,包括页表条目。这肯定需要一些时间。您可以使用大页面(使用VirtualAlloc),这会使页面大几百倍,从而将上述 1600 万页减少到更合理的数字。

于 2013-11-03T17:04:18.217 回答
1

看起来您的操作系统正在懒惰地分配内存。查看这个问题的答案

基本上,当您调用 malloc 时,操作系统会为您的程序提供一个地址,并承诺以请求的数量提供内存。我读过的类比是操作系统为内存“写了一张支票”,但只有当你的程序试图通过使用它来“兑现”支票时才分配物理内存。

我认为,如果您希望延迟命中在您之前出现malloc,您应该memset在那里。

于 2013-11-03T16:54:15.690 回答
1

您可以同时尝试malloc并立即为其分配一个空值,以克服 NicholasM 所说的“惰性内存分配”。即使这样,编译器也可能会发现您正在分配但没有使用分配并将它们从编译的代码中删除。但是不要忘记为什么操作系统会延迟分配内存。

于 2013-11-03T17:02:55.717 回答