在Windows应用程序中,我有一个包装文件名和缓冲区的类。您使用文件名构造它,您可以查询对象以查看缓冲区是否已填充,如果没有则返回nullptr,如果是,则返回缓冲区地址。当对象超出范围时,缓冲区将被释放:
class file_buffer
{
public:
file_buffer(const std::string& file_name);
~file_buffer();
void* buffer();
private:
...
}
我想异步将数据放入内存,据我所知,我有两个选择:创建缓冲区并通过ReadFileEx使用重叠IO,或者使用MapViewOfFile并触摸另一个线程上的地址。
目前我正在使用ReadFileEx,这会出现一些问题,因为超过大约16MB的请求容易出现故障:我可以尝试拆分请求,但后来我遇到同步问题,如果对象超出范围之前IO完成我有缓冲区清理问题。此外,如果快速连续创建类的多个实例,则会非常繁琐。
映射和触摸另一个线程上的数据似乎相当容易,因为我没有上限问题:如果客户端现在绝对必须拥有数据,他们可以简单地取消引用地址,让操作系统担心页面错误并阻止阻塞。
这个应用程序需要支持单核机器,所以我的问题是:另一个软件线程的页面错误会比当前线程上的重叠IO更昂贵吗?他们会拖延这个过程吗?重叠的IO是否以相同的方式停止进程,或者是否存在一些我不明白的操作系统魔法?是否使用重叠IO执行页面错误?
我已经很好地阅读了这些主题: http://msdn.microsoft.com/en-us/library/aa365199(v=vs.85).aspx(文件管理中的IO概念) http://msdn.microsoft.com/en-us/library/windows/desktop/aa366556(v=vs.85).aspx(文件映射) 但我似乎无法推断出如何进行绩效权衡。
答案 0 :(得分:11)
您肯定希望使用内存映射文件。一些人多年来一直主张重叠IO(带有FILE_FLAG_NO_BUFFERING
)作为“将数据送入RAM的最快方式”,但这只适用于具有非常特定条件的非常复杂的情况。在正常的平均情况下,关闭缓冲区缓存是一种严重的反优化。
现在,没有 FILE_FLAG_NO_BUFFERING
的重叠IO 具有重叠IO的所有怪癖,并且大约慢50%(原因我仍然无法理解)。
我做了一些相当广泛的基准测试a year ago。底线是:内存映射文件更快,更好,更不令人惊讶。
重叠IO使用更多CPU,使用缓冲区缓存时速度慢得多,异步恢复到一些记录良好和一些未记录条件下的同步(例如加密,压缩和......纯粹机会?请求大小?请求数量?),在不可预测的时间停止你的申请
提交请求有时可能需要“有趣”的时间,而CancelIO
有时不会取消任何内容,只能等待完成。具有未完成请求的进程是不可销毁的。管理具有出色重叠写入的缓冲区是非常重要的额外工作。
文件映射正常。句号。而且效果很好。没有惊喜,没有有趣的东西。触摸每个页面的开销非常小,并且提供的速度与磁盘能够提供的速度一样快,并且它利用了缓冲区缓存。您对单核CPU的关注没有问题。如果触摸线程发生故障,它会阻塞,并且一如一旦线程阻塞,另一个线程就会获得CPU时间。
我现在甚至使用文件映射来写,只要我有多个字节要写。这有点不重要(必须手动增长/预分配文件和映射,并在关闭时截断为实际长度),但是对于一些帮助程序类,它完全可行。写入500 MiB的数据,它需要“零时间”(你基本上做memcpy
,实际的写操作在后台发生,任何时候都可以,甚至在你的程序完成之后)。即使您知道操作系统自然而然,这也很有效
当然,在操作系统写出所有页面之前,最好不要断电,但对于任何类型的写入都是如此。什么不在磁盘上不在磁盘上 - 真的没有什么可说的了。如果您必须确定这一点,则必须等待磁盘同步完成,即使这样,在等待同步时也无法确定指示灯是否未出现。这就是生活。
答案 1 :(得分:4)
我并不认为比你更了解这一点,因为你似乎做了一些发明。并且完全确定你需要进行实验。但这是我对问题的理解,顺序相反: