improve - python optimize for loop
Acelerando Python (18)
A menudo es posible alcanzar velocidades cercanas a C (¡lo suficientemente cerca para cualquier proyecto que use Python, en primer lugar!) Reemplazando algoritmos explícitos escritos a mano en Python con un algoritmo implícito usando una llamada incorporada de 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/
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 python establecido, ¿cuáles son algunas formas decentes para acelerarlo más allá de la simple optimización en código?
En segundo lugar : al escribir un programa desde cero en Python, ¿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?
Además del (gran) psyco y el (simpático) http://code.google.com/p/shedskin/ , recomiendo probar el cython un gran tenedor de pyrex .
O, si no tiene prisa, le recomiendo que espere. Las nuevas máquinas virtuales de python están llegando, y en unladen-swallow encontrará su camino en la corriente principal.
Cython y pyrex se pueden usar para generar código c usando una sintaxis tipo pitón. Psyco también es fantástico para proyectos apropiados (a veces no notarás mucha aceleración de la velocidad, a veces será tanto como 50 veces más rápido). Todavía creo que la mejor manera es perfilar su código (cProfile, etc.) y luego simplemente codificar los cuellos de botella como funciones c para python.
Ejecute su aplicación a través del generador de perfiles de Python. Encuentre un serio cuello de botella. Reescribe ese cuello de botella en C. Repite.
En cuanto a "En segundo lugar: al escribir un programa desde cero en Python, ¿cuáles son algunas buenas maneras de mejorar en gran medida el rendimiento?"
Recuerde 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 .
No optimices sobre la marcha. Primero hazlo bien. Entonces hazlo rápido. Optimizar un programa incorrecto sigue siendo incorrecto.
Recuerde la regla 80/20.
Siempre ejecute los puntos de referencia "antes" y "después". De lo contrario, no sabrás si has encontrado el 80%.
Use los algoritmos correctos y las estructuras de datos. Esta regla debería ser lo primero. Nada importa tanto como el algoritmo y la estructura de datos.
Línea de fondo
No puede evitar o evitar el esfuerzo de "optimizar este programa". Es parte del trabajo. Tienes que planearlo y hacerlo con cuidado, al igual que el diseño, el código y las actividades de prueba.
En lugar de solo ir a C, sugeriría:
Haga que su código cuente. Haz más con menos ejecuciones de líneas:
- Cambia el algoritmo a uno más rápido. No es necesario ser elegante para ser más rápido en muchos casos.
- Usa primitivas de pitón que casualmente están escritas en C. Algunas cosas forzarán el despacho de un intérprete cuando algo no lo haga. 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 range y xrange. En general, a menudo vale la pena pensar en el uso de la memoria del programa. El uso de generadores a veces puede reducir el uso de la memoria O (n) a O (1).
- Python generalmente no optimiza. Código de invariante de polipasto fuera de bucles, eliminar subexpresiones comunes donde sea posible en bucles apretados.
- Si algo es costoso, precomputa o memoriza. Las expresiones regulares se pueden compilar, por ejemplo.
- ¿Necesitas crujir números? Es posible que desee comprobar
numpy
. - Muchos programas python son lentos porque están sujetos a E / S de disco o acceso a la base de datos. Asegúrese de tener algo que valga la pena mientras espera a que lleguen los datos en lugar de solo bloquearlos. Un arma podría ser algo así como el framework
Twisted
. - Tenga en cuenta que muchas bibliotecas cruciales de procesamiento de datos tienen versiones C, ya sea XML, JSON o cualquier otra cosa. A menudo son considerablemente más rápidos que el intérprete de Python.
Si todo lo anterior falla para el código perfilado y medido, entonces comience a pensar en la ruta C-reescritura.
Espero que hayas leído: http://wiki.python.org/moin/PythonSpeed/PerformanceTips
Reanudar lo que ya existe son generalmente 3 principios:
- escriba el código que se transforma en un mejor bytecode, como, use los locales, evite búsquedas / llamadas innecesarias, use construcciones idiomáticas (si hay sintaxis natural para lo que quiere, úselo - generalmente más rápido. Por ejemplo: no lo haga: "para ingresar la clave some_dict.keys () ", do" para la clave en some_dict ")
- todo lo que está escrito en C es considerablemente más rápido, abuse de las funciones C / módulos que tenga disponibles
- en caso de duda, tiempo de importación, perfil
Este es el procedimiento que trato de seguir:
- importar psyco; 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. (DESACTIVAR psyco para este paso!)
- Intente 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] son mucho, mucho más lentas que map (str, x) o map (str.strip, x).
- Después de esto, si todavía necesito más velocidad, en realidad es realmente fácil poner en marcha PyRex. Primero copio una sección de código python, lo puse directamente en el código pyrex y veo qué sucede. Luego juego con él hasta que se vuelve más y más rápido.
Esto no necesariamente acelerará ninguno de tus códigos, pero es un conocimiento crítico al programar en Python si quieres evitar reducir la velocidad de tu código. El "bloqueo de intérprete global" (GIL) tiene el potencial de reducir drásticamente la velocidad de su programa de subprocesos múltiples si no se entiende su comportamiento (sí, esto me mordió ... tenía una bonita máquina de 4 procesadores que no use más de 1.2 procesadores a la vez). Hay un artículo introductorio con algunos enlaces para comenzar en SmoothSpan .
La gente ha dado algunos buenos consejos, pero hay que ser consciente de que cuando se necesita un alto rendimiento, el modelo de python es: punt to c. Los esfuerzos como psyco pueden ayudar en el futuro un poco, pero Python simplemente 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 generar código muy rápido; al menos para el futuro previsible (y algunos de los trabajos de diseño contra la compilación rápida) que será el caso.
Entonces, si realmente te encuentras en este aprieto, tu mejor opción será aislar las partes de tu sistema que son inaceptables, lentas en (buena) python, y diseñar alrededor de la idea de que vas a reescribir esos bits en C. Lo siento. Un buen diseño puede ayudar a que esto sea menos doloroso. Prototipo en python primero, sin embargo, también es fácil obtener un control de cordura en su c.
Esto funciona bastante bien para cosas como numpy, después de todo. No puedo enfatizar lo suficiente cuánto te ayudará el buen diseño. Si solo pinchas iterativamente en tus bits de pitón y reemplazas los más lentos con C, es posible que termines con un gran desastre. Piense exactamente dónde se necesitan los C bits, y cómo se pueden minimizar y encapsular con sensatez.
La referencia canónica sobre cómo mejorar el código de Python está aquí: http://wiki.python.org/moin/PythonSpeed/PerformanceTips . Yo recomendaría no optimizar en C a menos que realmente lo necesite. Para la mayoría de las aplicaciones, puede obtener el rendimiento que necesita siguiendo las reglas publicadas en ese enlace.
Lo primero que viene a la mente es psyco . Se ejecuta solo en x86, por el momento.
Entonces, 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.
Dijiste aparte de la optimización en código, así que no voy a profundizar en esto, pero mantén tu mente abierta para los errores típicos ( for i in range(10000000)
viene a la mente) que las personas tienen.
Los sospechosos habituales: perfilarlo, encontrar la línea más cara, descubrir qué está haciendo, arreglarlo. Si no has hecho muchos perfiles antes, podría haber algunos bucles cuadráticos grandes y gruesos o una duplicación de cuerdas escondida detrás de expresiones que por lo demás son inocuas.
En Python, dos de las causas más comunes que he encontrado para la desaceleración no obvia son la concatenación de cadenas y generadores. Dado que las cadenas de Python son inmutables, hacer algo como esto:
result = u""
for item in my_list:
result += unicode (item)
copiará toda la cadena 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 bits en C! Python, como un lenguaje dinámico 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 a 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 lo más clara y obvia posible: cualquier error debería ser fácil de diagnosticar y corregir. Escribe tus pruebas contra este módulo. Luego escribe la versión C y pruébala. Su comportamiento debe ser en todos los casos igual al de la implementación de Python: si difieren, debería ser muy fácil determinar cuál es el problema y corregir el problema.
Me sorprende que nadie haya mencionado a ShedSkin: http://code.google.com/p/shedskin/ , convierte automágicamente su programa python a C ++ y en algunos puntos de referencia produce mejores mejoras que psyco en velocidad.
Además de historias anecdóticas sobre la simplicidad: http://pyinsci.blogspot.com/2006/12/trying-out-latest-release-of-shedskin.html
Sin embargo, hay limitaciones, consulte: http://tinyurl.com/shedskin-limitations
Para un proyecto establecido, creo que la ganancia de rendimiento principal será hacer uso de la lib interna de Python tanto como sea posible.
Algunos consejos están aquí: http://blog.hackerearth.com/faster-python-code
Si utilizo psyco, recomendaría psyco.profile()
lugar de psyco.full()
. Para un proyecto más grande, será más inteligente sobre las funciones que se optimizaron y utilizaron una tonelada menos de memoria.
También recomendaría buscar iteradores y generadores. Si su aplicación usa grandes conjuntos de datos, esto le ahorrará muchas copias de los contenedores.
Solo una nota sobre el uso de psyco: en algunos casos, en realidad puede producir tiempos de ejecución más lentos. Especialmente cuando trato de usar psyco con el código que se escribió 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.
Un par de formas de acelerar el código de Python se introdujeron después de que se hizo esta pregunta:
- Pypy tiene un compilador JIT, lo que lo hace mucho más rápido para el código de CPU.
- Pypy está escrito en Rpython , un subconjunto de Python que se compila en código nativo, aprovechando la cadena de herramientas LLVM.