如何故意触摸内存页? 首先,在任何人抱怨之前,我意识到从理论上完美的C++代码的角度来看,内存模型是一个我不应该依赖的实现细节。然而,我更喜欢表现而不是纪律
这里是一个场景:我有一个地址空间区域,我已经告诉操作系统使用我选择的文件返回该区域——也就是说,该文件是内存映射的。如果我对VMM通常如何工作的理解是正确的,那么操作系统在将页面加载到映射中时可能会非常懒惰,并且可能只有在实际接触页面时才会这样做 通常我可以忽略这个细节,但在这个特殊情况下,我将映射数据发送到工作线程池中。如果我只是天真地向工作线程传递一个指向此缓冲区的指针,那么当第一次触摸页面时,工作线程本身很有可能会遇到页面错误,这将导致工作线程阻塞,直到VMM实际加载页面为止 工作池的设计是这样的,它的线程在I/O上阻塞是非常糟糕的,而在作业中发送的线程可以容忍被阻塞。因此,我想让我的发送者线程先接触映射的页面,这样页面错误就会阻止它 (我理解,不能保证先触摸页面会停止工作线程中随后出现的页面错误,但该程序在大多数情况下都是最佳的,并且总是正确的。) 在x86汇编语言中,这很简单:如何故意触摸内存页? 首先,在任何人抱怨之前,我意识到从理论上完美的C++代码的角度来看,内存模型是一个我不应该依赖的实现细节。然而,我更喜欢表现而不是纪律,c++,c,multithreading,memory-mapped-files,C++,C,Multithreading,Memory Mapped Files,这里是一个场景:我有一个地址空间区域,我已经告诉操作系统使用我选择的文件返回该区域——也就是说,该文件是内存映射的。如果我对VMM通常如何工作的理解是正确的,那么操作系统在将页面加载到映射中时可能会非常懒惰,并且可能只有在实际接触页面时才会这样做 通常我可以忽略这个细节,但在这个特殊情况下,我将映射数据发送到工作线程池中。如果我只是天真地向工作线程传递一个指向此缓冲区的指针,那么当第一次触摸页面时,工作线程本身很有可能会遇到页面错误,这将导致工作线程阻塞,直到VMM实际加载页面为止 工作池的设计
; get the page's address in ebx
mov al, Byte Ptr [ebx]
遗憾的是,C或C++中不是那么简单。一个简单的实现很简单:
char *pPage = ...;
char Dummy = *pPage;
但是,这可能不起作用,因为任何有自尊心的优化器都会意识到代码什么也不做,只是省略了它
我们可以使用内联汇编,但这可能会严重削弱优化器。我们可以调用汇编语言函数来实现这一点,但这样我们就有了(无可否认的)不必要的函数调用开销
我们可以把Dummy
变成一个外部可见的变量,这样可以工作,因为编译器不能假设赋值是无意义的。但是,这可能会导致CPU缓存线上的争用,从而严重降低多核系统的性能。(更不用说,我们浪费了缓存线和访问。)
我也想过这样做:
char volatile *pPage = ...;
char Dummy = *pPage;
我知道volatile
关键字有两个保证:
- 编译器不会对访问进行重新排序;及
- 编译器不会假定连续读取之间的值相同
有什么想法吗?volatile保证按照定义执行内存访问,因此一个简单的解决方案正是您建议的:
volatile char *prefetch_me = ...;
(void)*prefetch_me;
但是,如果您想以(可能)更有效的方式(并且您正在*ix系统上运行)触摸多个页面,请查看,特别是MADV\u将需要
和/或MADV\u顺序
。从手册页:
-预计在不久的将来可以访问。(因此,最好先看几页。)MADV_将需要
-页面引用应按顺序排列。(因此,给定范围内的页面可以提前主动读取,并可能在访问后很快被释放。)MADV_SEQUENTIAL
volatile
有效。关于madvise()
的有趣说明,也可能很好。请注意,我不希望在加载完成之前它一定会阻塞,因此它与触摸页面并不完全相同。我想知道是否有WinAPI等价于madvise()
。