我有一个速度关键程序,它将重复从磁盘读取图像并从中计算值。图像太多,无法存储在内存中。
将读取相同的图像集,我们不会更改/编辑它们,并且它们的顺序是固定的。
并非所有图像都具有相同的大小,但在编码为PNG时它们都具有大约1 Mb。它们有成千上万,大部分RAM已经用于存储计算值。
除了购买更快的磁盘或使用RAID之外,读取一系列图像的最快方法是什么?
将它们全部放在大型tar文件中(并使用自定义代码读取它们)而不是文件夹中的单个文件会更快吗?
我找不到PNG解码的多线程实现,所以这个阶段也可能成为瓶颈。使用WebP而不是PNG会提供额外的速度优势吗?
我应该考虑/评估哪些其他想法?
答案 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,但在磁盘访问上显示“频谱的两面”。令人惊讶的是,两台机器的结论都相同。我只报告一组结果(对于后一种情况)。文件格式之间的比率在两个实验中几乎相同。
从这些实验中我学到了两件重要的事情:
我正在修复序列中读取一组~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慢,我只能推测网络磁盘系统有一些“内部”缓存,这有点改变了行为。但这不会影响课程。
如果您将多次读取文件(或一组文件),操作系统磁盘缓存将使所有未来读取基本上“与从RAM读取一样快”。
即使从磁盘读取,解压缩图像的时间也是不可忽视的。
将所有文件放入单个未压缩(tar)文件中会使事情变得更快,因为操作系统将假定将读取整个文件,甚至在我们访问之前预先加载未来图像。只是在文件夹内阅读时,这似乎不会发生。
在从磁盘读取一系列图像时(特别是重复读取),可以小心地获得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小于1,则意味着您读取的速度将超过消耗图像的速度,因此不再需要更快的读取速度。但是,如果您的消费图像更快(例如,N个线程一起工作以消耗图像),那么您需要更多线程来在内存中存储足够的图像单元。
使用线程构建消费者 - 生产者模型在理论上是直截了当的。但实施通常很棘手。
PS:在单个处理器上运行多个线程通常比普通的无线程序效率低。除非你有多核机器,否则我看不到改进的方法。