performance perl mod-perl

performance - ¿Cómo puedo ejecutar código ineficiente solo en tiempo de compilación cuando uso mod_perl?



mod-perl (6)

He estado evaluando el rendimiento de un marco que estoy escribiendo en Perl y recibo una disminución del 50% en las solicitudes por segundo sobre nuestra base de código existente (algunos aciertos son comprensibles, porque pasamos de un código de espagueti de procedimiento a un Marco OOP MVC).

La aplicación se está ejecutando bajo mod_perl, y he agregado Moose y todo mi código de framework en el script startup.pl , que duplicó mis solicitudes por segundo. Estoy buscando mejorar aún más este número para acercarlo lo más posible a la cantidad existente. El argumento es que esta es una optimización prematura, pero hay un par de ineficiencias flagrantes que me gustaría solucionar y ver cómo afecta el rendimiento.

Al igual que la mayoría de los marcos, tengo un archivo de configuración y un despachador. La parte de configuración es manejada por Config :: General , por lo que se necesita un poco de IO y análisis para obtener mi archivo de configuración cargado en la aplicación. ¡El problema más grande que veo aquí es que estoy haciendo esto por CADA PETICIÓN que entra!

La ejecución de Devel :: Dprof en mi aplicación apunta a Config :: General :: BEGIN y un conjunto de módulos de IO relacionados como uno de los principales puntos lentos que no es Moose. Entonces, lo que me gustaría hacer, y lo que tiene más sentido en retrospectiva es aprovechar la persistencia de mod_perl y las cosas de compilación de startup.pl para hacer solo el trabajo de cargar en el archivo de configuración una vez, cuando el servidor se inicie.

El problema es que no estoy muy familiarizado con cómo funcionaría esto.

Actualmente, cada proyecto tiene una clase PerlHandler bootstrapping que es bastante delgada y se ve así:

use MyApp; MyApp->new(config_file => ''/path/to/site.config'')->run();

MyApp.pm hereda del módulo de proyecto de framework, que tiene este código:

my $config = Config::General->new( -ConfigFile => $self->config_file, -InterPolateVars => 1, ); $self->config({$config->getall});

Para hacer esto solo en tiempo de compilación, tanto mi bootstrap como los módulos base del Proyecto tendrán que cambiar (creo), pero no estoy seguro de qué cambios realizar y aún así mantener el código agradable y sin problemas. Puede alguien señalarme la dirección correcta?

ACTUALIZAR

Probé BEGIN BLOCK en cada enfoque de módulo de proyecto como se describe por ysth en su respuesta. Entonces ahora tengo:

package MyApp::bootstrap; use MyApp; my $config; BEGIN { $config = {Config::General->new(...)->getall}; } sub handler { ..etc. MyApp->new(config => $config)->run();

Este cambio rápido por sí solo me dio un aumento del 50% en las solicitudes por segundo, confirmando mis pensamientos de que el archivo de configuración era un importante obstáculo que valía la pena solucionar. La cifra de referencia en nuestra vieja máquina dev de crotchety es 60rps, y mi marco ha pasado de 30rps a 45rps con este cambio solo. Para aquellos que dicen que Moose es lento y tiene un tiempo de compilación alcanzado ... Obtuve el mismo (50%) aumento cuando compilé todo mi código Moose en la puesta en marcha como lo hice al precompilar mi archivo de configuración.

El único problema que tengo ahora es que esto viola el principio DRY ya que el mismo código Config :: General-> new está en cada bloque BEGIN con solo la ruta al archivo de configuración que difiere. Tengo algunas estrategias diferentes para limitar esto, pero solo quería publicar los resultados de este cambio.


Suponiendo que sus aplicaciones no cambian la configuración en absoluto, muévala a un bloque de inicio:

# this code goes at file scope my $config; BEGIN { $config = { Config::General->new( ... )->getall } } # when creating a new instance $self->config( $config );

Y asegúrese de que todos sus módulos estén compilados en startup.pl.

Podrías ser más elegante y tener una clase singleton que proporcione el hash de configuración, pero no es necesario.


JackM tiene la idea correcta.

Al cargar todas sus clases y crear instancias de sus objetos de nivel de Aplicación (en su caso, la configuración) en el proceso Apache " Madre ", no tiene que compilarlos cada vez que un nuevo trabajador genera, ya que ya están disponibles y en memoria Los muy meticulosos entre nosotros agregan una línea de "uso" para cada módulo que su aplicación usa regularmente. Si no carga sus paquetes y módulos en el buque nodriza, cada trabajador no solo aprovechará la carga de rendimiento de cargar los módulos, sino que no obtendrá el beneficio de compartir la memoria que ofrecen los sistemas operativos modernos.

En realidad, es la otra mitad de la diferencia entre mod_perl y CGI. La primera mitad es el perl-engine persistente de mod_perl frente al perl de reaparición de CGI para cada invocación.


Tuve los mismos problemas en una instalación de HTML :: Mason framework, y me pareció que funcionaba bastante bien: en httpd.conf:

PerlRequire handler.pl <FilesMatch "/.mhtml$"> SetHandler perl-script PerlHandler YourModule::Mason </FilesMatch>

En su archivo handler.pl, usted define todos sus elementos estáticos como su configuración, identificadores de bases de datos, etc. Esto los define en el ámbito de YourModule :: Mason que se compila cuando se inicia el hilo apache (los nuevos hilos obviamente tendrán un efecto inherente). gastos generales). YourModule :: Mason luego tiene un método de handler que maneja la solicitud.

Debo admitir que puede haber algo de magia que esté sucediendo en HTML :: Mason que me está ayudando con esto, pero me funciona, ¿quizás para ti?


Una forma común de acelerar tales cosas con pocos cambios es simplemente usar variables globales y estado de caché en ellas entre las invocaciones del mismo proceso de Apache:

use vars qw ($config); # ... $config = Config::General->new( ... )->getall unless blessed($config); # add more suitable test here

No es muy limpio y puede llevar a errores desconocidos (aunque "mi $ var" lleva a más en mi experiencia) y a veces consume mucha memoria, pero muchas declaraciones de inicialización costosas (repetidas) se pueden evitar de esta manera. La ventaja sobre el uso de BEGIN {}; solo el código es que puede volver a inicializarse en función de otros eventos sin necesidad de reiniciar Apache o matar su proceso (por ejemplo, incluyendo la marca de tiempo de un archivo en el disco en la prueba anterior).

Sin embargo, ten cuidado con las trampas: una manera fácil de entrar


Si puedes hacer que tus clases de Moose sean inmutables , eso podría darte otro golpe de velocidad.


El sub de importación de un módulo se ejecuta en tiempo de compilación, por lo que podríamos usarlo para reducir / eliminar el DRY de la respuesta de ysth .

En el siguiente ejemplo, utilizamos un método de importación para leer el archivo de configuración con los argumentos que se nos proporcionan y luego enviar esa configuración al paquete de llamadas.

La advertencia de que cualquier variable $config en el paquete de llamada va a quedar anulada por esto.

package Foo_Config; use English qw(-no_match_vars); sub import { my ($self, @cfg) = @ARG; my $call_pkg = caller; my $config = {Config::General->new(@cfg)->getall}; do{ # this will create the $config variable in the calling package. no strict ''refs''; ${$call_pkg . ''::config''} = $config; }; return; } package MyApp; # will execute Foo_Config->import(''/path/to/site.config'') at compile time. use Foo_Config ''/path/to/site.config'';