Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

Otimizando solicitações simultâneas do ImageMagick usando redis/php-resque


Seu comando realmente se resume a isso:
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

Meus pensamentos são os seguintes:

Ponto 1:

O primeiro -composite em uma tela em branco parece inútil - presumivelmente 1.png é um PNG de 600x400 com transparência, então sua primeira linha pode evitar a operação de composição e economizar 16% do tempo de processamento alterando para:
convert -background none 1.png -fill ... -colorize 100% \
   \( 2.png ..
   \( 3.png ...

Ponto 2

Eu coloquei o equivalente do seu comando em um loop e fiz 100 iterações e leva 15 segundos. Em seguida, alterei todas as suas leituras de arquivos PNG para leituras de MPC arquivos - ou arquivos Magick Pixel Cache. Isso reduziu o tempo de processamento para pouco menos de 10 segundos, ou seja, em 33%. Um Magic Pixel Cache é apenas um arquivo pré-descompactado e pré-decodificado que pode ser lido diretamente na memória sem nenhum esforço da CPU. Você pode pré-criá-los sempre que seu catálogo mudar e armazená-los junto com os arquivos PNG. Para fazer um você faz
convert image.png image.mpc

e você obterá image.mpc e image.cache . Então você simplesmente mudaria seu código para ficar assim:
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

Ponto 3

Infelizmente, você ainda não respondeu minhas perguntas, mas se o seu catálogo de ativos não for muito grande, você pode colocá-lo (ou os equivalentes MPC acima) em um disco RAM na inicialização do sistema.

Ponto 4

Você definitivamente deve correr em paralelo - isso renderá os maiores ganhos de todos. É muito simples com o GNU Parallel - exemplo aqui.

Se você estiver usando o REDIS, é realmente mais fácil do que isso. Apenas LPUSH suas imagens codificadas em MIME em uma lista REDIS como esta:
#!/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);

e, em seguida, execute vários trabalhadores que ficam sentados lá fazendo BLPOPs de trabalhos a fazer
#!/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);

Se eu executar um processo gerador como acima e gerar 100.000 imagens de 200kB cada, e lê-las com 4 processos de trabalho no meu iMac de especificação razoável, levará 59 segundos, ou cerca de 1.700 imagens/s podem passar pelo REDIS.