2012-02-02 9 views
6

Desde el punto de vista del rendimiento: Es bien si en cada acceso a mis cursores utilizo algo como:"optimizando" el acceso a los cursores en Android: Posición vs Los nombres de columna

public static final String COLUMN_NAME = "my_column_name"; 
cursor.getString(cursor.getColumnIndex(COLUMN_NAME)); 

O debería ver una medible mejora en el rendimiento si uso esto en su lugar:

public static final int COLUMN_POSITION = #column_position; 
cursor.getString(COLUMN_POSITION); 

yo prefiero la primera aproximación ya que el resto del código no está en función de la posición de las columnas de la consulta, pero sólo en el nombre de la columna. Entonces mi pregunta es: ¿Vale la pena sacrificar esto por la "mejora del rendimiento" de acceder al cursor usando posiciones constantes? ¿Qué enfoque prefieres en tus aplicaciones de Android?

Respuesta

1

Supongo que esto no tiene nada que ver con Android. en SQLite, se supone que acceder a la columna a través del índice (posición de la columna) es más rápido.

+2

Lo sé, mi pregunta es: "significativamente" más rápido? – Sergio

+2

no sé el número exacto, pero creo que la diferencia será significativa solo si tiene 100k + columnas en una tabla. La mejor manera de determinar esto es crear un proyecto de muestra y calcular la diferencia de tiempo usando 'System.nanoTime()' –

+0

Esto pertenece a Android. Un Cursor devuelto a través de ContentProvider no requiere que esté respaldado por SQLite.De hecho, muchos no lo son. Puede ver las implementaciones de los cursores para descubrirlo usted mismo. En una vista rápida de AbstractCursor.getColumnIndex, querrá guardar en caché los valores de índice si tiene más de un par de columnas (la búsqueda es O (n) donde n es el número de columnas). – lilbyrdie

25

Para responder a su pregunta (y la mía por cierto), hice algunas pruebas.

Esta prueba fue básicamente para comprobar cuánto tiempo la consulta tuvo por esos dos casos:

  1. Utilizando el cursor.getString(cursor.getColumnIndex(COLUMN_NAME)) método
  2. Obtener el ID de columna primero y luego llamar directamente a la cursor.getString(COLUMN_POSITION) método

Para hacer que la prueba de rendimiento sea significativa, he insertado líneas en una base de datos y luego hice una consulta arrojé mi ContentProvider en esos elementos.

Resultados:

___________________________________________________________________________ 
| Column count| Time (ms) getColumnIndex | Time (ms) columnId | improvement | 
|_____________|__________________________|____________________|_____________| 
| 500   | 34564     | 30401    | 13%   | 
| 200   | 9987     | 8502    | 17%   | 
| 100   | 4713     | 4004    | 17%   | 
| 50   | 2400     | 1971    | 21%   | 
| 20   | 1088     | 915    | 19%   | 
|___________________________________________________________________________| 

lo tanto, obtener el ID de la primera columna y la llamada directamente el método getString() tomará alrededor de 20% menos tiempo.


detalles Método de ensayo:

Plataforma: Nexus 7 (2012) en Android 4.3

creación de bases de datos:

public static int TESTSPEEDCOLUMNCOUNT = 200; 
StringBuilder sb = new StringBuilder(); 
sb.append("CREATE TABLE " + Tables.TESTSPEED + " ("); 
sb.append(BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "); 
for (int i = 0; i < (TESTSPEEDCOLUMNCOUNT - 1); ++i) { 
    sb.append("C" + i + " TEXT, "); 
} 
sb.append("C" + (TESTSPEEDCOLUMNCOUNT - 1) + " TEXT)"); 
db.execSQL(sb.toString()); 

TestCase:

public class ProviderTestSpeed extends ProviderTestCase2<MyProvider> { 

    private ContentValues createElementForId(String id) { 
     ContentValues cv = new ContentValues(); 
     for (int i = 0; i < TESTSPEEDCOLUMNCOUNT; ++i) { 
      cv.put("C" + i, id); 
     } 
     return cv; 
    } 



    public void testSpeed() { 
     Log.d(TAG, "testSpeed start columnCount = " + columnCount); 
     ArrayList<ContentValues> list = new ArrayList<ContentValues>(); 
     ContentValues[] tabcv = {}; 
     for (int j = 0; j < 10; ++j) { 
      list.clear(); 
      for (int i = 0; i < 500; ++i) { 
       ContentValues cv = createElementForId(String.valueOf(i)); 
       list.add(cv); 
      } 
      mContentResolver.bulkInsert(TestSpeedCONTENT_URI, list.toArray(tabcv)); 
     } 
     Log.d(TAG, "testSpeed insertFinished"); 
     Cursor cursor = mContentResolver.query(TestSpeedCONTENT_URI, null, null, null, null); 
     cursor.moveToFirst(); 
     Log.d(TAG, "testSpeed itemCount = " + cursor.getCount() + " columnCount=" + cursor.getColumnCount()); 

     // build the tab to avoid dynamic allocation during the mesure 
     ArrayList<String> listColumns = new ArrayList<String>(); 
     for (int i = 0; i < TESTSPEEDCOLUMNCOUNT; ++i) { 
      listColumns.add("C" + i); 
     } 
     String[] tabColumnsType = {}; 
     String[] tabColumns = listColumns.toArray(tabColumnsType); 

     Date now = new Date(); 
     long start = now.getTime(); 
     do { 
      for (int i = 0; i < TESTSPEEDCOLUMNCOUNT; ++i) { 
       // get the all the columns of the table 
       cursor.getString(cursor.getColumnIndex(tabColumns[i])); 
      } 
     } while (cursor.moveToNext()); 
     now = new Date(); 
     long end = now.getTime(); 

     Log.d(TAG, "testSpeed took " + (end - start) + " with getColumnIndex at each time"); 
     cursor.moveToFirst(); 
     now = new Date(); 
     start = now.getTime(); 
     do { 
      for (int i = 0; i < TESTSPEEDCOLUMNCOUNT; ++i) { 
       // get the all the columns of the table using directly the column id 
       cursor.getString(i); 
      } 
     } while (cursor.moveToNext()); 
     now = new Date(); 
     end = now.getTime(); 
     Log.d(TAG, "testSpeed took " + (end - start) + " with getColumnIndex before loop"); 
    } 
} 

Creo que la caída de rendimiento entre 200 y 500 proviene de la ventana del cursor. Tenía muchos registros como esos por encima de 200 columnas:

W/CursorWindow(1628): Window is full: requested allocation 2412 bytes, free space 988 bytes, window size 2097152 bytes 
+1

En cualquier caso, está llamando a getString (índice). Lo más importante es que getColumnIndex en 500 columnas tarda casi 4 segundos en hacerlo. Dado el aspecto del código getColumnIndex, es fácil y vale la pena guardarlo en la memoria caché. – lilbyrdie

+1

Esta respuesta ahora está un poco desactualizada. Si observa el código fuente de 'getColumnIndex (String columnName)' en 'SQLiteCursor.java', puede ver que crean un mapa de String (nombre) en Integer (índice) en la parte superior del método. Por lo tanto, la búsqueda del nombre de columna ahora solo ocurre una vez por columna, por tabla. – k2col

Cuestiones relacionadas