2012-07-05 22 views
13

Estoy buscando una forma eficiente de almacenar y buscar UUID en Core Data. Esos UUID son generados por muchos dispositivos iOS en un sistema distribuido. Cada uno de esos dispositivos puede almacenar aproximadamente 20-50k UUID.Cómo insertar y buscar eficientemente UUID en Core Data

Es obvio que el almacenamiento de UUID como cadena en Core Data dañará la eficacia de la indexación. Pero después de una serie de investigaciones descubrí que almacenando el UUID como Datos Binarios en Datos Básicos (e indexándolo) puede ser menos eficiente que almacenarlo como Cadena.

Como no hay tipos BINARY-like o VARBINARY-like en SQLit es compatible. Supongo que cualquier tipo de datos de datos binarios en Core Data se almacena como BLOB en SQLit. Como BLOB podría ser el tipo de datos más lento para indexar, tendrá una influencia negativa en el rendimiento.

Entonces, ¿alguien puede ayudarme a responder, hay una manera más eficiente de almacenar UUID en Core Data?

+0

que son conscientes de que el acceso a UDID de haber dejado de utilizarse a partir del iOS 5, ¿verdad? –

+3

El OP está hablando de UUID que son diferentes del UDID. –

+0

@JodyHagins, tienes razón. El UUID que mencioné es IDentificador universal único para ManagedObjects creado por mi aplicación. –

Respuesta

30

Guárdelos como una cadena ASCII y convierta el campo en un índice.

EDITAR

Egads, que pasó a estar haciendo algunos hurgando, y me encontré con esto. Qué vergonzosa respuesta. Debo haber estado un poco de humor ese día. Si pudiera, simplemente lo eliminaría y seguiría. Sin embargo, eso no es posible, así que proporcionaré un recorte de una actualización.

Primero, la única manera de saber qué es "eficiente" es medir, teniendo en cuenta el tiempo y el espacio del programa, así como la complejidad del código fuente y el esfuerzo del programador.

Afortunadamente, este es bastante fácil.

Escribí una aplicación OSX muy simple. El modelo consiste en un solo atributo: identifier.

Nada de esto importa, si no marca su atributo como índice. Tomará mucho más tiempo al crear la tienda, pero hará que las consultas sean mucho más rápidas.

Además, tenga en cuenta que la creación de un predicado para un atributo binario es exactamente el mismo que crear una para una cadena:

fetchRequest.predicate = 
    [NSPredicate predicateWithFormat:@"identifier == %@", identifier]; 

La aplicación es muy sencilla. Primero, crea N objetos y asigna un UUID al atributo identificador. Guarda el MOC cada 500 objetos. Luego almacenamos todos los identificadores en una matriz y los mezcla aleatoriamente. Toda la pila de CD se derriba por completo para eliminarlo todo de la memoria.

A continuación, construimos la pila de nuevo, y luego iteramos sobre los identificadores, y hacemos una búsqueda simple. El objeto fetch se construye con un predicado simple para obtener ese objeto. Todo esto se realiza dentro de un autoreleasepool para mantener cada búsqueda lo más prístina posible (reconozco que habrá alguna interacción con las memorias caché de CD). Eso no es tan importante, ya que solo estamos comparando las diferentes técnicas.

El identificador binario es el de 16 bytes para el UUID.

Cadena UUID es una cadena de 36 bytes, como resultado de una llamada a [UUID UUIDString], y se ve así (B85E91F3-4A0A-4ABB-A049-83B2A8E6085E).

Base64 String es una cadena de 24 bytes, el resultado de la base 64 que codifica los datos binarios del UUID de 16 bytes, y se ve así (uF6R80oKSrugSYOyqOYIXg ==) para el mismo UUID.

La cuenta es el número de objetos para esa ejecución.

Tamaño de SQLite es el tamaño del archivo sqlite real.

tamaño WAL es el tamaño del archivo WAL (escritura anticipada-registro) recibe - lo digo ...

Crear es el número de segundos para crear la base de datos, incluyendo el ahorro.

La consulta es el número de segundos para consultar cada objeto.

Data Type  | Count (N) | SQLite Size | WAL Size | Create | Query 
--------------+-----------+-------------+-----------+---------+--------- 
Binary  | 100,000 | 5,758,976 | 5,055,272 | 2.6013 | 9.2669 
Binary  | 1,000,000 | 58,003,456 | 4,783,352 | 59.0179 | 96.1862 
UUID String | 100,000 | 10,481,664 | 4,148,872 | 3.6233 | 9.9160 
UUID String | 1,000,000 | 104,947,712 | 5,792,752 | 68.5746 | 93.7264 
Base64 String | 100,000 | 7,741,440 | 5,603,232 | 3.0207 | 9.2446 
Base64 String | 1,000,000 | 77,848,576 | 4,931,672 | 63.4510 | 94.5147 

La primera cosa a tener en cuenta es que el tamaño de la base de datos real es mucho mayor que los bytes almacenados (1.600.000 y 16.000.000) - que es de esperar de una base de datos. La cantidad de almacenamiento adicional será relativamente relativa al tamaño de sus objetos reales ... este solo almacena el identificador por lo que el porcentaje de sobrecarga será mayor).

En segundo lugar, sobre los problemas de velocidad, como referencia, haciendo la misma consulta de objeto 1.000.000, pero utilizando el objeto-id en la zona de alcance tomó unos 82 segundos (nótese la gran diferencia entre eso y llamar existingObjectWithID:error: que tuvo la friolera 0.3065 segundos)

Debe crear un perfil de su propia base de datos, que incluya un uso juicioso de los instrumentos en el código de ejecución. Imagino que los números serían algo diferentes si hiciera varias ejecuciones, pero están tan cerca que no es necesario para este análisis.

Sin embargo, basándonos en estos números, veamos las medidas de eficiencia para la ejecución del código.

  • Como era de esperar, almacenar los datos binarios del UUID sin procesar es más eficiente en términos de espacio.
  • El tiempo de creación es bastante cercano (la diferencia parece estar basada en el tiempo para crear las cadenas y el espacio de almacenamiento adicional requerido).
  • Los tiempos de consulta parecen casi idénticos, con la secuencia binaria parece ser un poco más lenta. Creo que esta fue la preocupación original: hacer una consulta sobre un atributo binario.

El binario gana mucho espacio, y se puede considerar como un acercamiento entre el tiempo de creación y el tiempo de consulta. Si solo consideramos eso, almacenar los datos binarios es el claro ganador.

¿Qué hay de la complejidad del código fuente y el tiempo del programador?

Bueno, si está utilizando una versión moderna de iOS y OSX, prácticamente no hay diferencia, especialmente con una categoría simple en NSUUID.

Sin embargo, hay una consideración para usted, y eso es fácil de usar los datos en la base de datos. Cuando almacena datos binarios, es difícil obtener una buena visualización de los datos.

Por lo tanto, si, por alguna razón, desea que los datos en la base de datos se almacenen de una manera más eficiente para los humanos, entonces almacenarlos como una cadena es una mejor opción. Por lo tanto, es posible que desee considerar una codificación base64 (o alguna otra codificación, aunque recuerde que ya está codificada en base-256).

Fwiw, aquí está una categoría ejemplo, para proporcionar un acceso más fácil a la UUID ya que tanto la cadena y NSData base 64:

- (NSData*)data 
{ 
    uuid_t rawuuid; 
    [self getUUIDBytes:rawuuid]; 
    return [NSData dataWithBytes:rawuuid length:sizeof(rawuuid)]; 
} 

- (NSString*)base64String 
{ 
    uuid_t rawuuid; 
    [self getUUIDBytes:rawuuid]; 
    NSData *data = [NSData dataWithBytesNoCopy:rawuuid length:sizeof(rawuuid) freeWhenDone:NO]; 
    return [data base64EncodedStringWithOptions:0]; 
} 

- (instancetype)initWithBase64String:(NSString*)string 
{ 
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0]; 
    if (data.length == sizeof(uuid_t)) { 
     return [self initWithUUIDBytes:data.bytes]; 
    } 
    return self = nil; 
} 

- (instancetype)initWithString:(NSString *)string 
{ 
    if ((self = [self initWithUUIDString:string]) == nil) { 
     self = [self initWithBase64String:string]; 
    } 
    return self; 
} 
+0

Buen consejo, gracias. Puede ahorrar la mitad de los esfuerzos para Core Data. Pero aún me pregunto cómo se correlaciona la cadena ASCII desde Core Data a SQLit. Supongo que solo ejecutar una prueba real puede decirlo. –

+0

En general, quiere asegurarse de que las cadenas que busca estén normalizadas para excluir Unicode. Además, en lugar de utilizar búsquedas que no distinguen entre mayúsculas y minúsculas, normalice los datos para eliminar unicode y mayúsculas y minúsculas. Use < and > en lugar de BEGINSWITH, etc. Hay sugerencias excelentes en los videos WWDC 2010, 2011 y 2012. Los recomiendo mucho. –

+0

hi @JodyHagins ¿Podría especificar los nombres de los videos de WWDC sobre este tema? Hay demasiados de ellos. Gracias por adelantado. –