Make WordPress Core

Opened 8 weeks ago

Closed 8 days ago

#63481 closed defect (bug) (worksforme)

WP_Image_Editor_Imagick takes longer time to upload image

Reported by: alimuzzamanalim's profile alimuzzamanalim Owned by:
Milestone: Priority: normal
Severity: major Version:
Component: Media Keywords: close
Focuses: performance Cc:

Description

Has anyone faced issue where

WP_Image_Editor_Imagick

takes longer time to generate metadata/sizes?

Is this known issue?
Is there any ticket already?
A specific image (281kb) upload is taking around 45 second. Where other image is doing just fine, even larger one.
With WP_Image_Editor_GD works fast.

Attachments (1)

44.png (274.6 KB) - added by alimuzzamanalim 8 weeks ago.

Download all attachments as: .zip

Change History (12)

@alimuzzamanalim
8 weeks ago

#1 in reply to: ↑ description @siliconforks
7 weeks ago

Replying to alimuzzamanalim:

Is this known issue?
Is there any ticket already?

Not exactly, but there have been a number of ImageMagick-related issues since 6.8 was released - #63338, #63448. I suspect it is the same code which is causing all of these issues (originally introduced in changeset [59589]).

This may have been fixed already with a changeset made a few days ago: [60246].

#2 @SirLouen
7 weeks ago

  • Keywords reporter-feedback added

@alimuzzamanalim can you be more specific on how are you testing this?
What do you mean with "generate metadata" ?

#3 follow-up: @siliconforks
7 weeks ago

Here's a simplified test script which demonstrates the issue:

<?php

$filename = '44.png';
$width = 1024;
$height = 665;

$image = new Imagick( $filename );
$image->resizeImage( $width, $height, Imagick::FILTER_TRIANGLE, 1 );
$start_time = hrtime( true) ;
$colors = $image->getImageColors();
$end_time = hrtime( true );
$nanoseconds = $end_time - $start_time;
$seconds = $nanoseconds / 1e9;
echo 'Elapsed time: ' . sprintf( '%.3F', $seconds ) . "\n";

This takes almost 10 seconds on my machine.

#4 in reply to: ↑ 3 ; follow-up: @SirLouen
7 weeks ago

Replying to siliconforks:

Here's a simplified test script which demonstrates the issue:

<?php

$filename = '44.png';
$width = 1024;
$height = 665;

$image = new Imagick( $filename );
$image->resizeImage( $width, $height, Imagick::FILTER_TRIANGLE, 1 );
$start_time = hrtime( true) ;
$colors = $image->getImageColors();
$end_time = hrtime( true );
$nanoseconds = $end_time - $start_time;
$seconds = $nanoseconds / 1e9;
echo 'Elapsed time: ' . sprintf( '%.3F', $seconds ) . "\n";

This takes almost 10 seconds on my machine.

But you are only using Imagick here, not WP_Image_Editor_Imagick.
Theoretically, we cannot control how much does it take to resize an image using a 3rd party library like this.

#5 in reply to: ↑ 4 @siliconforks
7 weeks ago

Replying to SirLouen:

Theoretically, we cannot control how much does it take to resize an image using a 3rd party library like this.

I think it's mostly the call to getImageColors (which was never done before [59589]) that is causing the performance issue.

Someone warned that getImageColors is slow almost a decade ago. It seems like this is still an issue with recent ImageMagick versions.

#6 @alimuzzamanalim
7 weeks ago

@siliconforks the issue simply happens when you upload image from media library wp-admin/media-new.php and wp uses WP_Image_Editor_Imagick (checked in Site Health).

Sorry for the confusion.

#7 @SirLouen
7 weeks ago

  • Focuses performance added
  • Keywords needs-patch 2nd-opinion added; reporter-feedback removed

#8 follow-up: @nosilver4u
7 weeks ago

@SirLouen Not only the call to getImageColors will be slow on a non-indexed PNG, but quantizeImage is also likely adding a bit of overhead.

#9 in reply to: ↑ 8 @siliconforks
7 weeks ago

Replying to nosilver4u:

@SirLouen Not only the call to getImageColors will be slow on a non-indexed PNG, but quantizeImage is also likely adding a bit of overhead.

Maybe a bit ... But I think the main issue is that getImageColors is just pathologically slow for some reason.

If I try calling quantizeImage with a fixed hard-coded value (256) for the number of colors, it only seems to take maybe a few hundredths of a second. But if I try calling getImageColors, that appears to be where the real problem is:

<?php

$filename = '44.png';
$width = 1024;
$height = 665;

$output_filename = '44-1024x665.png';
$start_time = hrtime( true ) ;
$image = new Imagick( $filename );
$image->resizeImage( $width, $height, Imagick::FILTER_TRIANGLE, 1 );
$image->writeImage( $output_filename );
$end_time = hrtime( true );
$nanoseconds = $end_time - $start_time;
$seconds = $nanoseconds / 1e9;
echo 'Size: ' . filesize( $output_filename ) . "\n";
echo 'Elapsed time: ' . sprintf( '%.3F', $seconds ) . "\n";

$output_filename = '44-1024x665-quantizeImage.png';
$start_time = hrtime( true ) ;
$image = new Imagick( $filename );
$image->resizeImage( $width, $height, Imagick::FILTER_TRIANGLE, 1 );
$image->quantizeImage( 256, $image->getColorspace(), 0, false, false );
$image->writeImage( $output_filename );
$end_time = hrtime( true );
$nanoseconds = $end_time - $start_time;
$seconds = $nanoseconds / 1e9;
echo 'Size (quantizeImage): ' . filesize( $output_filename ) . "\n";
echo 'Elapsed time: ' . sprintf( '%.3F', $seconds ) . "\n";

$output_filename = '44-1024x665-getImageColors.png';
$start_time = hrtime( true ) ;
$image = new Imagick( $filename );
$image->resizeImage( $width, $height, Imagick::FILTER_TRIANGLE, 1 );
$current_colors = $image->getImageColors();
$max_colors = min( 256, $current_colors );
$image->quantizeImage( $max_colors, $image->getColorspace(), 0, false, false );
$image->writeImage( $output_filename );
$end_time = hrtime( true );
$nanoseconds = $end_time - $start_time;
$seconds = $nanoseconds / 1e9;
echo 'Size (getImageColors): ' . filesize( $output_filename ) . "\n";
echo 'Elapsed time: ' . sprintf( '%.3F', $seconds ) . "\n";

The above code tries three different things:

  1. Resize the image without reducing the number of colors
  2. Resize the image calling quantizeImage with a fixed value of 256
  3. Resize the image calling quantizeImage with a value derived from getImageColors

This is the result I got:

Size: 505955
Elapsed time: 0.526
Size (quantizeImage): 185245
Elapsed time: 0.558
Size (getImageColors): 185245
Elapsed time: 10.328

#10 @SirLouen
7 weeks ago

  • Keywords close added; needs-patch 2nd-opinion removed

I've been reviewing the Imagick's source code for getImageColors and it's an n2 complexity. The algorithm basically has to check every single pixel in the image to determine the number of colors, and it's a trustworthy operation, meaning they have not applied any sampling optimizations.

Currently, we could say that this report has been fixed because in [60246] we don't use this function any more. For this reason, I will say that we should bring this conversation back to where we are discussing the future of compression #63448, taking this observations by @siliconforks as new context and close this as worksforme (fixed, in the current trunk), to avoid having the conversation context divided between two threads.

#11 @adamsilverstein
8 days ago

  • Milestone Awaiting Review deleted
  • Resolution set to worksforme
  • Status changed from new to closed

Closing because this is now resolved in trunk. Thanks for the bug report @alimuzzamanalim!

Note: See TracTickets for help on using tickets.