有没有一种方法可以强制Windows缓存文件?

7

有没有一种批处理命令或其他方法可以强制Windows缓存该文件?我正在尝试创建一个游戏预加载器,在启动游戏之前将某些游戏文件加载到缓存中。我能做到吗?

更新了int main代码:

int main(int argc, const char** argv)
{
if(argc >= 2) for(int i = 1; argv[i]; ++i) pf("C:\\Games\World_of_Tanks\res\packages\gui.pkg"[i]);
return 0;
}
2个回答

15
您只需要使用ReadFile或通过内存映射文件并触碰每一页来加载文件即可(实际上,由于分配粒度,每16页就足够了,但在理论上,您应该接触每一页)。内存映射更快且更易于缓存,因为您不需要分配额外的内存来保存数据(您不会用于任何有用的事情!)。操作系统将重用相同的物理内存用于缓存和进程可以看到的虚拟内存。
包括Microsoft Office和Adobe Reader在内的几个主流应用程序正是这样做以更快地启动。这些“延迟启动”服务会在您登录后使硬盘灯闪烁数十秒钟。
请注意,虽然您可以强制Windows1以这种方式缓存文件,但您不能强制它无限期保持文件在缓存中。如果没有足够的可用物理RAM,则系统将删除缓存内容以满足应用程序需求。
编辑:使用文件映射的最小工作示例实现:
#include <windows.h>
#include <cstdio>

void pf(const char* name)
{
    HANDLE file = CreateFile(name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if(file == INVALID_HANDLE_VALUE) { printf("couldn't open %s\n", name); return; };

    unsigned int len  = GetFileSize(file, 0);

    HANDLE mapping  = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0);
    if(mapping == 0) { printf("couldn't map %s\n", name); return; }

    const char* data = (const char*) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);

    if(data)
    {
        printf("prefetching %s... ", name);

        // need volatile or need to use result - compiler will otherwise optimize out whole loop
        volatile unsigned int touch = 0;

        for(unsigned int i = 0; i < len; i += 4096)
            touch += data[i];
    }
    else
        printf("couldn't create view of %s\n", name);

    UnmapViewOfFile(data);
    CloseHandle(mapping);
    CloseHandle(file);
}

int main(int argc, const char** argv)
{
    if(argc >= 2) for(int i = 1; argv[i]; ++i) pf(argv[i]);
    return 0;
}

该程序将尝试预取命令行中给定的任何文件名。
代码并不是非常漂亮,但它能够工作。它使用ANSI文件名,并且在打开成功但映射失败时泄漏文件句柄(但是...这并不是真正的问题,操作系统会在程序退出后清理--如果这让您感到烦恼,请使用RAII包装句柄)。由于32位构建的地址空间,它还限制了约1.8GiB的文件大小,否则由于GetFileSize而限制为4GiB,但如果您确实需要那么大的文件,这也很容易解决。
volatile相比,一个人可能想要返回或以其他方式消耗“result”,但无论哪种方式都可以(与磁盘访问相比,volatile对性能没有真正可衡量的影响!)。


1说实话,您实际上不能强制Windows,但除非您明确请求未缓冲的I/O,否则它总是按照这种方式工作。
理论上,您可以强制操作系统将页面读入内存,甚至强制将其锁定在RAM中,但您的工作集配额(非常小,您需要管理员权限才能修改它)通常不会让您这样做。这是一件好事,因为锁定大量内存是一个非常糟糕的想法。


太好了,我会研究一下如何在我的预加载器中使用这个。谢谢! - user1808010
是的,虽然这个代码看起来更像是C而不是C++。我认为在代码中包含<cstdio>而不是<stdio.h>(我讨厌iostream,所以仍然使用stdio进行输出!)是阻止它被视为普通C代码的唯一因素 :-) - Damon
1
嘿,如果你还在看这个,我应该在哪里添加pf()文件路径(恰好是C:\Games\World_of_Tanks\res\packages\gui.pkg,这是我想要加载到内存或“预取”中的文件)? - user1808010
当然,是的。否则操作系统将无法找到它。如果在“World”后面有空格,则需要将其放在引号中。 - Damon
这是一个下划线,不知道为什么我忘记包括它了...无论如何,在哪个pf()函数中放置文件路径?(void pf()和main pf()中都有一个)感谢您的回复。 - user1808010
显示剩余3条评论

0
我知道这个问答已经很旧了,但我看到它并且发现它对我的目的来说不够用。这是我在“现代”Windows PowerShell中想出来的一个更直接的方法。
它的机制是一样的。你只需要从磁盘上读取文件,将其加载到内存中。显然,正如之前提到的,你不能真正强制操作系统将文件保留在内存中,但如果你的RAM容量足够,并且你的用例使用率低,那么你可以假设没有理由将内存驱逐。
说了这么多,下面是Windows PowerShell中的一行代码。
Measure-Command { Get-Childitem -path <your dir> -recurse | Get-FileHash -Algorithm MD5 | Out-Default }

你会注意到这确实会多做一些CPU工作,但如果你试图将其缓存到旋转硬盘上,那将成为瓶颈,而不仅仅是运行MD5的时间。
那么这是做什么的呢?嗯,我添加了Measure-Command,为什么不呢?它只是输出所提供命令的总运行时间。虽然不是必需的,但它可以轻松回答“花了多长时间?”这个问题。这只是锦上添花,如下所示。
Days              : 0
Hours             : 0
Minutes           : 3
Seconds           : 54
Milliseconds      : 659
Ticks             : 2346597665
TotalDays         : 0.00271596951967593
TotalHours        : 0.0651832684722222
TotalMinutes      : 3.91099610833333
TotalSeconds      : 234.6597665
TotalMilliseconds : 234659.7665

其余部分非常简单。给定您的路径并告诉Get-Childitem-recurse的方式操作给定目录中的所有内容。我选择了-Algorithm MD5用于Get-FileHash,因为它比其他SHA哈希算法更快一些。我们实际上不需要更多的操作,只需强制读取文件并对磁盘上包含的所有数据进行一些操作。
结果是从磁盘中读取并对整个文件集进行哈希计算,这需要将它们加载到内存中。如果您没有足够的多余内存,操作系统将会将它们驱逐出内存。如果您要处理的文件总量较小,或者您的RAM足够多,不会被驱逐出内存,那么这将有效地预缓存文件。
只是为了跟进我的使用情况。我使用的机器非常精简,峰值使用时内存约为6-8GB。我想要缓存的文件在磁盘上占用约17GB。有了总共32GB的内存,我可以指望有2-4GB的空闲内存。这里的空闲指的是实际未使用的内存,可以成为操作内存,或者只是用来存储更多的磁盘读取缓存。因此,我可以指望我加载的文件保持在缓存中,系统没有理由将它们从内存中驱逐出去。随着时间的推移,它们最终会被驱逐出去,但我只需再次运行命令,重新缓存可能已被驱逐的内容。上面显示的总共约4分钟的运行时间是在重新启动后从磁盘上完全读取的。如果已经完全缓存在内存中,没有磁盘读取,运行时间将不到一分钟。
附言:我实际上尝试将输出导向/dev/null(Windows的等效操作),但所需时间比简单地对所有内容进行MD5哈希计算要长6倍以上。无法理解为什么会这样,但我希望通过不对文件内容进行计算来加快进程。但这条路行不通。
如果你能想到比MD5哈希更轻量且更快的方法将文件内容加载到内存中,请给我留言!

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