2012-02-22 13 views
10

Actualmente estoy seleccionando una gran lista de filas de una base de datos usando pyodbc. El resultado luego se copia en una lista grande, y luego estoy tratando de iterar sobre la lista. Antes de abandonar Python, y tratar de crear esto en C#, quería saber si había algo que estaba haciendo mal.Python es lento al iterar sobre una lista grande

clientItems.execute("Select ids from largetable where year =?", year); 
allIDRows = clientItemsCursor.fetchall() #takes maybe 8 seconds. 

for clientItemrow in allIDRows: 
    aID = str(clientItemRow[0]) 
    # Do something with str -- Removed because I was trying to determine what was slow 
    count = count+1 

Algunos más información:

  • El bucle se está ejecutando actualmente en alrededor de 5 ciclos por segundo, y que parece increíblemente lento para mí.
  • El total de filas seleccionadas es ~ 489,000.
  • La máquina que se ejecuta tiene mucha RAM y CPU. Parece que solo ejecuta uno o dos núcleos, y ram es 1.72GB de 4gb.

¿Alguien puede decirme qué pasa? ¿Los scripts solo funcionan así de lentos?

Gracias

+1

¿'clientItemRow [0]' es realmente grande? 489,000 es un número bajo de filas, pero 5rows/s es ridículamente lento. Además, alguien puede corregirme si me equivoco, pero estoy bastante seguro de que tu código, tal como está escrito, solo se ejecutará en un núcleo, pero aún así debería ser millas más rápido que 5 iteraciones por segundo. Además, puedes usar el perfil c incorporado para ver dónde estás golpeando un cuello de botella. – prelic

+0

En cuanto al uso de CPU, si está haciendo cosas extravagantes con los resultados que recupera, puede obtener más núcleos de CPU en la mezcla utilizando el módulo de multiprocesamiento de Python, pero primero tengamos en cuenta esta interacción. – jsbueno

+0

¿Qué devuelve 'type (allIDRows)'? – tMC

Respuesta

17

Esto no debe ser lenta con listas nativas de Python - pero tal vez el conductor de ODBC está devolviendo un objeto "perezosa" que trata de ser inteligente, pero sólo se pone lento. Intenta simplemente haciendo

allIDRows = list(clientItemsCursor.fetchall())

en su código y post futuros valores de referencia.

(listas de Python pueden ser lento si se inicia la inserción de las cosas en su parte media, pero sólo iterar sobre una lista grande debe ser rápido)

+0

WOW. Seguí adelante e hice este cambio, y el número por segundo pasó de 1000. Esto es increíble. Aceptaremos esto como la respuesta. – nycynik

+5

Sería bueno encontrar la página web del proyecto pyodbc e introducir esto como un error. Si fetchall no devuelve una lista, debería ser mucho más eficiente que una lista, no un show stopper – jsbueno

+0

Gran respuesta, ¿me hizo pensar es una lista de la forma más rápida de almacenar datos recopilados como este? ¿Un conjunto, tupla, dict (con valor ficticio), etc. sería más rápido que una lista como esta? – Lostsoul

1

Es probable que sea lento porque se carga todo el resultado en la memoria primera y la realización de la iteración más de una lista. Intenta iterar el cursor en su lugar.

Y no, las secuencias de comandos no deberían ser que lento.

clientItemsCursor.execute("Select ids from largetable where year =?", year); 
for clientItemrow in clientItemsCursor: 
    aID = str(clientItemrow[0]) 
    count = count + 1 
+1

Eso está en la dirección opuesta a lo que sugerí, solo las pruebas lo indicarán, y espero que el O.P. nos mantenga actualizados. La iteración sobre una lista no debe ser lenta de todos modos, pero posiblemente 'para clientItemRow en cursor.fetchall(): ...' como usted sugiere es mejor. – jsbueno

+1

He probado este mentol, y no fue más rápido en absoluto. Esto coincide con la documentación de pyODBC. Todavía es muy lento para iterar de ellos. – nycynik

1

Se necesita más investigación aquí ... considerar la siguiente secuencia de comandos:

bigList = range(500000) 
doSomething = "" 
arrayList = [[x] for x in bigList] # takes a few seconds 
for x in arrayList: 
    doSomething += str(x[0]) 
    count+=1 

Esto es más o menos lo mismo que su script, menos las cosas de la base de datos, y toma unos segundos para ejecutarse en mi máquina no demasiado rápida.

0

Cuando te conectas a tu base de datos directamente (es decir, recibes un aviso de SQL), ¿cuántos segundos ejecuta esta consulta?

Cuando termina la consulta, se obtiene un mensaje como este:

NNNNN rows in set (0.01 sec) 

Por lo tanto, si ese tiempo es tan grande, y su consulta es lento como "nativo", puede ser que usted tiene que crear un índice en esa mesa.

0

Esto es lento, ya que son

  1. Conseguir que todos los resultados
  2. La asignación de la memoria y la asignación de los valores a que la memoria para crear la lista allIDRows
  3. iteración en esa lista y contando.

Si ejecutar le devuelve un cursor, utilice el cursor para su ventaja y comience a contar a medida que recupera el material y ahorra tiempo en la asignación de memoria.

clientItemsCursor.execute("Select ids from largetable where year =?", year); 
for clientItemrow in clientItemsCursor: 
    count +=1 

Otros consejos:

  • crear un índice en el año
  • uso 'select count (*) from ... para obtener el recuento para el año' esta probablemente será optimizado en el db.
  • Eliminar la línea AID si no es necesario, esto está convirtiendo el primer elemento de la fila en una cadena aunque no se use.
Cuestiones relacionadas