在C中使用文件支持的mmap进行预取

问题描述

我正在编写一些性能关键代码(例如,在一个非常紧密循环中,并通过性能分析表示),其逻辑基本上(the_key一个参数,mmap_base是内存映射文件的基地址):

while (current_item && (struct my_struct *)(mmap_base + current_item) -> key < the_key){
    /* Do something less performance critical */
    current_item = (struct my_struct *)(mmap_base + current_item) -> next;
}

分析表明,这段代码在取消引用(mmap_base + current_item)时是磁盘绑定的,这是有道理的,因为随机磁盘IO相当慢。

由于文件很大(大约100 GB),因此无法将mmap中的相关部分加载到内存中。我正在考虑使用类似__builtin_prefetch()的东西:

while (current_item && (struct my_struct *)(mmap_base + current_item) -> key < the_key){
    __builtin_prefetch(mmap_base + ((struct my_struct *)(mmap_base + current_item) -> next),0);
    /* Do something less performance critical */
    current_item = (struct my_struct *)(mmap_base + current_item) -> next;
}

但是,这将不起作用。看来__builtin_prefetch()在mmap-ed内存上毫无用处。
然后,我尝试了madvise()

while (current_item && (struct my_struct *)(mmap_base + current_item) -> key < the_key){
    madvise(mmap_base + ((struct my_struct *)(mmap_base + current_item) -> next),sizeof(struct my_struct),MADV_WILLNEED);
    /* Do something less performance critical */
    current_item = (struct my_struct *)(mmap_base + current_item) -> next;
}

但是,这甚至降低了性能,分析表明madvise()调用现在已成为主要的开销。

是否存在一些编译器内置函数(x86_64,GCC)或其他方法来告诉内核(linux)将数据从磁盘预取到内存/ cpu缓存中?

编辑1:
一些人建议,如果不改善数据局部性,这根本是不可能的。但是,在这种情况下,我确实想知道为什么在继续进入“对性能要求不高”的部分时不可能对磁盘进行异步读取,这应该允许更快的访问;是更多关于内核不执行此限制还是仅执行理论/物理限制?

编辑2:
一些建议使用一个单独的线程来预访问内存,以便让内核来预取它们。但是,我认为线程可能很昂贵。为每个预取启动一个线程真的有帮助吗?代码处于紧密循环中,因此可能意味着将需要启动/加入许多线程。另一方面,如果我仅使用一个线程,我应该如何与它交流有关预取的信息?

解决方法

这种类型的访问模式总是很慢,因为它可能会跳来跳去,而没有任何明智的方式来预测该模式。

我将尝试的方法是生成一个单独的内存映射键索引文件,其中仅包含键值和相应记录的偏移量;键按升序排列。这样,使用非常简单的二进制搜索,找到特定的密钥大约需要O(log N)的时间复杂度(取决于您如何处理重复的密钥)。

如果在操作过程中修改了100 GB文件中的密钥,则单个平面文件不适合描述数据。

如果您可以处理代码的复杂性,则数组形式的分区二进制搜索树将具有更好的性能。在这种情况下,您可以将索引文件分成固定大小的部分,例如64 kB(4096个键偏移对),以阵列形式包含完美平衡的二进制搜索树的矩形部分。例如,第一个分区包含中间键,1/4和3/4键,1 / 8、3 / 8、5 / 8和7/8键,等等。此外,您仅在主索引文件中包含键,而将辅助索引文件用于记录偏移。 (如果您有重复的密钥,请让辅助索引文件引用第一个索引文件,而每个重复的第二个索引文件条目引用下一个索引文件,因此您可以直接跟踪链,而不会花费太多时间,但不会增加空间成本。) / p>

这比排序数组上的二进制搜索要好得多,但是代码和逻辑复杂性有点令人生畏。