读取图像序列的最快方法是什么?

时间:2013-05-17 13:29:07

标签: c++ image performance io

我有一个速度关键程序,它将重复从磁盘读取图像并从中计算值。图像太多,无法存储在内存中。

将读取相同的图像集,我们不会更改/编辑它们,并且它们的顺序是固定的。

并非所有图像都具有相同的大小,但在编码为PNG时它们都具有大约1 Mb。它们有成千上万,大部分RAM已经用于存储计算值。

  

除了购买更快的磁盘或使用RAID之外,读取一系列图像的最快方法是什么?

将它们全部放在大型tar文件中(并使用自定义代码读取它们)而不是文件夹中的单个文件会更快吗?

我找不到PNG解码的多线程实现,所以这个阶段也可能成为瓶颈。使用WebP而不是PNG会提供额外的速度优势吗?

我应该考虑/评估哪些其他想法?

5 个答案:

答案 0 :(得分:3)

PNG不是为速度而打造的。它比jpeg慢,并且不小于tif。如果你坚持使用PNG,那么其他任何优化都不会产生任何影响。

例如:

$ time vips avg wtc.tif
117.853995
real    0m0.525s
user    0m0.756s
sys 0m0.580s
$ time vips avg wtc.png
117.853995
real    0m3.622s
user    0m3.984s
sys 0m0.584s

其中“wtc”是10,000 x 10,000像素RGB照片,tif是未压缩的条带格式,png也是未压缩的,两个图像都在光盘缓存中,“avg”找到并打印平均像素值。

vips有自己的“.v”格式,这只是一个巨大的像素缓冲区。这种格式可以与mmap()并行读取,并且再次快一点:

$ time vips avg wtc.v
117.853995
real    0m0.162s
user    0m0.460s
sys 0m0.092s

如果你的图像可以被压缩,那么权衡会有所改变。例如,jpeg通常压缩10倍,因此解码速度变得比光盘速度重要得多。您希望使用像libturbojpeg这样的优化解码库,并一次处理多个文件。

$ time vips avg wtc.jpg
117.853995 
real    0m1.413s
user    0m1.696s
sys 0m0.564s

PNG使用libz,对于摄影图像,压缩不会超过2倍。即使在相同的压缩级别,它也比使用deflate的tif慢得多:

$ time vips avg wtc.tif
117.853995
real    0m3.154s
user    0m3.496s
sys 0m0.540s
$ time vips avg wtc.png
117.853995
real    0m4.888s
user    0m5.196s
sys 0m0.556s
$ ls -l wtc.*
-rw-r--r-- 1 john john  15150881 Feb 20  2012 wtc.jpg
-rw-rw-r-- 1 john john 135803013 May 18 12:47 wtc.png
-rw-rw-r-- 1 john john 143807446 May 18 12:53 wtc.tif
-rw-rw-r-- 1 john john 263509369 May 18 12:37 wtc.v

我认为另一个因素是您的处理时间。如果你正在做一些密集的事情,读取速度和解码速度将不重要。

答案 1 :(得分:3)

亲爱的堆栈溢出社区,

这里承诺的是根据您的许多建议完成的实验结果。 特别感谢@ user894763如何让我走上“正确的道路”。

  

tl; dr在未压缩的tar中使用 pnm文件(是的,我说过pnm!)。

我在两台高端机器上进行了实验,一台使用SSD磁盘,另一台使用网络文件系统。两者都有高端CPU,但在磁盘访问上显示“频谱的两面”。令人惊讶的是,两台机器的结论都相同。我只报告一组结果(对于后一种情况)。文件格式之间的比率在两个实验中几乎相同。

从这些实验中我学到了两件重要的事情:

  • 当关于来自磁盘的文件时,操作系统磁盘缓存是王道(即操作系统尽可能地尝试将文件操作保持在RAM而不是物理设备中,并且它在这方面做得非常好。)< / LI>
  • 与我最初的猜测相反,从磁盘读取图像是一个CPU限制操作,而不是I / O限制。

实验方案

我正在修复序列中读取一组~1200幅图像,没有对图像进行计算,我只是测量在内存中加载像素的时间。 tar文件大小约为600 MB,采用pnm格式,~300 MB采用png格式,约200 MB采用webp格式。

“Fresh read”表示在机器上进行首次读取 “缓存读取”表示在同一台机器(以及任何后续机器)上完成的第二次读取。

所有数字大约为+ - 10 Hz。

webp fresh read: 30 Hz
webp cached read: 80 Hz

webp + tar fresh read: 100 Hz
webp + tar cached read: 100 Hz

png fresh read:  50 Hz
png cached read: 165 Hz

png + tar fresh read: 200 Hz
png + tar cached read: 200 Hz

pnm fresh read: 50 Hz
pnm cached read: 600 Hz

pnm + tar fresh read: 200 Hz
pnm + tar cached read: 2300 Hz

注释

我被告知可能有办法更改webp压缩参数以使解压缩更快。我怀疑它仍然不符合pnm性能。

请注意,我使用自定义代码读取tar文件中的图像,文件从磁盘“image by image”读取。

我不知道为什么阅读webp图像“新鲜”比png慢,我只能推测网络磁盘系统有一些“内部”缓存,这有点改变了行为。但这不会影响课程。

吸取

  1. 如果您将多次读取文件(或一组文件),操作系统磁盘缓存将使所有未来读取基本上“与从RAM读取一样快”。

  2. 即使从磁盘读取,解压缩图像的时间也是不可忽视的。

  3. 将所有文件放入单个未压缩(tar)文件中会使事情变得更快,因为操作系统将假定将读取整个文件,甚至在我们访问之前预先加载未来图像。只是在文件夹内阅读时,这似乎不会发生。

  4. 在从磁盘读取一系列图像时(特别是重复读取),可以小心地获得4x~x10的加速因子。

答案 2 :(得分:1)

你应该改变阅读的顺序。也就是说,在从图像1读取到图像N的第一遍中,然后在从图像N读取到图像1的第二遍中,然后在从图像1读取到图像N的第三遍中,依此类推。这样你就可以更多地点击磁盘缓存。

在不同的线程中同时处理(或至少加载)多个图像也可能有利于整体吞吐量,因为操作系统将能够优化磁盘搜索。

如果操作系统对AIO有很好的支持,那么它也可能是有益的。

将图像放入单个文件可能确实有助于最小化搜索(但取决于文件系统碎片整理策略)。在这种情况下,您应该使用可快速访问单个文件的存档,以便能够以相反的顺序读取文件,例如没有压缩的“拉链”。

使用内存映射时,应该有一个选项可以要求操作系统预取部分内存映射文件(例如MAP_POPULATE)。以这种方式读取大部分存档可能会更快,然后逐块读取。

答案 3 :(得分:0)

内存映射,特别是因为您打算多次重新读取图像,这是将数据尽可能少地复制到RAM中的最快方法。
使用“聪明的技巧”(如无缓冲读取)来利用DMA是不可取的,因为这不会使用比磁盘快几个数量级的缓冲区。这种可能在触摸数据一次且只有一次时是一个优势 - 但是如果你想在你的情况下多次阅读一件,那就永远不会。正常缓冲读取通常比内存映射慢得多,因为它们需要进行内存复制。

在典型的硬盘上,第一次运行时的性能大约为100 MB / s,第二次运行时的性能大约为3-4 GB / s(在快速机器上可能更多)。

解码PNG涉及解压缩LZ77流,因此这也可能成为限制因素。为了解决这个问题,你可以多线程。多线程解码单个流并不是完全无关紧要的,但没有什么阻碍你同时解码多个图像( 非常简单)。

将图像连接成一个巨大的文件可能会带来优势,因为它可能会减少搜索,但如果您必须阅读数百或数千个文件,这通常才真正重要。在这种情况下,您最好将它们按照您读取它们的顺序存储(希望这会导致磁盘上的连续布局,但无法保证)。

答案 4 :(得分:0)

你应该问自己,

  • 计算在一个单元上计算的任何内容需要多长时间(完整图像或其中的一部分)。
  • 在此期间,您可以阅读多少单位的图像(假设为N)。

我不知道如何更快地阅读单个图像单元,但还有其他一些东西可以尝试。

创建共享/全局变量以保存图像单元。使用线程在其中存储图像单元。如果N小于1,则意味着您读取的速度将超过消耗图像的速度,因此不再需要更快的读取速度。但是,如果您的消费图像更快(例如,N个线程一起工作以消耗图像),那么您需要更多线程来在内存中存储足够的图像单元。

使用线程构建消费者 - 生产者模型在理论上是直截了当的。但实施通常很棘手。

PS:在单个处理器上运行多个线程通常比普通的无线程序效率低。除非你有多核机器,否则我看不到改进的方法。