Diferencias de rendimiento entre SQLite en Android e iOS

Estoy tratando de realizar un punto de referencia entre el rendimiento de SQLite en Android y iOS para un proyecto, y parecen obtener un rendimiento muy malo en la plataforma iOS, en comparación con Android.

Lo que estoy tratando de lograr es medir el tiempo para insertar un número de filas (5000) en el SQLite DB y comparar entre plataformas. Para Android obtengo resultados alrededor de 500ms para realizar las 5000 inserciones, pero para iOS la misma operación toma más de 20s. ¿Cómo puede ser esto?

Este es un fragmento de mi código de iOS (la parte de la inserción), dataArray es una matriz con 5000 NSStrings al azar 100 caracteres:

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 

Snippet de código de Android:

  // 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 

Debe utilizar una transacción: inicie ejecutando BEGIN y finalizando con COMMIT .

Esto mejorará en gran medida el rendimiento de INSERT .

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

Una vez hecho esto esperaría que 5000 insertos fueran bastante rápidos en ambas plataformas.

Aquí hay otra respuesta de StackOverflow que enumera una tonelada de cosas diferentes que pueden mejorar el rendimiento de SQLite, incluyendo el uso de variables de enlace y la habilitación de varios modos PRAGMA que intercambian la robustez por la velocidad: ¿ Mejora el rendimiento INSERT-per-second de SQLite?

En iOS, además del cambio BEGIN / COMMIT que discutió StilesCrisis, que ofrece la diferencia de rendimiento más dramática, si desea optimizar aún más su rendimiento de iOS, considere la posibilidad de preparar SQL una vez y luego repetidamente llamar a sqlite3_bind_text , sqlite3_step y sqlite3_reset . En este caso, parecía hacerse aproximadamente el doble de rápido.

Por lo tanto, aquí está mi versión de su lógica iOS existente con sqlite3_exec (que utiliza stringWithFormat y %@ para construir manualmente el SQL cada vez):

 - (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 } 

He aquí una interpretación optimizada del código en el que preparo el SQL solo una vez, pero luego utilizamos sqlite3_bind_text para enlazar nuestros datos al mismo ? marcador de posición en el SQL que utilizó su código de Android:

 - (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 } 

En mi iPhone 5, tomó 280-290ms para insertar 5.000 registros usando su lógica sqlite3_exec (mi método insertWithExec ) y tomó 110-127ms para insertar los mismos 5.000 registros con sqlite3_bind_text , sqlite3_step y sqlite3_reset (mi método insertWithBind ). Mis números no son comparables a los suyos (dispositivo diferente, insertar diferentes objetos dataValues , lo hice en una cola de fondo, etc.), pero es notable que tomó menos de la mitad del tiempo al preparar la sentencia SQL una vez, y luego sólo repitiendo las llamadas bind, step y reset.

Mirando el código de Android, me doy cuenta de que está usando el ? placeholder, así que supongo que está haciendo sqlite3_bind_text detrás de las escenas, también (aunque no sé si se está preparando una vez y vinculante / pisar / restablecer cada vez, o volver a preparar cada vez, probablemente el último).


Como un aparte, como regla general, siempre debe utilizar el ? , como lo hiciste en Android, en lugar de construir el SQL manualmente con stringWithFormat , ya que evita que necesites apóstrofos de escape manuales en tus datos, te protege contra ataques de inyección de SQL, etc.

  • Android SQLite ON CONFLICT UPDATE es posible?
  • Android: la columna '_id' no existe
  • Base de datos SQLite en android y java
  • Rendimiento ORM: ¿es greenDAO más rápido que ORMLite?
  • ¿Es la operación costosa de SQLite cursor.getCount en Android
  • Datetime ('ahora') da tiempo equivocado
  • OnCreate () no es llamado por getWritableDatabase ()
  • Tablas de muestra Android sqlite
  • Vista de lista con la opción de actualización y supresión mediante contextmenu
  • Cursor while loop devuelve cada valor pero el último
  • Android: ¿puedo usar una clase SQLiteOpenHelper para varios archivos de base de datos?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.