sql >> Base de Datos >  >> NoSQL >> Redis

Optimización de solicitudes simultáneas de ImageMagick mediante redis/php-resque

Tu comando en realidad se reduce a esto:

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

Mis pensamientos son los siguientes:

Punto 1:

El primer -composite en un lienzo en blanco parece inútil, presumiblemente 1.png es un PNG de 600 x 400 con transparencia, por lo que su primera línea puede evitar la operación de composición y ahorrar un 16 % del tiempo de procesamiento cambiando a:

convert -background none 1.png -fill ... -colorize 100% \
   \( 2.png ..
   \( 3.png ...

Punto 2

Puse el equivalente de su comando en un ciclo e hice 100 iteraciones y tomó 15 segundos. Luego cambié todas sus lecturas de archivos PNG en lecturas de MPC archivos - o archivos Magick Pixel Cache. Eso redujo el tiempo de procesamiento a poco menos de 10 segundos, es decir, en un 33 %. Un Magic Pixel Cache es solo un archivo predescomprimido y predecodificado que se puede leer directamente en la memoria sin ningún esfuerzo de la CPU. Puede crearlos previamente cada vez que cambie su catálogo y almacenarlos junto con los archivos PNG. Para hacer uno lo haces

convert image.png image.mpc

y obtendrás image.mpc y image.cache . Entonces simplemente cambiaría su código para que se vea así:

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

Punto 3

Desafortunadamente, aún no ha respondido a mis preguntas, pero si su catálogo de activos no es demasiado grande, puede colocarlo (o los equivalentes de MPC anteriores) en un disco RAM al iniciar el sistema.

Punto 4

Definitivamente debería ejecutar en paralelo, eso producirá las mayores ganancias de todas. Es muy simple con GNU Parallel - ejemplo aquí.

Si está utilizando REDIS, en realidad es más fácil que eso. Simplemente LPUSH sus imágenes codificadas con MIME en una 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);

y luego ejecute varios trabajadores que se sientan allí haciendo BLPOP de trabajos por hacer

#!/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);

Si ejecuto un proceso generador como el anterior y hago que genere 100 000 imágenes de 200 kB cada una, y las leo con 4 procesos de trabajo en mi iMac con especificaciones razonables, demora 59 segundos, o alrededor de 1700 imágenes/s pueden pasar a través de REDIS.