Я работаю над сайтом, который использует 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
– или файлы Magix 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);
а затем запустите нескольких сотрудников, которые все сидят там, выполняя BLPOPs заданий, чтобы сделать
#!/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);
в#!/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.
Очередь обрабатывается по очереди за раз? Вы пытались выполнить параллельные задания, которые будут работать параллельно, поэтому вы будете работать сразу несколько элементов, если это так?