如何在 PHP 中优化 VIPS 性能

How to optimize VIPS performance in PHP

我想尽快将变量中作为字符串保存的图像转换为 WebP 格式,同时缩小较大的图像但不放大较小的图像。 基本系统是带有 PHP 7.3 的 Debian 9.9。我尝试使用以下技术测量速度:imagejpegimagewebp、使用 cwepphp-vips。我使用了以下代码:

$jpeg = function() use ($image) {
    $old_image = @imagecreatefromstring($image);
    $old_width = (int)@imagesx($old_image);
    $old_height = (int)@imagesy($old_image);
    $new_width = 1920;
    $new_width = min($old_width, $new_width);
    $ratio = $new_width / $old_width;
    $new_height = $old_height * $ratio;
    $new_image = imagecreatetruecolor($new_width, $new_height);
    imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $new_width, $new_height, $old_width, $old_height);
    ob_start();
    imagejpeg($new_image, NULL, 75);
    $image = ob_get_clean();
};
$webp = function() use ($image) {
    $old_image = @imagecreatefromstring($image);
    $old_width = (int)@imagesx($old_image);
    $old_height = (int)@imagesy($old_image);
    $new_width = 1920;
    $new_width = min($old_width, $new_width);
    $ratio = $new_width / $old_width;
    $new_height = $old_height * $ratio;
    $new_image = imagecreatetruecolor($new_width, $new_height);
    imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $new_width, $new_height, $old_width, $old_height);
    ob_start();
    imagewebp($new_image, NULL, 75);
    $image = ob_get_clean();
};
$convert = function(string $image, int $width, int $height) {
    $cmd = sprintf('cwebp -m 0 -q 75 -resize %d %d -o - -- -', $width, $height);
    $fd = [
        0 => [ 'pipe', 'r' ], // stdin is a pipe that the child will read from
        1 => [ 'pipe', 'w' ], // stdout is a pipe that the child will write to
        2 => [ 'pipe', 'w' ], // stderr is a pipe that the child will write to
    ];
    $process = proc_open($cmd, $fd, $pipes, NULL, NULL);
    if (is_resource($process)) {
        fwrite($pipes[0], $image);
        fclose($pipes[0]);
        $webp = stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        $result = proc_close($process);
        if ($result === 0 && strlen($webp)) {
            return $webp;
        }
    }
    return FALSE;
};
$cwebp = function() use ($image, $convert) {
    $old_image = @imagecreatefromstring($image);
    $old_width = (int)@imagesx($old_image);
    $old_height = (int)@imagesy($old_image);
    $new_width = 1920;
    $new_width = min($old_width, $new_width);
    $ratio = $new_width / $old_width;
    $new_height = $old_height * $ratio;
    $image = $convert($image, $new_width, $new_height);
};
$vips = function() use ($image) {
    $image = Vips\Image::newFromBuffer($image);
    $old_width = (int)$image->get('width');
    $old_height = (int)$image->get('height');
    $new_width = 1920;
    $new_width = min($old_width, $new_width);
    $ratio = $new_width / $old_width;
    // $new_height = $old_height * $ratio;
    $image = $image->resize($ratio);
    $image = $image->writeToBuffer('.webp[Q=75]');
};

我在一个循环中调用了 $jpeg()$webp()$cwebp()$vips() 十次,以秒为单位的运行时间是:

JPEG: 0.65100622177124
WEBP: 1.4864070415497
CWEBP: 0.52562999725342
VIPS: 1.1211001873016

所以调用 cwebp CLI 工具似乎是最快的方法,这令人惊讶。我读过很多次 vips 是一个非常快的工具(大部分比 imagemagick 快),所以我想关注 vips.

谁能帮我优化 $vips() 以获得更好的性能?也许 writeToBuffer()resize() 有一些我不知道的选项。非常重要的是,所有操作都只在内存中进行,而无需从磁盘读取文件或将文件存储在磁盘上。

为了速度,不要使用resize,使用thumbnail_buffer。它将打开和调整大小结合在一个操作中,因此它可以利用加载时收缩等功能。根据图像格式和大小,您可以获得巨大的加速。

您可以将您的 cwebp 设置与以下内容相匹配:

$vips = function() use ($source_bytes) {
    $image = Vips\Image::thumbnail_buffer($source_bytes, 1920);
    $dest_bytes = $image->writeToBuffer('.webp', [
        'Q' => 75,
        'reduction-effort' => 0,
        'strip' => TRUE
    ]);
};

libvips 在直接 webp 压缩时似乎较慢。我试过了:

$ time cwebp -m 0 -q 75 ~/pics/k2.jpg -o x.webp
real    0m0.102s
user    0m0.087s
sys 0m0.012s

匹配的 vips 命令为:

$ time vips copy ~/pics/k2.jpg x.webp[Q=75,reduction-effort=0,strip]
real    0m0.144s
user    0m0.129s
sys 0m0.024s

您可以看到它可能慢了 30%。这里没有图像处理,只是调用 libjpeg 和 libwebp,因此 cwebp 必须使用一些 libwebp 保存优化,而 libvips 没有。他们 运行 与一年前的速度相同——我应该再读一遍 cwebp.c 看看有什么变化。

如果你也做一些处理,那么 libvips 会变得更快。我尝试使用 10,000 x 10,000 像素的图像并调整大小:

$ /usr/bin/time -F %M:%e cwebp -m 0 -q 75 ~/pics/wtc.jpg -resize 1920 1920 -o x.webp
618716:1.37

620mb 内存和 1.4s,对比:

$ /usr/bin/time -f %M:%e vipsthumbnail ~/pics/wtc.jpg -s 1920 -o x.webp[Q=75,reduction-effort=0,strip]
64024:1.08

64mb 内存和 1.1s。

所以 libvips 运行速度更快,需要的内存更少,尽管在 webp 压缩时速度较慢,因为它可以快速调整大小。 libvips 也在进行更高质量的调整大小(自适应 lanczos3),而不是 cwebp 的简单立方体(我认为)。