Я работаю над сайтом, который использует ImageMagick для генерации изображений. Сайт будет получать сотни запросов каждую минуту, и использование ImageMagick для этого приводит к сбою сайта.
Поэтому мы реализовали Redis и Php-resque, чтобы генерировать ImageMagick в фоновом режиме на отдельном сервере, чтобы он не зависал на нашем главном. Проблема в том, что для создания изображений требуется очень много времени. Пользователь может ожидать до 2-3 минут ожидания запроса изображения, потому что сервер так занят обработкой этих изображений.
Я не уверен, какую информацию вам дать, но я больше ищу совет. Я думаю, что если мы сможем сократить начальное время обработки запроса ImageMagick, то, очевидно, это поможет ускорить количество изображений, которые мы можем обработать.
Ниже приведен пример скрипта ImageMagick, который мы используем:
convert -size 600x400 xc:none \( ".$path."assets/images/bases/base_image_69509021433289153_8_0.png -fill rgb\(255,15,127\) -colorize 100% \) -composite \( ".$path."assets/images/bases/eye_image_60444011438514404_8_0.png -fill rgb\(15,107,255\) -colorize 100% \) -composite \( ".$path."assets/images/markings/marking_clan_8_marking_10_1433289499.png -fill rgb\(255,79,79\) -colorize 100% \) -composite \( ".$path."assets/images/bases/shading_image_893252771433289153_8_0.png -fill rgb\(135,159,255\) -colorize 100% \) -compose Multiply -composite \( ".$path."assets/images/highlight_image_629750231433289153_8_0.png -fill rgb\(27,35,36\) -colorize 100% \) -compose Overlay -composite \( ".$path."assets/images/lineart_image_433715161433289153_8_0.png -fill rgb\(0,0,0\) -colorize 100% \) -compose Over -composite ".$path."assets/generated/queue/tempt_preview_27992_userid_0_".$filename."_file.png
Моя теория состоит в том, что причина, по которой это занимает довольно много времени, связана с процессом окрашивания изображений. Есть ли способ оптимизировать этот процесс вообще?
Я был бы очень признателен всем, кто имеет некоторый опыт работы с большими нагрузками процессов imagemagick или может найти несколько простых способов оптимизировать наши запросы.
Спасибо 🙂
Ваша команда на самом деле сводится к следующему:
convert -size 600x400 xc:none \
\( 1.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 2.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 3.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 4.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 5.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 6.png -fill rgb\(x,y,z\) -colorize 100% \) -composite \
result.png
Мои мысли таковы:
Точка 1:
Первый -composite
на пустой холст кажется бессмысленным — предположительно 1.png
это PNG 600×400 с прозрачностью, поэтому ваша первая строка может избежать операции компоновки и сэкономить 16% времени обработки, изменив на:
convert -background none 1.png -fill ... -colorize 100% \
\( 2.png ..
\( 3.png ...
Пункт 2
Я поместил эквивалент вашей команды в цикл и сделал 100 итераций, и это займет 15 секунд. Затем я изменил все ваши чтения файлов PNG на чтения MPC
файлы — или файлы Magick Pixel Cache. Это сократило время обработки до чуть менее 10 секунд, то есть на 33%. Magic Pixel Cache — это просто предварительно распакованный, предварительно декодированный файл, который может быть считан непосредственно в память без каких-либо усилий процессора. Вы можете предварительно создавать их всякий раз, когда ваш каталог изменяется, и хранить их вместе с файлами PNG. Чтобы сделать тот, который вы делаете
convert image.png image.mpc
и ты выйдешь image.mpc
а также image.cache
, Тогда вы просто изменили бы свой код так:
convert -size 600x400 xc:none \
\( 1.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 2.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 3.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 4.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 5.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
\( 6.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite \
result.png
Пункт 3
К сожалению, вы еще не ответили на мои вопросы, но если ваш каталог ресурсов не слишком большой, вы можете поместить его (или эквиваленты MPC выше) на RAM-диск при запуске системы.
Пункт 4
Вы должны определенно работать параллельно — это принесет наибольшую выгоду из всех. Это очень просто с GNU Parallel — пример здесь.
Если вы используете REDIS, это на самом деле проще, чем это. Просто LPUSH
ваши изображения в формате MIME в список REDIS, например:
#!/usr/bin/perl
################################################################################
# generator.pl <number of images> <image size in bytes>
# Mark Setchell
# Base64 encodes and sends "images" of specified size to REDIS
################################################################################
use strict;
use warnings FATAL => 'all';
use Redis;
use MIME::Base64;
use Time::HiRes qw(time);
my $Debug=0; # set to 1 for debug messages
my $nargs = $#ARGV + 1;
if ($nargs != 2) {
print "Usage: generator.pl <number of images> <image size in bytes>\n";
exit 1;
}
my $nimages=$ARGV[0];
my $imsize=$ARGV[1];
# Our "image"my $image="x"x$imsize;
printf "DEBUG($$): images: $nimages, size: $imsize\n" if $Debug;
# Connection to REDIS
my $redis = Redis->new;
my $start=time;
for(my $i=0;$i<$nimages;$i++){
my $encoded=encode_base64($image,'');
$redis->rpush('images'=>$encoded);
print "DEBUG($$): Sending image $i\n" if $Debug;
}
my $elapsed=time-$start;
printf "DEBUG($$): Sent $nimages images of $imsize bytes in %.3f seconds, %d images/s\n",$elapsed,int($nimages/$elapsed);
а затем запустить несколько рабочих, которые все сидят там, делая BLPOP работы, чтобы сделать
#!/usr/bin/perl
################################################################################
# worker.pl
# Mark Setchell
# Reads "images" from REDIS and uudecodes them as fast as possible
################################################################################
use strict;
use warnings FATAL => 'all';
use Redis;
use MIME::Base64;
use Time::HiRes qw(time);
my $Debug=0; # set to 1 for debug messages
my $timeout=1; # number of seconds to wait for an image
my $i=0;
# Connection to REDIS
my $redis = Redis->new;
my $start=time;
while(1){
#my $encoded=encode_base64($image,'');
my (undef,$encoded)=$redis->blpop('images',$timeout);
last if !defined $encoded;
my $image=decode_base64($encoded);
my $l=length($image);
$i++;
print "DEBUG($$): Received image:$i, $l bytes\n" if $Debug;
}
my $elapsed=time-$start-$timeout; # since we waited that long for the last one
printf "DEBUG($$): Received $i images in %.3f seconds, %d images/s\n",$elapsed,int($i/$elapsed);
Если я запускаю один генераторный процесс, как описано выше, и он генерирует 100 000 изображений по 200 КБ каждое и считывает их с 4 рабочими процессами на моем подходящем iMac, это занимает 59 секунд, или около 1700 изображений / с могут проходить через REDIS.
Очередь обрабатывается по одному? Вы пытались выполнять параллельные задания, которые будут работать параллельно, чтобы вы работали более чем с одним элементом одновременно, если это так?