2012-02-02 13 views
6

Z punktu widzenia wydajności: Czy to w porządku, jeśli w każdym dostępu do moich kursorów używać coś takiego:„Optymalizacja” dostęp do kursorów w Androidzie: Położenie vs nazwy kolumn

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

Albo powinienem zobaczyć mierzalne poprawa wydajności, jeśli mogę użyć tego zamiast:

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

wolę pierwsze podejście, ponieważ reszta kodu nie jest w zależności od położenia kolumn w zapytaniu, ale tylko na imię kolumna. Więc moje pytanie brzmi: czy warto poświęcić to dla "poprawy wydajności" dostępu do kursora przy użyciu stałych pozycji zamiast?. Jakie preferujesz podejście w swoich aplikacjach na Androida?

Odpowiedz

1

Domyślam się, że to nie ma nic wspólnego z Androidem. w SQLite dostęp do kolumny poprzez indeks (pozycja kolumny) powinien być szybszy.

+2

Wiem, że moje pytanie brzmi: "znacząco" szybciej? – Sergio

+2

Nie znam dokładnej liczby, ale myślę, że różnica będzie znacząca tylko wtedy, gdy masz 100k + kolumn w tabeli. Najlepszym sposobem ustalenia tego jest utworzenie przykładowego projektu i obliczenie różnicy czasu za pomocą 'System.nanoTime()' –

+0

Dotyczy to systemu Android. Kursor zwrócony przez ContentProvider nie ma wymogu, aby był on wspierany przez SQLite.W rzeczywistości bardzo wielu nie jest. Możesz przyjrzeć się implementacjom kursorów, aby się przekonać. W szybkim wyglądzie AbstractCursor.getColumnIndex, będziesz absolutnie chciał cachować wartości indeksu samodzielnie, jeśli masz więcej niż kilka kolumn (wyszukiwanie to O (n) gdzie n jest liczbą kolumn). – lilbyrdie

25

Aby odpowiedzieć na twoje pytanie (i moje przy okazji), zrobiłem kilka testów.

Test ten był w zasadzie do sprawdzenia, ile czasu kwerendy zabrał tych dwóch przypadkach:

  1. Stosując metodę
  2. Uzyskiwanie identyfikatora kolumny, a następnie wywołanie bezpośrednio metodę
cursor.getString(COLUMN_POSITION) cursor.getString(cursor.getColumnIndex(COLUMN_NAME))

Aby test wydajności był znaczący, wstawiłem linie w bazie danych, a następnie wysłałem zapytanie: ContentProvider w sprawie tych elementów.

Wyniki:

___________________________________________________________________________ 
| 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%   | 
|___________________________________________________________________________| 

Więc coraz id kolumny pierwszej i wywołanie bezpośrednio metodę getString() zajmie około 20% mniej czasu.


szczegóły metody badawczej:

Platforma: Nexus 7 (2012) na Androidzie 4.3

stworzenie bazy danych:

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"); 
    } 
} 

Myślę, że spadek wydajności od 200 do 500 pochodzi z okna kursora. Miałem dużo dzienników takich jak powyżej 200 kolumn:

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

W obu przypadkach wywołujesz getString (indeks). Im większe zabranie tutaj, tym że getColumnIndex na 500 kolumnach zajmuje _jedno 4 sekundy _ na własnej. Biorąc pod uwagę, jak wygląda kod getColumnIndex, łatwo i warto go buforować. – lilbyrdie

+1

Ta odpowiedź jest teraz trochę nieaktualna. Jeśli spojrzysz na kod źródłowy dla 'getColumnIndex (String columnName)' w 'SQLiteCursor.java', możesz zobaczyć, że tworzy ona mapę String (name) do Integer (index) na górze metody. Dlatego wyszukiwanie nazwy kolumny dzieje się teraz tylko raz na kolumnę, na tabelę. – k2col