Acelerando Python

Estas son realmente dos preguntas, pero son muy similares, y para ser más simples, me di cuenta de que simplemente las juntaría:

Para la primera pregunta, imagine que se le entrega un proyecto escrito decentemente y necesita mejorar el rendimiento, pero parece que no puede obtener una gran ganancia a través de la refactorización / optimización. ¿Qué harías para acelerarlo en este caso antes de reescribirlo en algo como C?

Respecto a “En segundo lugar: al escribir un progtwig desde cero en python, ¿cuáles son algunas buenas maneras de mejorar considerablemente el rendimiento?”

Recuerda las reglas de optimización de Jackson:

  • Regla 1: No lo hagas.
  • Regla 2 (solo para expertos): No lo hagas todavía.

Y la regla de Knuth:

  • “La optimización temprana es la raíz de todo mal.”

Las reglas más útiles se encuentran en las Reglas generales para la optimización .

  1. No optimices a medida que avanzas. Primero hazlo bien. Entonces hazlo rápido. La optimización de un progtwig incorrecto sigue siendo incorrecta.

  2. Recuerda la regla 80/20.

  3. Ejecute siempre los puntos de referencia “antes” y “después”. De lo contrario, no sabrás si has encontrado el 80%.

  4. Utilice los algoritmos y estructuras de datos correctos. Esta regla debe ser la primera. Nada importa tanto como el algoritmo y la estructura de datos.

Línea de fondo

No se puede evitar o evitar el esfuerzo de “optimizar este progtwig”. Es parte del trabajo. Debe planificarlo y hacerlo con cuidado, al igual que el diseño, el código y las actividades de prueba.

En lugar de solo castigar a C, sugeriría:

Haz que tu código cuente. Hacer más con menos ejecuciones de líneas:

  • Cambia el algoritmo a uno más rápido. No es necesario ser lujoso para ser más rápido en muchos casos.
  • Use primitivas de python que están escritas en C. Algunas cosas forzarán el envío de un intérprete donde otras no. Este último es preferible.
  • Tenga cuidado con el código que primero construye una estructura de big data seguida de su consumo. Piensa en la diferencia entre rango y rango. En general, a menudo vale la pena pensar en el uso de memoria del progtwig. El uso de generadores a veces puede llevar el uso de memoria O (n) a O (1).
  • Python es generalmente no optimizante. Levante el código invariante de los bucles, elimine las subexpresiones comunes cuando sea posible en bucles ajustados.
  • Si algo es caro, precomputarlo o memorizarlo. Las expresiones regulares se pueden comstackr por ejemplo.
  • ¿Necesitas hacer números? Es posible que desee comprobar numpy out.
  • Muchos progtwigs de Python son lentos porque están limitados por E / S de disco o acceso a la base de datos. Asegúrese de tener algo que valga la pena hacer mientras espera que lleguen los datos en lugar de solo bloquearlos. Un arma podría ser algo así como el armazón Twisted .
  • Tenga en cuenta que muchas bibliotecas de procesamiento de datos cruciales tienen versiones C, ya sea XML, JSON o lo que sea. A menudo son considerablemente más rápidos que el intérprete de Python.

Si todo lo anterior falla para el código medido y con perfil, entonces comience a pensar en la ruta de C-reescritura.

Los sospechosos habituales: perfílelo, encuentre la línea más cara, averigüe qué está haciendo, arréglela. Si no has hecho muchos perfiles antes, podría haber algunos grandes bucles cuadráticos gruesos o duplicaciones de cadenas que se esconden detrás de expresiones de apariencia inocua.

En Python, dos de las causas más comunes que he encontrado para una desaceleración no obvia son la concatenación de cadenas y los generadores. Dado que las cuerdas de Python son inmutables, hacer algo como esto:

 result = u"" for item in my_list: result += unicode (item) 

Copiará la cadena completa dos veces por iteración. Esto ha sido bien cubierto y la solución es usar "".join :

 result = "".join (unicode (item) for item in my_list) 

Los generadores son otro culpable. Son muy fáciles de usar y pueden simplificar algunas tareas enormemente, pero un generador mal aplicado será mucho más lento que simplemente agregar elementos a una lista y devolver la lista.

Finalmente, ¡no tengas miedo de reescribir los bits en C! Python, como un lenguaje dynamic de alto nivel, simplemente no es capaz de igualar la velocidad de C. Si hay una función que no puede optimizar más en Python, considere extraerla en un módulo de extensión.

Mi técnica favorita para esto es mantener las versiones Python y C de un módulo. La versión de Python está escrita para ser tan clara y obvia como sea posible: cualquier error debe ser fácil de diagnosticar y corregir. Escribe tus pruebas contra este módulo. Luego escribe la versión C, y prueba. En todos los casos, su comportamiento debería ser igual al de la implementación de Python: si difieren, debería ser muy fácil averiguar cuál es el problema y corregir el problema.

Lo primero que me viene a la mente: el psico . Se ejecuta solo en x86, por el momento.

Entonces, la unión constante . Es decir, hacer que todas las referencias globales (y global.attr , global.attr.attr …) sean nombres locales dentro de funciones y métodos. Esto no siempre es exitoso, pero en general funciona. Se puede hacer a mano, pero obviamente es tedioso.

Usted dijo que aparte de la optimización en el código, así que no voy a profundizar en esto, pero mantén tu mente abierta a los errores típicos ( for i in range(10000000) viene a la mente) que la gente comete.

Cython y pyrex se pueden usar para generar código c usando una syntax similar a python. Psyco también es fantástico para los proyectos apropiados (a veces no notará un gran aumento de velocidad, a veces será hasta 50 veces más rápido). Todavía considero que la mejor manera es perfilar su código (cProfile, etc.) y luego codificar los cuellos de botella como funciones c para python.

Me sorprende que nadie mencione ShedSkin: http://code.google.com/p/shedskin/ , convierte automáticamente su progtwig de Python a C ++ y, en algunos puntos de referencia, produce mejores mejoras que el psyco en velocidad.

Más historias anecdóticas sobre la simplicidad: http://pyinsci.blogspot.com/2006/12/trying-out-latest-release-of-shedskin.html

Sin embargo, existen limitaciones, consulte: http://tinyurl.com/shedskin-limitations

Espero que hayas leído: http://wiki.python.org/moin/PythonSpeed/PerformanceTips

Resumiendo lo que ya hay habitualmente hay 3 principios:

  • escriba código que se transforme en un mejor código de bytes, por ejemplo, use locales, evite búsquedas / llamadas innecesarias, use construcciones idiomáticas (si hay una syntax natural para lo que desea, úsela, por lo general, más rápido, por ejemplo: no hacer: some_dict.keys () “, do” para la clave en some_dict “)
  • todo lo que está escrito en C es considerablemente más rápido, utilice las funciones / módulos C que tenga disponibles
  • En caso de duda, importar timeit, perfil.

Ejecute su aplicación a través del generador de perfiles de Python. Encuentra un cuello de botella serio. Reescriba ese cuello de botella en C. Repita.

La gente ha dado algunos buenos consejos, pero hay que tener en cuenta que cuando se necesita un alto rendimiento, el modelo de python es: punt to c. Esfuerzos como psyco pueden ayudar en el futuro, pero python no es un lenguaje rápido, y no está diseñado para serlo. Muy pocos idiomas tienen la capacidad de hacer las cosas dinámicas realmente bien y aún así generar código muy rápido; al menos en el futuro previsible (y algunos de los diseños funcionan contra una comstackción rápida) que será el caso.

Entonces, si realmente te encuentras en este enlace, lo mejor será aislar las partes de tu sistema que son inaceptables y lentas en Python (bueno), y diseñar alrededor de la idea de que reescribirás esos bits en C. Lo siento. Un buen diseño puede ayudar a que esto sea menos doloroso. Sin embargo, primero prototipo en python, luego también tienes un control de cordura en tu c.

Esto funciona lo suficientemente bien para cosas como adormecer, después de todo. Sin embargo, no puedo enfatizar lo suficiente el buen diseño que te ayudará. Si solo pincha iterativamente los bits de Python y reemplaza los más lentos con C, puede terminar con un gran lío. Piense exactamente dónde se necesitan los bits C, y cómo se pueden minimizar y encapsular con sensatez.

Esto no necesariamente acelerará ninguno de su código, pero es un conocimiento crítico al progtwigr en Python si desea evitar que su código sea más lento. El “Bloqueo global de intérpretes” (GIL), tiene el potencial de reducir drásticamente la velocidad de su progtwig de subprocesos múltiples si no se comprende su comportamiento (sí, este bit me … Tenía una buena máquina de 4 procesadores que no lo haría. utilizar más de 1.2 procesadores a la vez). Hay un artículo introductorio con algunos enlaces para comenzar en SmoothSpan .

Solo una nota sobre el uso de psyco: en algunos casos puede producir tiempos de ejecución más lentos. Especialmente cuando intenté usar psyco con el código que estaba escrito en C. No recuerdo el artículo que leí, pero las funciones map() y reduce() se mencionaron específicamente. Afortunadamente, puede decirle a psyco que no maneje funciones y / o módulos específicos.

Este es el procedimiento que trato de seguir:

  • importación psico; psyco.full ()
  • Si no es lo suficientemente rápido, ejecute el código a través de un generador de perfiles, vea dónde están los cuellos de botella. (¡DESACTIVA a la psico para este paso!)
  • Trate de hacer cosas como otras personas han mencionado para obtener el código en esos cuellos de botella lo más rápido posible.
    • Cosas como [str (x) para x en l] o [x.strip () para x en l] es mucho, mucho más lento que map (str, x) o map (str.strip, x).
  • Después de esto, si todavía necesito más velocidad, es realmente fácil poner en funcionamiento PyRex. Primero copio una sección del código de Python, la coloco directamente en el código de Pyrex y veo qué sucede. Luego lo hago con más velocidad hasta que se vuelve más y más rápido.

A menudo es posible alcanzar velocidades cercanas a C (¡lo suficientemente cerca para cualquier proyecto que use Python en primer lugar!) Reemplazando los algoritmos explícitos escritos a largo plazo en Python con un algoritmo implícito que usa una llamada incorporada en Python. Esto funciona porque la mayoría de las incorporaciones de Python están escritas en C de todos modos. Bueno, en CPython, por supuesto 😉 https://www.python.org/doc/essays/list2str/

La referencia canónica sobre cómo mejorar el código de Python está aquí: PerformanceTips . Yo recomendaría no optimizar en C a menos que realmente lo necesites. Para la mayoría de las aplicaciones, puede obtener el rendimiento que necesita siguiendo las reglas publicadas en ese enlace.

Si utilizo psyco, recomendaría psyco.profile() lugar de psyco.full() . Para un proyecto más grande, será más inteligente acerca de las funciones que se optimizaron y usarían una tonelada menos de memoria.

También recomendaría mirar iteradores y generadores. Si su aplicación utiliza grandes conjuntos de datos, esto le ahorrará muchas copias de contenedores.

Además de la (gran) psyco y la (bonita) piel de gallo , recomiendo probar cython un gran tenedor de pyrex .

O, si no tienes prisa, te recomiendo que esperes. Las nuevas máquinas virtuales de Python están llegando, y Unladen-Swallow encontrará su camino hacia la stream principal.

Un par de formas para acelerar el código de Python se introdujeron después de esta pregunta:

  • Pypy tiene un comstackdor JIT, que lo hace mucho más rápido para el código enlazado a la CPU.
  • Pypy está escrito en Rpython , un subconjunto de Python que se comstack en código nativo, aprovechando la cadena de herramientas LLVM.

Para un proyecto establecido, siento que la ganancia de rendimiento principal será el uso de lib interna python tanto como sea posible.

Algunos consejos están aquí: http://blog.hackerearth.com/faster-python-code