2009-08-30 5 views
5

Tuve una discusión con un colega sobre escribir python de manera eficiente. Afirmó que, aunque está programando python, tiene que optimizar al máximo los pequeños bits de su software, como si estuviera escribiendo un algoritmo eficiente en C++.¿Debo optimizar mi código python como C++? ¿Importa?

Cosas como:

  • en un comunicado por if con un or siempre ponen la condición más probable que falle en primer lugar, por lo que el segundo no se comprobará.
  • Utilice las funciones más eficientes para manipular cadenas de uso común. No es un código que muele cadenas, sino cosas simples como hacer uniones y divisiones, y buscar subcadenas.
  • Llama al menor número de funciones posible, incluso si se produce a expensas de la legibilidad, debido a la sobrecarga que esto genera.

Digo, que en la mayoría de los casos no importa. También debería decir que el contexto del código es no un NOC súper eficiente o sistemas de guía de misiles. Principalmente estamos escribiendo pruebas en Python.

¿Cuál es su opinión del asunto?

+10

No sé acerca de Python, pero estas micro "optimizaciones" son una mala práctica al escribir el código C++. –

Respuesta

14

Mi respuesta a eso sería:

Debemos olvidarnos de pequeñas eficiencia, dicen que alrededor del 97% de las veces : la optimización prematura es la raíz de todo mal.

(Citando Knuth, Donald. Programación estructurada con ir a los estados, Encuestas ACM Computing Diario, Vol 6, No. 4, 1974. p.268 diciembre)


Si su aplicación está haciendo algo así como una consulta a la base de datos, esa consulta tomará más tiempo que cualquier cosa que pueda ganar con ese tipo de optimizaciones pequeñas, de todos modos ...

Y si se ejecuta después de interpretaciones así, ¿por qué no codifica en el ensamblaje? lenguaje, después de todo? Porque Python es más fácil/rápido de escribir y mantener? Bueno, si es así, tiene razón :-)

Lo más importante es que su código es fácil de mantener; ¡ni un par de microsegundos de tiempo de CPU!
Bueno, tal vez excepto si tiene miles de servidores, pero ¿es así?

+0

Funcionan en muchos servidores. Una docena como máximo, y cada prueba es autosuficiente. No hay recursos compartidos, excepto desde el repositorio desde el que se verifica el código de prueba. –

+3

Es importante destacar que esto ** también se aplica a C++. ** –

+4

Esto se aplica a casi cualquier lenguaje/aplicación de programación, de hecho (ya no codigo en C++ y nunca he programado en Python, pero todo esto es cierto para PHP , Javascript, SQL, o lo que sea que pueda pensar ^^) –

3

Debo decir también que el contexto del código no es un sistema de conducción de NOC o de misiles super eficiente. Principalmente estamos escribiendo pruebas en Python.

Teniendo en cuenta esto, yo diría que usted debe tomar el consejo de su colega sobre la escritura del pitón eficiente, pero ignorar cualquier cosa que diga que va en contra de dar prioridad a la legibilidad y mantenibilidad del código, que probablemente será más importante que la velocidad a la que ejecutará

13

La respuesta es muy simple:

  • mejores prácticas Seguimiento de Python, no mejores prácticas C++.
  • La legibilidad en Python es más importante que la velocidad.
  • Si el rendimiento se convierte en un problema, mida y luego comience a optimizar.
+6

Si la legibilidad es más importante, la velocidad depende de tus circunstancias. – EFraim

+2

La legibilidad no siempre es más importante que la velocidad, y el idioma que elija no hace esa determinación por usted. Debe seguir las mismas reglas en general en cualquier idioma: escriba un código bueno, limpio y legible utilizando los patrones aceptados de ese idioma, y ​​cuando sea demasiado lento, optimícelo. Por supuesto, no todas las optimizaciones prematuras son malas, solo la optimización prematura especulativa: si * sabes * estarás lidiando con un conjunto de datos de cierto tamaño o característica, entonces, por supuesto, elige un algoritmo que maneje ese pozo. –

+1

Estoy de acuerdo con ambos.EFraim, las circunstancias, por supuesto, son importantes, pero en este caso, Python le permite vincularse a cualquier lenguaje que sea más rápido por sí mismo. Entonces, si se dan estas circunstancias, sería aún mejor elegir C sobre Python para la parte de su sistema con cuello de botella. Python no está destinado a ser rápido. En absoluto. @Nick Bastin, de hecho, el sentido común va en tu dirección. Si conoce la mejor manera de hacerlo y no a un alto costo de mantenimiento, ¿por qué no hacerlo de la manera más rápida? Es solo que la elección de Python ya está eligiendo una filosofía de codificación que coloque el tiempo humano por encima del tiempo de CPU. –

2

En una sentencia if con una o siempre puso la condición más probable que falle en primer lugar, por lo que el segundo no se marcada.

Esto generalmente es un buen consejo, y también depende de la lógica de su programa. Si tiene sentido que la segunda declaración no se evalúe si la primera devuelve falso, hágalo. Hacer lo contrario podría ser un error de lo contrario.

Utilice las funciones más eficientes para manipulando cadenas de uso común. No código que muele cadenas, pero cosas simples como hacer uniones y splits, y encontrar subcadenas.

Realmente no entiendo este punto. Por supuesto, debe usar las funciones proporcionadas por la biblioteca, ya que probablemente estén implementadas en C, y una implementación pura de python es más probable que sea más lenta. En cualquier caso, no es necesario reinventar la rueda.

llamada como menos funciones como sea posible, incluso si se trata de la costa de facilitar la lectura, debido a la sobrecarga esto crea.

$ cat withcall.py 
def square(a): 
     return a*a 

for i in xrange(1,100000): 
     i_square = square(i) 

$ cat withoutcall.py 
for i in xrange(1,100000): 
     i_square = i*i 

$ time python2.3 withcall.py 
real 0m5.769s 
user 0m4.304s 
sys  0m0.215s 
$ time python2.3 withcall.py 
real 0m5.884s 
user 0m4.315s 
sys  0m0.206s 

$ time python2.3 withoutcall.py 
real 0m5.806s 
user 0m4.172s 
sys  0m0.209s 
$ time python2.3 withoutcall.py 
real 0m5.613s 
user 0m4.171s 
sys  0m0.216s 

quiero decir ... vamos ... por favor.

+0

Pide una prueba ANOVA, dos muestras son muy pocas, especialmente con la varianza observada. –

+1

Con un número suficiente de muestras, cualquier diferencia será significativa;) La pregunta es si realmente importa. –

10

Este tipo de micro-optimización prematura suele ser una pérdida de tiempo en mi experiencia, incluso en C y C++. Escribir código legible primero. Si se ejecuta demasiado lento, ejecútelo a través de un generador de perfiles y, si es necesario, arregle los puntos conflictivos.

Fundamentalmente, debe pensar en el retorno de la inversión. ¿Vale la pena el esfuerzo extra de leer y mantener el código "optimizado" durante los microsegundos que te ahorra? En la mayoría de los casos, no lo es.

(. Además, los compiladores y los tiempos de ejecución son cada vez más inteligentes Algunos micro-optimizaciones pueden convertirse en micro-pessimisations con el tiempo.)

+4

+1: perfil antes de jugar con "optimización". –

4

Estoy de acuerdo con los demás: código legible primero ("El rendimiento no es un problema hasta que el rendimiento es una problema.").

Solo quiero agregar que cuando necesitas absolutamente escribir un código ilegible y/o no intuitivo, generalmente puedes aislarlo en algunos métodos específicos, para lo cual puedes escribir comentarios detallados y mantener el resto de tu código altamente legible Si lo haces, terminarás teniendo un código fácil de mantener, y solo tendrás que pasar por las partes ilegibles cuando realmente lo necesites.

1

Sure siga las mejores prácticas de Python (y, de hecho, estoy de acuerdo con las dos primeras recomendaciones), pero la facilidad de mantenimiento y la eficiencia no son opuestas, son en su mayoría en conjunto (si es una palabra).

Las declaraciones como "siempre escriba sus declaraciones IF de cierta manera para el rendimiento" son a priori, es decir, no se basan en el conocimiento de lo que su programa pasa tiempo, y por lo tanto son conjeturas. La primera (o segunda, o tercera, lo que sea) regla de ajuste de rendimiento es no adivinar.

Si después de medir, el perfil, o en mi caso do this, en realidad se sabe que se puede ahorrar mucho tiempo por las pruebas de re-ordenamiento, por todos los medios, hacer. Mi dinero dice que está en el nivel de 1% o menos.

2

Creo que hay varias 'leyendas urbanas' relacionadas aquí.

  • Falso Poner la condición más marcada a menudo por primera vez en unas optimizaciones condicionales y similares de suficiente tiempo para un programa típico que es digno para un programador típico.

  • True Algunas personas, pero no muchas, están usando tales estilos en Python con la creencia incorrecta descrita anteriormente.

  • verdaderos Mucha gente utiliza tal estilo en Python cuando piensan que mejora la legibilidad de un programa Python.

Acerca de la legibilidad: Creo que de hecho es útil cuando primero se da el condicional más útil, ya que esto es lo que las personas notan primero de todos modos. También debe usar ''.join() si quiere decir concatenación de cadenas, ya que es la forma más directa de hacerlo (la operación s += xpodría significar algo diferente).

"Llamar menos funciones como sea posible" disminuye la legibilidad y va en contra del principio Pythonic de la reutilización de código. Y entonces no es un estilo que la gente usa en Python.

2

Antes de introducir optimizaciones de rendimiento a expensas de la legibilidad, busque en módulos como psyco que harán algunas compilaciones JIT-ish de distintas funciones, a menudo con resultados llamativos, sin menoscabo de la legibilidad.

Entonces, si realmente desea iniciarse en la ruta de optimización, primero debe aprender a medir y perfilar. La optimización DEBE SER CUANTITATIVA - no vaya con su instinto. El generador de perfiles de punto de acceso le mostrará las funciones en las que su programa está consumiendo la mayor cantidad de tiempo.

Si la optimización resulta una función como esta está siendo con frecuencia llamados:

def get_order_qty(ordernumber): 
    # look up order in database and return quantity 

Si hay alguna repetición de ordernumbers, entonces memoization sería una buena técnica de optimización de aprender, y es fácilmente empaquetado en una @memoize decorator para que haya poco impacto en la legibilidad del programa. El efecto de la memorización es que los valores devueltos para un conjunto dado de argumentos de entrada se almacenan en caché, de modo que la función costosa puede invocarse solo una vez, con las llamadas subsecuentes resueltas contra la caché.

Por último, considere levantar invariantes fuera de los bucles. Para grandes estructuras multidimensionales, esto puede ahorrar mucho tiempo; de hecho, en este caso, yo diría que esta optimización mejora la legibilidad de, ya que a menudo sirve para aclarar que alguna expresión puede calcularse a un alto nivel. dimensión en la lógica anidada.

(Por cierto, ¿es esto realmente lo que quería decir? • En una sentencia if con una o poner siempre la condición más probable que falle en primer lugar, por lo que el segundo no lo examinará.

que debe pensar que esto podría ser el caso para "y", pero un "o" se cortocircuitará si el primer valor es verdadero, guardando la evaluación del segundo término del condicional. Así que cambiaría esta "regla" de optimización a:

  • Si prueba "A y B", ponga A primero si es más probable que evalúe a
    F alse.
  • Si prueba "A o B", ponga A en primer lugar si es más probable que evalúe a Verdadero.

Pero a menudo, la secuencia de las condiciones es impulsado por los propios pruebas:

if obj is not None and hasattr(obj,"name") and obj.name.startswith("X"): 

No se puede reordenar estos para la optimización - que tienen ser en este orden (o simplemente dejar que el excepciones volar y atrapar más adelante:

if obj.name.startswith("X"): 
1

Mi reacción visceral es la siguiente:

He trabajado con gente como tu colega y, en general, no recibiría consejos de ellos.

Pregúntele si alguna vez ha usado un perfilador.

Cuestiones relacionadas