2016-12-21 127 views
7

我需要读取/解析大小为8192字节的固定块的二进制文件(4〜6 GB)。我目前的解决方案涉及使用单生产者多用户(SPMC)模式对文件块进行流式传输。多线程文件读取

编辑

文件大小= N * 8192个字节

所有我需要做的是做一些事来每个8192个字节。该文件只需要自上而下阅读一次。

转念一想,这应该是一个尴尬的并行问题,我想有X线程在同等范围内读取(文件大小/ X)独立尺寸。线程根本不需要互相通信。

我已经试过产卵X线程打开相同的文件,寻求各自的部分来处理,但是,这种解决方案似乎有一个问题,在由于HDD机械力求显然比执行更糟糕SPMC解决方案。

如果在SSD上使用此方法,会有什么区别吗?

或者更简单的方法是只对内存映射整个文件并使用#pragma omp parallel for来处理这些块?我想我会需要足够的RAM来做到这一点?

你会建议什么?

+0

您不会对文件的语法和格式有足够的了解。 –

+1

它是一个N * 8192字节的二进制文件,我需要对这8192个字节分别进行操作。 –

+2

系统调用(假设Linux操作系统)提到[这里](http://stackoverflow.com/a/41237690/841108)可能会有所帮助。但我猜想瓶颈是磁盘本身。你可能不需要太多的线程。 (所以* X *将会是4到10,不会有很多)。你应该基准。这不是一个令人尴尬的并行问题,因为硬件磁盘一次只能处理一个(或很少)请求。 –

回答

5

你会建议什么?

不要使用mmap()

Linux Torvalds himself

人们喜欢的mmap()等方式与页表发挥 优化掉复制操作,有时这是值得的。然而,使用虚拟内存映射玩游戏非常昂贵。它有一些非常真实的缺点, 人们往往忽视,因为内存复制被认为是非常缓慢的东西,有时优化该副本远离被视为显而易见的 改进。

不利之处MMAP:

  • 相当明显的安装和拆卸的成本。我的意思是明显的。 就像在页表中清除所有内容一样。这是记录所有
    映射的列表。这是取消映射东西后需要的TLB刷新。
  • 页面错误是昂贵的。这就是地图填充的方式,而且速度很慢。 MMAP的

上升空间:

  • 如果数据被重新使用一遍又一遍(在一张地图操作中),或者如果你可以通过只在映射的东西避免了很多其他的逻辑,mmap()只是自切片面包以来最伟大的事情。

这可能是你很多次去了一个文件(可执行的二进制图像是明显的情况下,在这里 - 代码跳转各地的地方),或设置在那里,实在太方便映射而不考虑mmap()刚刚赢得的实际使用模式。您可能有随机访问模式,并使用mmap()作为跟踪您实际需要的数据的一种方式。

  • 如果数据很大,mmap()是让系统知道它可以对数据集做些什么的好方法。内存可能因为内存压力而忘记页面,迫使系统将页面内容分页,然后再自动重新获取它们。

    而自动共享显然就是这样的一个例子。

但你的测试套件(只复制一次数据)可能是pessimal 对mmap()的。

注意最后 - 只使用一次数据对于mmap()是一个不好的用例。

有关的SSD文件,因为没有物理磁头寻道动作:

  1. 打开文件一次,使用open()得到一个int文件描述符。

  2. 每个线程使用pread()来读取适当的8kB块。 pread()不使用lseek()而从指定的偏移量读取,并且不会影响正在读取的文件的当前偏移量。

由于在每个线程中都会有相当多的IO等待,因此您可能需要比CPU核心更多的线程。

对于机械硬盘(一个或多个)上的一个文件:

你希望尽量减少磁头寻道(S)机械磁盘上​​。

打开文件一次,使用open()直接IO(假设Linux的,open(filename, O_RDONLY | O_DIRECT);)绕过页面缓存(因为你要流的文件,从来没有重读的任何部分,页面缓存做了你这里没有好)

  1. 使用单个生产者线程,读取大块(说64K到1MB +) 为N页对齐缓冲器中的一个。
  2. 当读取一个缓冲区,将其传递给工作线程,然后读,填补了一个缓冲

  3. 当所有的工人正在用自己的缓冲区的部分完成,通过 缓冲区回读线程。

你需要用正确的read()大小,工作线程的数量,并通过周围的缓冲区的数目进行试验。较大的read() s会更高效,但较大的缓冲区大小会使内存需求变大,并使得从工作线程获得该缓冲区的延迟变得更加难以预测。您希望尽可能少地拷贝数据,所以您希望工作线程直接在从文件读取的缓冲区上工作。

+0

*不使用mmap *的前提似乎是*仅使用一次数据*。我没有读到这个问题。 – Zulan

+0

@Zulan你能否解释一下你从什么地方读取数据将从文件中读取多次的问题? –

+0

我也没有读过。我所读的全部是字面意思*“做某事”*。 – Zulan

4

即使每个8K块的处理都很重要(缺少OCR处理),I/O仍是瓶颈。除非能够安排到由以前的操作中已缓存文件的部分....

如果系统是在运行上可以是专用的问题:

  1. 获取文件大小(fstat
  2. 分配一个大小的缓冲区。
  3. 打开并将整个文件读入缓冲区。
  4. 找出如何为每个线程分割数据并分离线程。
  5. 算法的时间。

然后,使用异步读取对其进行修改。请参阅man aio_readman 7 aio以了解需要完成的工作。

+1

如果你想要这样读取文件,我会添加使用直接IO。像这样的处理的两个缺点是1)内存要求 - 文件必须适合内存和2)延迟 - 直到整个文件被读取才开始处理。不过,这当然是最简单和最可维护的方法。 –