2008-10-07 20 views
27

Esto es realmente dos preguntas, pero son tan similares, y para que sea sencillo, pensé que acababa de rodar juntos:¿Cómo puedo acelerar mi programa Perl?

  • En primer lugar: Dado un proyecto de Perl establecida, ¿cuáles son algunas maneras decentes para 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 maneras de mejorar el rendimiento?

Para la primera pregunta, imaginemos que se entregan por escrito un proyecto decentemente y que necesita para mejorar el rendimiento, pero parece que no puede conseguir mucho de una 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?

Evite las técnicas de optimización general a menos que sean Perl specific.

me preguntó acerca de este Python antes, y pensé que sería bueno que lo haga por otros idiomas (Estoy especialmente ansioso por ver si hay corolarios de psycho y pyrex para Perl).

Respuesta

149

Por favor, recuerde las reglas de la optimización del Club:

  1. La primera regla de la optimización del club es, que no optimizan.
  2. La segunda regla de Optimization Club es que no se optimiza sin medir.
  3. Si su aplicación se ejecuta más rápido que el protocolo de transporte subyacente, la optimización ha terminado.
  4. Un factor a la vez.
  5. No marketroids, no marketroid horarios.
  6. Las pruebas continuarán tanto como sea necesario.
  7. Si esta es su primera noche en el Optimization Club, tiene que escribir un caso de prueba.

Entonces, suponiendo que realmente tenga un código de trabajo, ejecute su programa bajo Devel::NYTProf.

Encuentra los cuellos de botella. Luego vuelve aquí para decirnos qué son.

Si no tienen código de trabajo, hágalo funcionar primero. La única optimización más grande que hará será pasar de no trabajar a funcionar.

+19

Mejor. Respuesta. Nunca. – pjf

+7

Edición sugerida: después de "Entonces, suponiendo ..." agregue "Si no tiene un código de trabajo, hágalo funcionar primero. La optimización más grande que hará será pasar de no trabajar a funcionar". –

+0

Hecho, gracias, Sherm. –

9

Esta mitad solo se relaciona con su pregunta, pero en interés de la documentación la publicaré aquí.

Un reciente CentOS/Perl bugfix 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.

+0

Oh, buen punto. Para obtener más información sobre el error y un programa de prueba para ver si afecta a su código, consulte http://perlbuzz.com/2008/08/red-hats-patch-slows-down-overloading-in-perl.html –

32

Andy ya ha mencionado Devel::NYTProf. Es impresionante. Realmente, realmente increíble. Úselo.

Si por alguna razón no puede usar Devel::NYTProf, puede recurrir al antiguo Devel::DProf, que viene de serie con Perl desde hace mucho tiempo. Si tiene funciones verdaderas (en el sentido matemático) que toman mucho tiempo para calcular (por ejemplo, números de Fibonacci), entonces puede encontrar que Memoize proporciona alguna mejora en la velocidad.

Mucho mal rendimiento 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 Benchmark también puede resultar útil.

La siguiente Perl Tips también puede resultar útil aquí:

Descargo de responsabilidad: escribí algunos de los recursos anteriores, por lo que puedo ser parcial con ellos.

14

Sin tener que reescribir grandes fragmentos, puede usar Inline::C para convertir cualquier subrutina lenta y sencilla en 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.

28

Hay muchas cosas que puede mejorar, por lo que primero debe averiguar qué es lento. Otros ya respondieron esa pregunta. También hablo un poco de esto en Mastering Perl.

Una lista incompleta de las cosas en que pensar que usted está escribiendo nuevo código:

  • perfil con algo como Devel::NYTProf para ver dónde está gastando la mayor parte de su tiempo en el código. A veces eso es sorprendente y fácil de solucionar. Mastering 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. Ver, por ejemplo, "A Timely Start", por 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.

  • Mire algunos de los módulos que utiliza. ¿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 reducen la velocidad. 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 costosos 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 pase de Lisp pensando que va a hacer estos algoritmos recursivos súper rápidos. Puede activar los en soluciones iterativas fácilmente (y se habla de eso en Intermediate Perl.

  • Mire sus expresiones regulares. Muchos de los cuantificadores abiertas (por ejemplo .*) puede conducir a una gran cantidad de retroceso. Salida de Mastering Regular Expressions para todos Jeffrey Freidl los detalles morbosos (y la mayoría de los idiomas). también puedes ver his regex website.

  • saber cómo se compila el perl. ¿usted realmente necesita roscado y DDEBUGGING? Aquellos reducir la velocidad un poco. Echa un vistazo a 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.

  • Extienda 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.

9

Perfile su aplicación utilizando, por ejemplo, el generador de perfiles mencionado anteriormente. A continuación, verá 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 lento, he encontrado:

  • keys() a gran hash es muy mala
  • El uso de Data::Dumper de 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); } 
    
  • mayoría de los módulos tienen alternativas con diferentes características de rendimiento - algunos, literalmente, chupar muy 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 (por ejemplo, las funciones SV * copian principalmente cadenas). 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, p. pérdidas de memoria, bloqueos, problemas de seguridad.

9

Un ensayo que vale la pena leer sobre el tema es la charla de Nicholas Clark When perl is not quite fast enough (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.

6

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 debe no hacer es sustituir descriptores de acceso tales 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 encapsulación-rotura:

my $color = $car->{color}; 

Uno podría pensar que esto va sin decir, pero uno también ve esto hecho por todos lados. Esto 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 y Get-set_color() que se implementan en XS y por lo tanto dos veces más rápido que la versión enrollados a mano. Los mutantes (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.

1

Si su código necesita aceleración, es probable que su suite de pruebas también lo haga.Esta charla toca en los puntos clave: método eficaz

Turbo Charged Test Suites

2

El costo podría ser mayor, para considerar un hardware más rápido (=> arquitectura de hardware apropiado). 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 análisis XML (tecnología de punta en ese momento < g>) de un servidor de Windows (rápido y confiable) a un SUN dedicado, aunque un tanto obsoleto plataforma con E/S más rápida por todas partes.

Como siempre, considere

  • rendimiento desarrollador (cuánto tiempo se tarda en código, lo complejo que es el problema, es el resultado de mantener),
  • rendimiento del hardware,
  • el rendimiento del software

y mejorar donde la mayoría (¡costo!) Efectivo para el problema en cuestión ...

+0

Me gusta esta idea, pero debería decirse que a veces gastar un poco más inicial en el tiempo del desarrollador puede conducir a mucho menos en el largo plazo en términos de costo/aceleración del hardware. Realmente depende de la duración del proyecto. –

6

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 vi que inserta un elemento en una lista ordenada:

while(my $new_item = <>){ 
    push @list, $new_item; 
    @list = sort @list; 
    ... use sorted list 
} 

especie es O (n log n). Una inserción en una lista ordenada es O (log n).

Solucione el algoritmo.

+0

La afirmación final de que la inserción en una lista ordenada es O (log n) es inexacta a menos que se refiera a "lista", por ejemplo. "árbol" – DavidO

0

Dump 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 momento en Ir.

real 0m0.425s

Cuestiones relacionadas