2013-04-18 3 views
3

Android 및 Project의 iOS에서 SQLite 성능 사이에서 벤치 마크를 수행하려고하고 있으며 Android와 비교하여 iOS 플랫폼에서 성능이 상당히 떨어지는 것 같습니다.Android 및 iOS의 SQLite 간의 성능 차이

내가 얻으려고하는 것은 SQLite DB에 여러 행 (5000)을 삽입하고 플랫폼을 비교하는 시간을 측정하는 것입니다. Android의 경우 5000 밀리 초를 수행하는 데 500 밀리미터의 결과를 얻지 만 iOS의 경우 동일한 작업을 20 초 이상 수행합니다. 어떻게 이럴 수있어?

int numEntries = 5000; 
self.dataArray = [[NSMutableArray alloc] initWithCapacity:numEntries];//Array for random data to write to database 

//generate random data (100 char strings) 
for (int i=0; i<numEntries; i++) { 
    [self.dataArray addObject:[self genRandStringLength:100]]; 
} 

// Get the documents directory 
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 

NSString *docsDir = [dirPaths objectAtIndex:0]; 

// Build the path to the database file 
NSString *databasePath = [[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent: @"benchmark.db"]]; 

NSString *resultHolder = @""; 

//Try to open DB, if file not present, create it 
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK){ 

    sql = @"CREATE TABLE IF NOT EXISTS BENCHMARK(ID INTEGER PRIMARY KEY AUTOINCREMENT, TESTCOLUMN TEXT)"; 

    //Create table 
    if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, NULL) == SQLITE_OK){ 
     NSLog(@"DB created"); 
    }else{ 
     NSLog(@"Failed to create DB"); 
    } 

     //START: INSERT BENCHMARK 
     NSDate *startTime = [[NSDate alloc] init];//Get timestamp for insert-timer 

     //Insert values in DB, one by one 
     for (int i = 0; i<numEntries; i++) { 
      sql = [NSString stringWithFormat:@"INSERT INTO BENCHMARK (TESTCOLUMN) VALUES('%@')",[self.dataArray objectAtIndex:i]]; 
      if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, NULL) == SQLITE_OK){ 
       //Insert successful 
      } 
     } 

     //Append time consumption to display string 
     resultHolder = [resultHolder stringByAppendingString:[NSString stringWithFormat:@"5000 insert ops took %f sec\n", [startTime timeIntervalSinceNow]]]; 

     //END: INSERT BENCHMARK 

안드로이드 코드 :

  // SETUP 
      long startTime, finishTime; 

     // Get database object 
      BenchmarkOpenHelper databaseHelper = new BenchmarkOpenHelper(getApplicationContext()); 
      SQLiteDatabase database = databaseHelper.getWritableDatabase(); 

      // Generate array containing random data 
      int rows = 5000; 
      String[] rowData = new String[rows]; 
      int dataLength = 100; 

      for (int i=0; i<rows; i++) { 
       rowData[i] = generateRandomString(dataLength); 
      } 

      // FIRST TEST: Insertion 
      startTime = System.currentTimeMillis(); 

      for(int i=0; i<rows; i++) { 
       database.rawQuery("INSERT INTO BENCHMARK (TESTCOLUMN) VALUES(?)", new String[] {rowData[i]}); 
      } 

      finishTime = System.currentTimeMillis(); 
      result += "Insertion test took: " + String.valueOf(finishTime-startTime) + "ms \n"; 
      // END FIRST TEST 

답변

2

당신은 필요

내 아이폰 OS 코드 (삽입 부분)의 조각이다, dataArray는 5000 무작위로 100 문자의 NSStrings와 배열입니다 거래를 사용하십시오 - BEGIN을 실행하고 COMMIT으로 끝내십시오.

이렇게하면 INSERT 성능이 크게 향상됩니다. 그는 내가 5000 인서트는 두 플랫폼 모두에 매우 빠른 것으로 기대 완료 일단

http://www.titaniumdevelopment.com.au/blog/2012/01/27/10x-faster-inserts-in-sqlite-using-begin-commit-in-appcelerator-titanium-mobile/

. 여기

입니다 속도 견고성을 거래 바인드 변수를 사용하여 다양한 PRAGMA 모드를 가능하게 포함 SQLite는 성능을 향상시킬 수있는 여러 가지의 톤 나열 다른 StackOverflow의 응답 다음에 추가로, iOS에서 Improve INSERT-per-second performance of SQLite?

+1

테스트 할 안드로이드 장치가 없기 때문에 확실하지 않습니다. 그러나 OS X와 ​​Linux에서 SQLite를 사용한 경험은 OS X이 I/O 차단을 야기하는 "디스크로 플러시"요청을 실제로 받아들이는 반면 Linux는 손을 흔드는 반면 차단하지는 않습니다. 설명 할 수 있습니다. – StilesCrisis

+0

나는 C (iOS) 방식이 Java (Android) 방식보다 약간 빠르다고 생각한다. –

+0

고맙습니다. 나는 동기화의 튜링으로 약간의 속도를 얻었다! – Andain

4

BEGIN/COMMIT StilesCrisis에 대한 설명이 변경되어 가장 큰 성능 차이가 나옵니다. iOS 성능을 더욱 최적화하려면 SQL을 한 번 준비한 다음 sqlite3_bind_text, sqlite3_stepsqlite3_reset을 반복적으로 호출하십시오. 이 경우 대략 두 배나 빠른 것으로 보입니다.

다음
- (void)insertWithExec 
{ 
    NSDate *startDate = [NSDate date]; 

    NSString *sql; 

    if (sqlite3_exec(database, "BEGIN", NULL, NULL, NULL) != SQLITE_OK) 
     NSLog(@"%s: begin failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 

    for (NSString *value in dataArray) 
    { 
     sql = [NSString stringWithFormat:@"INSERT INTO BENCHMARK (TESTCOLUMN) VALUES('%@')", value]; 
     if (sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL) != SQLITE_OK) 
      NSLog(@"%s: exec failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 
    } 

    if (sqlite3_exec(database, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) 
     NSLog(@"%s: commit failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 

    NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:startDate]; 

    // log `elapsed` here 
} 

내가 SQL을 준비 코드의 최적화 된 연주입니다 :

그래서, 여기 (수동 때마다 SQL을 구축 stringWithFormat%@ 사용) sqlite3_exec와 기존의 아이폰 OS 로직의 내 연주입니다 한 번만, 그러나 당신의 안드로이드 코드를 사용하는 SQL에서 같은 ? 자리에 우리의 데이터를 바인딩 sqlite3_bind_text를 사용

내 아이폰 5에
- (void)insertWithBind 
{ 
    NSDate *startDate = [NSDate date]; 

    if (sqlite3_exec(database, "BEGIN", NULL, NULL, NULL) != SQLITE_OK) 
     NSLog(@"%s: begin failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 

    sqlite3_stmt *statement; 

    NSString *sql = @"INSERT INTO BENCHMARK (TESTCOLUMN) VALUES(?)"; 

    if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, NULL) != SQLITE_OK) 
     NSLog(@"%s: prepare failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 

    for (NSString *value in dataArray) 
    { 
     if (sqlite3_bind_text(statement, 1, [value UTF8String], -1, NULL) != SQLITE_OK) 
      NSLog(@"%s: bind failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 

     if (sqlite3_step(statement) != SQLITE_DONE) 
      NSLog(@"%s: step failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 

     if (sqlite3_reset(statement) != SQLITE_OK) 
      NSLog(@"%s: reset failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 
    } 

    sqlite3_finalize(statement); 

    if (sqlite3_exec(database, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) 
     NSLog(@"%s: commit failed: %s", __FUNCTION__, sqlite3_errmsg(database)); 

    NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:startDate]; 

    // log `elapsed` here 
} 

, sqlite3_exec 논리 (내 insertWithExec 메서드)를 사용하여 280-290ms를 삽입하고 sqlite3_bind_text, sqlite3_stepsqlite3_reset (내 insertWithBind 메서드)와 동일한 5,000 개의 레코드를 삽입하는 데 110-127ms가 걸렸습니다. 내 번호는 다른 장치 (다른 장치를 삽입하는 것, 다른 dataValues 개체를 삽입하는 것, 백그라운드 대기열에서 수행 한 것 등)과 비교할 수는 없지만 한 번 SQL 문을 준비 할 때 절반 이하의 시간이 걸렸습니다. bind, step 및 reset 호출 만 반복합니다.

안드로이드 코드를 살펴보면 ? 자리 표시자를 사용하고 있다는 것을 알았 기 때문에 sqlite3_bind_text도 역시 뒤에서 발생한다고 가정합니다. (단 한 번 준비하고 바인딩/스테핑/매번 다시 설정하거나 매번 다시 준비하는 작업, 아마도 후자 작업). 당신이 안드로이드에서했던 것처럼 수동 탈출에 필요에서 당신을 절약 할 여담으로


은, 엄지 손가락의 일반적인 규칙으로, 당신은 항상, 오히려 stringWithFormat에 수동으로 SQL을 구축하는 것보다,의 ? 자리를 사용해야합니다 데이터의 아포스트로피, SQL 인젝션 공격으로부터 당신을 보호합니다.

+0

@ user2295573 내 답변에 동의 해 주셔서 감사 드리며 도움이 되서 기쁩니다. 성능 문제의 99 %가 BEGIN/COMMIT으로 해결되었으므로 StilesCrisis의 답변을 수락하면 불쾌감을 느끼지 않을 것입니다. – Rob