我需要读取/解析一个大型二进制文件(4~6 GB),该文件以8192字节的固定块存在。我目前的解决方案涉及使用Single Producer Multiple Consumer(SPMC)模式流式传输文件块。
修改
文件大小= N * 8192字节
我需要做的就是对这8192个字节中的每个字节做一些事情。该文件只需要自上而下读取一次。
考虑到这应该是一个令人尴尬的并行问题,我希望 X 线程在相同的范围内读取(文件大小/ X )大小独立。线程根本不需要彼此通信。
我尝试生成 X 线程来打开同一个文件并寻求各自的部分进行处理,但是,这个解决方案似乎有问题由于HDD机械寻求并且显然比SPMC解决方案表现更差。
如果在SSD上使用此方法会有什么不同吗?
或者更简单地将内存映射到整个文件并使用#pragma omp parallel for
来处理块?我想我需要足够的RAM才能做到这一点?
你会建议什么?
答案 0 :(得分:5)
你会建议什么?
请勿使用mmap()
人们喜欢用mmap()和其他方式来玩页表 优化复制操作,有时值得。
但是,使用虚拟内存映射玩游戏非常有用 本身很贵。它有许多非常不利的缺点 人们往往会忽视,因为记忆复制被认为是非常重要的东西 缓慢,有时优化该副本被视为一个显而易见的 改良效果。
下行到mmap:
- 非常明显的设置和拆卸成本。我的意思是明显的。 它就像跟随页面表一样干净地取消映射所有内容。它是用于维护所有内容列表的簿记 映射。它是取消映射后需要的TLB刷新。
- 页面错误是昂贵的。这就是映射如何填充,而且速度很慢。
mmap的好处:
- 如果数据被一遍又一遍地重复使用(在单个地图操作中),或者如果你可以通过映射内容来避免很多其他逻辑,那么mmap()就是切片面包以来最好的东西。
这可能是你多次访问的文件(可执行文件的二进制图像在这里是明显的例子 - 代码在各处跳转),或者是一个设置它可以方便地映射的文件整个过程中不考虑mmap()刚刚获胜的实际使用模式。您可能拥有随机访问模式,并使用mmap()作为跟踪实际需要的数据的方式。
如果数据很大,mmap()是让系统知道它可以用数据集做什么的好方法。内核可以忘记页面,因为内存压力迫使系统将页面输出,然后再自动重新获取它们。
自动共享显然就是这种情况。
但是你的测试套件(只是复制一次数据)可能是悲观的 对于mmap()。
注意最后一次 - 仅使用一次数据对于mmap()
来说是一个不好的用例。
对于SSD上的文件,因为没有物理头部搜索动作:
使用open()
打开文件一次,获取单个int
文件描述符。
每个线程使用pread()
来读取适当的8kB块。 pread()
在不使用lseek()
的情况下从指定的偏移量读取,并且不会影响正在读取的文件的当前偏移量。
您可能需要比CPU核心更多的线程,因为在每个线程上都会有重要的IO等待。
对于机械磁盘上的文件:
您希望最小化机械磁盘上的磁头搜索。
使用带有直接IO的open()
打开文件一次(假设Linux,open( filename, O_RDONLY | O_DIRECT );
)以绕过页面缓存(因为您要转发文件并且永远不会重新读取任何文件)它的一部分,页面缓存对你没有好处)
读取缓冲区时,将其传递给工作线程,然后读取以填充下一个缓冲区
当所有工作人员使用他们的部分缓冲区完成后,通过 缓冲回阅读线程。
您需要尝试使用正确的read()
大小,工作线程数以及传递的缓冲区数量。较大的read()
将更有效,但较大的缓冲区大小会使内存需求更大,并且使得从工作线程返回缓冲区的延迟更加难以预测。您希望尽可能少地创建数据副本,因此您希望工作线程直接在从文件读取的缓冲区上工作。
答案 1 :(得分:4)
即使每个8K块的处理很重要(缺少OCR处理),i / o也是瓶颈。除非可以安排文件的某些部分已经被先前的操作缓存....
如果要运行的系统可以专用于此问题:
fstat
)然后,使用异步读取修改它。请参阅man aio_read
和man 7 aio
了解需要完成的工作。