sintaxis - perl pdf
¿Cómo puedo acelerar mi programa Perl? (12)
En realidad, se trata de dos preguntas, pero son muy similares, y para hacerlo simple, pensé que simplemente las juntaría:
En primer lugar : dado un proyecto establecido de Perl, ¿cuáles son algunas formas decentes de acelerarlo más allá de la simple optimización en código?
En segundo lugar : al escribir un programa desde cero en Perl, ¿cuáles son algunas buenas formas de mejorar el rendimiento en gran medida?
Para la primera pregunta, imagine que se le entrega un proyecto escrito decentemente y que necesita mejorar el rendimiento, pero parece que no puede obtener una gran ganancia a través de refactorización / optimización. ¿Qué harías para acelerarlo en este caso, sin volver a escribirlo en algo como C?
Por favor, manténgase alejado de las técnicas de optimización generales a menos que sean específicas de Perl .
Pregunté esto sobre Python antes, y pensé que sería bueno hacerlo para otros idiomas (estoy especialmente curioso si hay corolarios para psycho y pyrex para Perl).
Andy ya ha mencionado a Devel::NYTProf . Es impresionante. Realmente, realmente increíble. Úselo.
Si por alguna razón no puede usar Devel::NYTProf
, puede recurrir al viejo Devel::DProf , que viene de serie con Perl desde hace mucho tiempo. Si tiene funciones verdaderas (en el sentido matemático) que tardan mucho en calcular (por ejemplo, números de Fibonacci), entonces puede encontrar que Memoize proporciona alguna mejora en la velocidad.
Un gran rendimiento pobre proviene de algoritmos y estructuras de datos inapropiadas. Un buen curso de informática puede ser de gran ayuda aquí. Si tiene dos formas de hacer las cosas y desea comparar su rendimiento, el módulo de Benchmark también puede resultar útil.
Los siguientes Consejos Perl también pueden ser útiles aquí:
- Clasificando con comparaciones costosas
- Perfilado con Devel :: DProf
- Notación Big-O y complejidad algorítmica
- Búsqueda de artículos en una lista grande
- Conceptos básicos de Benchmarking
- Memoizing
Descargo de responsabilidad: escribí algunos de los recursos anteriores, por lo que puedo ser parcial con ellos.
El método más rentable podría ser considerar un hardware más rápido (=> arquitectura de hardware apropiada). No estoy hablando de CPU más rápidas, sino de discos más rápidos, redes más rápidas ... algo más rápido, en realidad, que acelera las E / S.
Experimenté esto hace muchos años, cuando movimos una aplicación basada en el análisis de XML (tecnología de punta en ese momento <g>) de un (rápido y confiable) Windows Server a una plataforma SUN dedicada, aunque algo obsoleta, con una I más rápida / O todo alrededor.
Como siempre, considere
- el rendimiento del desarrollador (cuánto tiempo lleva codificar, qué tan complejo es el problema, es el resultado mantenible),
- Rendimiento del hardware
- Rendimiento del software
y mejorar donde más (¡costo!) efectivo para el problema en cuestión ...
Esta mitad solo se relaciona con su pregunta, pero en interés de la documentación la publicaré aquí.
Una corrección de errores reciente de CentOS / Perl aumentó la velocidad de nuestra aplicación más del doble. Esto es imprescindible para cualquier persona que ejecute CentOS Perl y use las funciones de bendición / sobrecarga.
Hay muchas cosas que puede mejorar, por lo que primero debe averiguar qué es lento. Otros ya respondieron esa pregunta. También hablo de esto un poco en Mastering Perl .
Una lista incompleta de cosas en las que pensar al escribir un nuevo código:
Perfil con algo como Devel::NYTProf para ver dónde pasa la mayor parte de su tiempo en el código. A veces eso es sorprendente y fácil de solucionar. El dominio de Perl tiene muchos consejos al respecto.
Perl tiene que compilar la fuente cada vez y la compilación puede ser lenta. Tiene que encontrar todos los archivos, etc. Véase, por ejemplo, "A Timely Start" , de Jean-Louis Leroy, donde acelera todo simplemente optimizando las ubicaciones de los módulos en
@INC
. Si sus costos iniciales son costosos e inevitables, también puede consultar perls persistentes, como pperl, mod_perl, etc.Mira algunos de los módulos que usas. ¿Tienen largas cadenas de dependencias solo para hacer cosas simples? Claro, no nos gusta la reinvención, pero si la rueda que desea colocar en su automóvil también incluye tres botes, cinco cabras y una hamburguesa con queso, tal vez quiera construir su propia rueda (o encontrar una diferente) .
Las llamadas a métodos pueden ser costosas. En el conjunto de pruebas Perl :: Critic, por ejemplo, sus llamadas a
isa
ralentizan las cosas. No es algo que realmente puedas evitar en todos los casos, pero es algo a tener en cuenta. Alguien tuvo una gran cita que decía algo así como: "A nadie le importa renunciar a un factor de 2, es cuando tienes a diez personas haciéndolo que es malo". :) Perl v5.22 tiene algunas mejoras de rendimiento para esto.Si llama los mismos métodos caros una y otra vez, pero obtiene las mismas respuestas, algo como Memoize podría ser para usted. Es un proxy para la llamada al método. Si se trata realmente de una función (es decir, la misma entrada proporciona la misma salida sin efectos secundarios), no es necesario que la invoque repetidamente.
Los módulos como Apache::DBI pueden reutilizar los identificadores de la base de datos para evitar la costosa apertura de las conexiones de la base de datos. Es un código realmente simple, por lo que mirar adentro puede mostrarte cómo hacerlo incluso si no estás usando Apache.
Perl no hace optimización de recursión de cola para usted, así que no venga de Lisp pensando que va a hacer estos algoritmos recursivos súper rápidos. Puede convertirlos en soluciones iterativas fácilmente (y hablamos de eso en Intermediate Perl .
Mire sus expresiones regulares. Muchos de los cuantificadores abiertos (por ejemplo,
.*
) pueden dar lugar a muchos retrocesos. Eche un vistazo a Mastering Regular Expressions de Jeffrey Freidl para conocer todos los detalles sangrientos (y en varios idiomas). También consulte su sitio web de expresiones regulares .Sepa cómo se compila su perl. ¿Realmente necesitas enhebrar y
DDEBUGGING
? Esos son un poco lentos. Consulte la utilidad perlbench para comparar diferentes binarios de Perl.Compare su aplicación con diferentes Perls. Algunas versiones más recientes tienen aceleraciones, pero también algunas versiones anteriores pueden ser más rápidas para conjuntos de operaciones limitados. No tengo un consejo particular ya que no sé lo que estás haciendo.
Extiende el trabajo. ¿Puedes hacer algún trabajo asincrónico en otros procesos o en computadoras remotas? Permita que su programa trabaje en otras cosas a medida que alguien más descubra algunos subproblemas. Perl tiene varios módulos asíncronos y de desplazamiento de carga. Sin embargo, tenga en cuenta que el andamiaje para hacer bien esas cosas podría perder cualquier beneficio al hacerlo.
La mejor manera de hacer que su programa funcione más rápido es hacer que su programa funcione menos. Elija el algoritmo correcto para el trabajo. He visto muchas aplicaciones lentas porque eligen un algoritmo tonto en alguna área del código que se llama millones de veces. Cuando está haciendo un millón * un millón de operaciones en lugar de solo un millón de operaciones, su programa se ejecutará un millón de veces más lento. Literalmente.
Por ejemplo, aquí hay un código que vi que inserta un elemento en una lista ordenada:
while(my $new_item = <>){
push @list, $new_item;
@list = sort @list;
... use sorted list
}
el tipo es O (n log n). Una inserción en una lista ordenada es O (log n).
Arregla el algoritmo.
Las llamadas a métodos y subrutinas no son gratuitas en Perl. Son relativamente caros. Entonces, si su perfil resulta que está gastando una parte razonablemente grande del tiempo de ejecución en pequeños métodos de acceso, eso podría ser una micro-optimización que vale la pena mirar.
Sin embargo, lo que no debes hacer es reemplazar accessors como get_color () aquí:
package Car;
# sub new {...}
sub get_color {
my $self = shift;
return $self->{color};
}
package main;
#...
my $color = $car->get_color();
con accesos directos de ruptura de encapsulamiento:
my $color = $car->{color};
Uno podría pensar que esto es evidente, pero uno también ve esto hecho por todos lados. Class::XSAccessor es lo que puede hacer, utilizando Class::XSAccessor
package Car;
# sub new {...}
use Class::XSAccessor
getters => {
get_color => ''color'',
},
setters => {
set_color => ''color'',
};
Esto crea nuevos métodos get- y set_color () que se implementan en XS y, por lo tanto, aproximadamente el doble de rápido que la versión enrollada a mano. Los mutadores (es decir, "$ car-> color (''red'')") también están disponibles, al igual que los métodos encadenados.
Dependiendo de su aplicación, esto puede darle un impulso muy pequeño (pero esencialmente gratuito). No esperes más de 1-2% a menos que estés haciendo algo peculiar.
Perfile su aplicación, utilizando, por ejemplo, el perfilador mencionado anteriormente. A continuación, verá a dónde va el tiempo
Si dedica tiempo a hacer otras cosas que no sean el uso de la CPU, primero debe reducirlas: la CPU es fácil de escalar, otras no.
Algunas operaciones son particularmente lentas, he encontrado:
-
keys()
en un gran hash es muy malo Uso de
Data::Dumper
para depuración. Usar esta función en una estructura grande es muy lento. Evitalo si puedes. Hemos visto código que hace:use Data::Dumper; $debugstr = Dumper(/%bighash); if ($debugflag_mostlyoff) { log($debugstr); }
La mayoría de los módulos tienen alternativas con diferentes características de rendimiento: algunos literalmente chupan increíblemente mal.
- Algunas expresiones regulares pueden ser muy lentas (muchas. *, Etc.) y pueden reemplazarse por otras equivalentes que son más rápidas. Las expresiones regulares son bastante sencillas para la prueba unitaria y la prueba de rendimiento (basta con escribir un programa que lo ejecute en un ciclo contra un gran conjunto de datos simulados). Las mejores expresiones regulares comienzan con algo que se puede probar muy rápidamente, como una cadena literal. A veces es mejor no buscar primero lo que está buscando y hacer una "mirada atrás" para comprobar si realmente es lo que está buscando. La optimización de las expresiones regulares realmente es un poco de arte negro en el que no soy muy bueno.
No considere reescribir algo en C, excepto como último recurso. Llamar a C desde Perl (o viceversa) tiene una sobrecarga relativamente grande. Si puede obtener una implementación rápida de Perl, eso es mejor.
Si reescribe algo en C, intente hacerlo de una manera que minimice la sobrecarga de llamadas y las llamadas al tiempo de ejecución de Perl (las funciones SV *, por ejemplo, copian cadenas en su mayoría). Una forma de lograr esto es hacer una función C que haga más y llamarla menos veces. Copiar cadenas en la memoria no es genial.
Por otro lado, reescribir algo en C conlleva un gran riesgo porque puede introducir nuevos modos de falla, por ejemplo, fugas de memoria, bloqueos, problemas de seguridad.
Por favor, recuerda las reglas del Club de Optimización:
- La primera regla del Club de optimización es que no se optimiza.
- La segunda regla de Optimization Club es que no se optimiza sin medir.
- Si su aplicación se ejecuta más rápido que el protocolo de transporte subyacente, la optimización ha terminado.
- Un factor a la vez.
- Sin marketroids, sin horarios marketroides.
- Las pruebas continuarán tanto como sea necesario.
- Si esta es su primera noche en el Optimization Club, debe escribir un caso de prueba.
Entonces, suponiendo que realmente tenga código de trabajo, ejecute su programa bajo Devel::NYTProf .
Encuentra los cuellos de botella. Luego vuelve aquí para decirnos qué son.
Si no tiene código de trabajo, hágalo funcionar primero. La única optimización más grande que hará será pasar de no trabajar a funcionar.
Si su código necesita aceleración, es probable que su suite de pruebas también lo haga. Esta charla toca los puntos clave:
Sin tener que volver a escribir fragmentos grandes, puede usar Inline::C para convertir cualquier subrutina lenta y sencilla a C. O directamente usar XS. También es posible convertir incrementalmente subs con XS. PPI/PPI::XS hace esto, por ejemplo.
Pero cambiar de idioma es siempre el último recurso. ¿Tal vez deberías conseguir un programador Perl experto para mirar tu código? Lo más probable es que detecte alguna peculiaridad que perjudica seriamente su rendimiento. Aparte de eso, perfila tu código. Recuerde, no hay una bala de plata.
Con respecto a psyco y pyrex: No, no hay equivalente para Perl.
Un ensayo que vale la pena leer sobre el tema es la charla de Nicholas Clark cuando Perl no es lo suficientemente rápido (PDF). Algunos de los puntos son ligeramente anticuados, como la referencia a Devel :: DProf, pero tenga en cuenta que fue escrito en 2002.
Sin embargo, gran parte del material cubierto sigue siendo relevante.
Vuelca Perl y usa Golang. Cambié mi programa para usar Go y aceleró el tiempo de ejecución 34 veces. Este es el tiempo de ejecución de Perl.
0m16.724s reales
Este es el tiempo de ir.
0m0.425s reales