Rendimiento de PHP exec ()

El siguiente código PHP me devuelve un tiempo de ejecución de aproximadamente 3.5 segundos (medido varias veces y promediado):

$starttime = microtime(true); exec('/usr/local/bin/convert 1.pdf -density 200 -quality 85% 1.jpg'); $endtime = microtime(true); $time_taken = $endtime-$starttime; 

Cuando ejecuto el mismo comando sobre un terminal ssh, el tiempo de ejecución se reduce a aproximadamente 0.6 segundos (medido con el time herramienta de línea de comandos).

La versión de la biblioteca de imagemgick es

 Version: ImageMagick 6.7.0-10 2012-12-18 Q16 http://www.imagemgick.org Copyright: Copyright (C) 1999-2011 ImageMagick Studio LLC Features: OpenMP 

¿Cuál podría ser el motivo de esta diferencia de tiempo?

Una respuesta a una pregunta similar aquí en stackoverflow fue que la sobrecarga proviene de que el servidor web tenga que iniciar un subproceso / shell. ¿Podría ser esta la razón? Pensé que los hilos son leightweight y no tardan mucho en comenzar / terminar.

Antes de llamar a exec establecí el número de subprocesos utilizados por imagemgick (porque era / es un error en OpenMP ?, Referencia ) en 1 con exec('env MAGICK_THREAD_LIMIT=1'); . El tiempo de ejecución de PHP no cambia mucho, independientemente del valor que establezca para MAGICK_THREAD_LIMIT . De todos modos, no parece haber un error en OpenMP en esta versión porque el tiempo de ejecución de la ejecución de la línea de comandos está bien.

Cualquier sugerencia de cómo podría mejorar el tiempo de ejecución del comando anterior sería muy apreciada.

Muchas gracias por su ayuda.

Cuando inicia sesión en una máquina Unix, ya sea desde el teclado o desde ssh, crea una nueva instancia de shell. El shell suele ser algo así como /bin/sh o /bin/bash . El shell te permite ejecutar comandos.

Cuando usa exec() , también crea una nueva instancia de un shell. Esa instancia ejecuta los comandos que le envió y luego sale.

Cuando crea una nueva instancia de un comando de shell, tiene sus propias variables de entorno. Entonces, si haces esto:

 exec('env MAGICK_THREAD_LIMIT=1'); exec('/usr/local/bin/convert 1.pdf -density 200 -quality 85% 1.jpg'); 

A continuación, crea dos capas, y la configuración en el primer caparazón nunca llega al segundo caparazón. Para obtener la variable de entorno en el segundo shell, necesita algo como esto:

 exec('env MAGICK_THREAD_LIMIT=1; /usr/local/bin/convert 1.pdf -density 200 -quality 85% 1.jpg'); 

Ahora, si crees que el caparazón en sí puede ser el problema, porque lleva mucho tiempo crear un caparazón, pruébalo con algo que sabes que casi no lleva tiempo:

 $starttime = microtime(true); exec('echo hi'); $endtime = microtime(true); $time_taken = $endtime-$starttime; 

En ese punto, usted sabe cómo intentar y encontrar la manera de hacer que la instancia del shell sea más rápida.

¡Espero que esto ayude!

He estado progtwigndo computadoras por más de 56 años, pero esta es la primera vez que encuentro un error como este. Así que pasé casi una semana tratando de comprender la velocidad de ejecución 7 veces peor cuando ejecuté un progtwig perl desde php vía exec versus ejecutando el progtwig perl directamente en la línea de comando. Como parte de este esfuerzo, también estudié detenidamente todas las veces que se planteó este problema en la web. Esto es lo que encontré: (1) Este es un error que se informó por primera vez en 2002 y no se ha corregido en los 11 años posteriores. (2) El error está relacionado con la forma en que apache interactúa con php, por lo que ambas organizaciones pasan el dinero al otro. (3) El error es el mismo en el ejecutor, el sistema o cualquiera de las alternativas. (4) El error no depende de si el progtwig ejecutado es perl, exe o lo que sea. (5) El error es el mismo en UNIX y Windows. (6) El error no tiene nada que ver con imagemgick o con imágenes en general. Encontré el error en una configuración completamente diferente. (7) El error no tiene nada que ver con los tiempos de inicio de fork, shell, bash, lo que sea. (8) El error no se soluciona al cambiar el propietario del servicio apache. (9) No estoy seguro, pero creo que se relaciona con una sobrecarga enormemente aumentada en las subrutinas de llamadas. Cuando encontré este problema, tenía un progtwig de Perl que se ejecutaría en 40 segundos, pero a través de Exec me llevó 304 segundos. Mi última solución fue descubrir cómo optimizar mi progtwig para que se ejecutara en 0.5 segundos directamente o en 3.5 segundos a través de la ejecución. Entonces nunca resolví el problema.

Cuando llamas a exec php no crea un hilo, crea un nuevo proceso secundario. Crear un nuevo proceso es muy costoso.

Sin embargo, cuando se conecta con ssh solo está pasando un comando para ejecutar. Usted no es el propietario de ese progtwig, por lo que se ejecuta como el usuario con quien se conectó. Para exec es el usuario que ejecuta PHP.

@Philipp, ya que tiene SSH y dado que su servidor permite el acceso a exec() , supongo que también tiene acceso completo a la raíz de la máquina.

Recomendado para el procesamiento de un solo archivo

Tener acceso de root a la máquina significa que puede cambiar la configuración del límite de memoria /etc/php5/php.ini .

Incluso sin acceso directo a /etc/php5/php.ini , puede verificar si su servidor admite anular las directivas php.ini creando un nuevo archivo php.ini en su directorio de proyectos.

Incluso si las anulaciones no están permitidas, puede cambiar su configuración de memoria desde .htaccess si AllowOverride is All .

Otra forma de cambiar el límite de la memoria es establecerlo durante el tiempo de ejecución de PHP utilizando ini_set('memory_limit', 256); .

Recomendado para el procesamiento de archivos por lotes

Lo único bueno de ejecutar la conversión a través de exec() es si no planea obtener un resultado de exec() y permitir que se ejecute de forma asíncrona:

 exec('convert --your-convert-options > /dev/null 2>/dev/null &'); 

El enfoque anterior suele ser útil si intenta procesar por lotes muchos archivos, no desea esperar a que finalicen el procesamiento y no necesita confirmación con respecto a que cada uno haya sido procesado.

Notas de rendimiento

Usar el código de arriba para hacer que exec ejecute de manera async para procesar un único archivo costará más tiempo de procesador y más memoria que usar GD / Imagick dentro de PHP. El tiempo / memoria será utilizado por un proceso diferente que no afecte el proceso PHP (haciendo que los visitantes sientan que el sitio se mueve más rápido), pero el consumo de memoria existe y cuando se trata de manejar muchas conexiones que contarán.

He experimentado este problema, un comando de procesamiento gráfico que cuando se ejecutaba a través de la línea de comando tomaría unos .025 segundos tardaba aproximadamente .3 segundos cuando se llamaba a través de exec () en PHP. Tras mucha investigación, parece que la mayoría de la gente cree que esto es un problema con Apache o PHP. Luego intenté ejecutar el comando a través de un script CGI, omitiendo por completo el PHP, y obtuve el mismo resultado.

Por lo tanto, parecía que el problema debía ser apache, así que instalé lighttpd y obtuve el mismo resultado.

Después de pensar y experimentar me di cuenta de que esto debe ser un problema con la prioridad del procesador. Entonces, si desea que sus comandos se ejecuten con una velocidad similar a la línea de comando, debe ejecutarse de la siguiente manera.

exec('echo "password" | sudo -S nice -n -20 command')

TENGA EN CUENTA: Sé que habrá todo tipo de objeciones de seguridad a esto. Solo quería centrarme simplemente en la respuesta de que todo lo que tienes que hacer es agregar un buen detalle antes de tu orden.