Java中的多线程解压缩

问题描述:

因此,我试图用Java以zip方式进行只读访问,使用多线程方式进行解压缩,因为我的ZipFile/ZipEntry标准简单单线程解决方案使用枚举和输入流以及什么不会导致它花费大约五整秒来解压缩到内存中一个50兆zip文件,这需要一秒AT MOST让我的磁盘无需解压缩即可读取。Java中的多线程解压缩

但是,整个Java zip库同步到令人难以置信的恶劣程度,毫无疑问,因为它都是抽象的读/写/等。在相同的代码中,而不是具有高效的非同步只读代码。

我已经看过第三方Java库,它们都是比使用大象枪射击苍蝇更糟糕的海量VFS库,否则他们获得性能优势的唯一原因是他们多线程无论如何,大部分线程都在磁盘IO上阻塞。

我只想把一个zip文件拉到一个byte []中,分叉一些线程,然后处理它。没有任何理由需要以任何方式进行任何同步,因为我在内存中分别使用每个解压缩文件而没有交互。

这为什么这么难?

+1

你缓冲自己的输入和输出? –

+0

你是什么意思“拉一个zipfile到一个字节[]”?你的意思是你把它全部读到一个字节数组中吗?如果是这样,它是压缩格式还是解压缩格式?以及如何处理文件太大而无法将所有内容保存到字节数组中?我认为如果你不把它全部放入RAM中,你将回到单个文件被多个线程使用的问题,这通常不会给你任何优于单个线程的优势。 –

+0

这些文件都不会很大:它们全都适合记忆,空间很小,不会有问题。但使用zip文件的随机访问特性会影响性能,而一次读取整个文件然后在内存中使用它会更好,因为硬盘驱动器在优化读取操作方面非常出色。 – JAKJ

用Java实现这一点的最快方法是使用NIO。您可以通过使用MappedByteBuffer将文件直接映射到内存中。

FileChannel channel = FileChannel.open(Paths.get("/path/to/zip"), 
    StandardOpenOption.READ); 
MappedByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, channel.size()); 

现在buffer包含整个文件的内存映射区域。你可以随心所欲地做任何事情,例如将一个offset和一个length传递给一个线程。我不知道哪个zip库支持,但显然你已经有这样的东西。我想测试一下50Mb的单个文件档案,平均只用不到200ms就可以用一般的ZipInputStream来读取它 - 我认为你在这里几乎没有什么优化。

+0

无论如何,这听起来像是一个很好的选择。我会。 – JAKJ

+0

@JAKJ您还可以使用更多的代码将缓冲区桥接到输入流,从而将'ZipInputStream'覆盖在缓冲区上。请参阅:http://stackoverflow.com/a/6603018/540873 –

+0

但这会带来什么好处?内存映射文件的内容在被访问之前仍然在磁盘上,直接访问磁盘缓存的RAM页面的低级API与通过常规文件API访问相同页面的内容之间的区别是相当微不足道的。包装成顺序的“InputStream”处理随机访问文件可能存在的任何优势。 –

只是为了子孙后代的缘故,在一些测试来回,我终于结束了使用变如下(从关闭的文件从头一个while (true)循环开始完成迭代)答案:

  • 使用DataInputStream.readFully拉整个(50兆,在这种情况下, )zip文件到byte[]

  • 菌种工作线程(在我的情况下每个物理CPU核心之一,4) 其中每个采取byte[]并创建一个 ZipInputStream(ByteArrayInputStream)。第一个工人跳过0 条目,第二跳跳过1,第二跳跳2等,因此它们都是 彼此偏移1。工作线程完全不同步 ,因此它们都有自己的本地副本,该文件的压缩文件的元数据为 ,而不是什么。这是线程安全的,因为zip文件是 只读且工作人员不共享解压缩数据。

  • 每个工作线程读取一个条目并对其进行处理,然后跳过 足够的条目,以使它们再次被偏移1。所以第一个 线程读取条目0,4,8 ...,第二个读取1,5,9 ...,然后第 。

  • 所有的工人都用.join()拉回来。

我的时间如下:

  • 读取压缩文件到byte[]与所有 (只是IO)没有解压给出0.1秒的平均每次迭代。

  • 直接在底层文件使用直的ZipFile为正常, 产生0.5秒的初始​​尖峰,随后平均0.26 秒的每次迭代之后(从新鲜开始关闭 先前的ZipFile之后)。

  • 读取的ZipFile成byte[],没有多线程 在所有创建 ZipInputStream(ByteArrayInputStream)有了它,结果在0.3秒的初始尖峰,随后是 平均0.26秒的其后每次迭代,显示出 磁盘高速缓存具有渲染随机访问和 初始读取等效的效果。

  • 读取的ZipFile成byte[],产卵4个工作线程 如上所述,与该byte[],并等待他们 光洁度,所带来的时间回落到平均0.1秒的每 迭代。

因此,判决是,用这种方法我已经成功地将一个中等规模的压缩文件的处理与适度强大的计算机下来所花费的时间简单地物理读取该文件,如果加上减压步骤完全不再显眼。很明显,对于数以万计条目的巨大zip文件,这种方法仍然会产生巨大的加速。

似乎我并没有试图优化没有任何东西,考虑到我减少了样本文件的处理时间(这是我需要处理的最大样本的大小)到38%的简单单一线程方法。

考虑如何令人难以置信的这个技巧,工作的工作,想象与实际设计要做到这一点,不带内置的同步本地Java拉链阅读器类可能加速。