本文经AI新媒体量子位(公众号ID:QbitAI)授权转载,转载请联系出处。
提到 PNG ,大多数人都不会感到陌生。
这种 位图格式 在图像领域使用频率仅次于JPEG。
然而在“解码PNG”这件事上,23年来主流的工具是一个叫做 libpng 的标准库。
但最近,一款号称“世界上最快的PNG图像解码器”诞生了,速度是“老大哥”的1.22-2.75倍!
除了速度方面的优势之外,更重要的一点,极其 安全 。
超快的PNG图像解码器
与用C语言为底层的libpng不同,这款PNG图像解码器采用的是 Wuffs 。
Wuffs是一种内存安全的编程语言(也是用这种语言编写的标准库),用于 安全处理不受信任的文件格式 。
包括解析、解码和编码图像,音频,视频,字体等。
Wuffs不是一种通用编程语言。它是用来编写库的,完整的程序需要将Wuffs与另一种编程语言结合使用。
尤其是需要同时考虑性能和安全性的时候。
ps. 机智的你发现了吗,Wuffs其实就是Wrangling Untrusted File Formats Safely的缩写。
不过它以前叫Puffs,至于为什么将Puffs(parsing)改为Wuffs(wrangling),留给你们自己去想象~
那么,解码速度快至2.75倍,怎么做到的?
Wuffs通过SIMD加速方案,8字节宽的输入和复制,一次将整个图像进行位扭曲和zlib解压缩到一个大的中间缓冲区来实现高性能。此法替代了此前的一次一行(小块重复压缩)的方式。
这“一包带走”的操作需要更多的中间存储,但能解码的图像数量也更多了。
具体咋回事儿呢?
我们知道,PNG图像格式编/解码基于以下三方面:
Wuffs对这其中的每一步都进行了优化。
首先 ,对两种校验和算法施以SIMD加速技术。
SIMD是一种采用一个控制器来控制多个处理器,同时对一组数据中的每一个分别执行相同的操作从而实现空间上的并行性的技术。
其次 ,0.2版本Wuffs具有与zlib库一样的DEFLATE实现,而0.3版Wuffs为现代CPU(具有64位未对齐加载和存储)添加了两个重要的优化: 8字节区块输入和8字节区块输出 。
DEFLATE是同时使用了LZ77算法与哈夫曼编码的一个无损数据压缩算法。
对于Wuffs,8字节区块输入设计的每个内部循环一次读取64位可使DEFLATE微基准加速多达 1.3倍 。
而8字节区块输出设计将副本长度舍入为8的倍数可以使DEFLATE微基准提高多达 1.48倍 。
此外,DEFLATE涉及写入目标缓冲区和写入缓冲区边界的问题。
(经典的“缓冲区溢出”安全漏洞,类似于从悬崖上奔跑,如何不落入鲨鱼口中)。
此方面,Wuffs使用和libpng相似的 蓝/红双重实现技术 。
蓝/红双重实现技术:一种快速的“蓝色”解压缩(在距缓冲区末端至少258个字节或更多字节 时)以及一种缓慢的“红色”解压缩(反之)技术。
以前面的从悬崖上奔跑做比喻,就是在离崖边还远时尽可能得快跑,离崖边很近时减速刹车。
但同样的技术,为什么Wuffs更快?
因为它能一次将几乎所有内容(eg. 300×200 RGB图像的像素的99%以上)解压缩到一个大的中间缓冲区中,而不是一次只压缩一行到一个小的、可重复使用的中间缓冲区中。
如图所示,几乎所有内容现在都在“蓝色”区域中解码。
这本身就比“红色”区域快。
而且在蓝色代码和红色代码之间交替时,Wuffs也避免了任何指令高速缓存或分支预测变慢的情况。
最后 ,虽然Wuffs和libpng都具有PNG二维过滤的SIMD实现。
但是因为libpng将任何自分配的像素行缓冲区对齐到最适合SIMD的边界时,对齐这步操作会影响SIMD指令的选择和性能。
而Wuffs对缓冲区对齐的承诺较少,部分原因是Wuffs不具有分配内存的能力,但主要还是因为一次全部解压缩时,zlib压缩要求放弃例如每行开头4字节的对齐。
为什么说最安全?
与Go或Rust不同,Wuffs的内存安全是在编译时强制执行的,而不是通过插入运行时再检查。
( ps.运行时安全检查也可能会影响性能。)
此外,在处理不受信任的(第三方)PNG图像时,沙盒和多进程体系结构可以提供额外的深度防御。
在上一节描述的三步优化技术也可用来给现有的libpng、Go/Rust PNG库等打补丁。
然后就有网友说这样一来Wuffs还是不是最快的就难说了……但开发者表示至少在安全性方面,Wuffs No.1没得说。
最后,Wuffs版本0.3.0-beta.1刚刚发布,但从目前的功能来看,它还不支持颜色空间或伽马校正。