2012-02-02 65 views
6

從性能上看:是不是很好,如果在每次訪問我的遊標我使用類似:「優化」獲得光標在安卓VS列名的位置

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

或者我應該看衡量提高性能,如果我用這個來代替:

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

我更喜歡第一種方法,因爲其餘代碼不依賴於在查詢中列的位置,但只的名字柱。所以我的問題是:爲了改善使用常量位置訪問遊標的性能,值得犧牲這一點?你喜歡什麼方式在你的Android應用程序?

回答

1

我想這與android無關。在SQLite中,通過索引訪問列(列位置)應該更快。

+2

我知道,我的問題是:「顯着」更快? – Sergio 2012-02-02 14:41:34

+2

我不知道確切的數字,但我認爲只有當你在桌上有100k +列時,差異才會顯着。確定這一點的最好方法是創建一個示例項目並使用'System.nanoTime()'計算時差。' – 2012-02-02 14:51:02

+0

這與Android有關。通過ContentProvider返回的遊標不需要由SQLite支持。事實上,很多人都沒有。你可以看看遊標的實現以找出你自己。在AbstractCursor.getColumnIndex的快看,你絕對要緩存,如果你有比只是一對夫婦的列更多的索引值自己(的尋找操作是O(n),其中n是列數)。 – lilbyrdie 2014-04-07 12:56:11

25

要回答你的問題(和我的方式),我做了一些測試。

這次試驗主要檢查多少時間查詢了那些兩種情況:

  1. 使用cursor.getString(cursor.getColumnIndex(COLUMN_NAME))方法
  2. 第一獲取列ID,然後直接調用cursor.getString(COLUMN_POSITION)方法

爲了使性能測試具有實際意義,我在數據庫中插入了行,然後我做了一個查詢,投擲我的ContentProvider這些元素。

結果:

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

因此,首先讓列ID和呼叫直接getString()方法將花費大約20%更少的時間。


試驗方法的細節:

平臺:在Android 4.3 的Nexus 7(2012)

數據庫創建:

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()); 

測試用例:

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

我認爲200到500之間的性能下降來自光標窗口。我有很多像200列以上的日誌:

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

在這兩種情況下,你調用的getString(指數)。更大的帶走這裏是getColumnIndex 500列發生在它自己的_nearly 4 seconds_。鑑於getColumnIndex代碼的外觀,對其進行緩存很容易且值得。 – lilbyrdie 2014-04-07 13:07:09

+1

這個答案現在有點過時了。如果你看一下'getColumnIndex(字符串COLUMNNAME)的源代碼''在你SQLiteCursor.java'可以看到,他們在方法的頂部創建地圖的字符串(名稱)以整數(指數)一個。因此,列名稱查找現在僅在每個列的每個表中發生一次。 – k2col 2016-05-16 17:38:32