2012-07-05 8 views
13

Ich bin auf der Suche nach einer effizienten Möglichkeit zum Speichern und Suchen von UUID in Core Data. Diese UUID werden von vielen iOS-Geräten in einem verteilten System generiert. Jedes dieser Geräte kann etwa 20 bis 50 k UUIDs speichern.Wie effizient UUID in Core Data einfügen und holen

Es ist offensichtlich, dass das Speichern von UUID als String in Core Data die Effizienz der Indexierung beeinträchtigt. Aber nach einer Reihe von Untersuchungen fand ich, dass UUID als Binärdaten in Core Data speichern (und Index es) möglicherweise weniger effizient als das Speichern als String.

Da kein BINARY-ähnlicher oder VARBINARY-ähnlicher Datentyp in SQLit unterstützt wird. Ich denke, dass jeder Datentyp von Binärdaten in Core Data als BLOB in SQLit gespeichert wird. Da BLOB der langsamste zu indexierende Datentyp sein kann, hat dies einen schlechten Einfluss auf die Leistung.

So kann jemand helfen zu beantworten, gibt es eine effizientere Möglichkeit, UUID in Core Data zu speichern?

+0

Sie sich bewusst sind, dass der Zugang zu UDID ist haben als von iOS 5 als veraltet, nicht wahr? –

+3

Das OP spricht über UUIDs, die sich von der UDID unterscheiden. –

+0

@JodyHagins, Sie haben Recht. Die UUID, die ich erwähnte, ist Universally Unique IDentifier für ManagedObjects, die von meiner App erstellt werden. –

Antwort

30

Speichern Sie sie als ASCII-Zeichenfolge, und machen Sie das Feld zu einem Index.

EDIT

Egads, ich zufällig etwas Stossen über, und kam über das zu tun. Was für eine beschämende Antwort. Ich muss an diesem Tag in einer guten Laune gewesen sein. Wenn ich könnte, würde ich es einfach löschen und weitermachen. Dies ist jedoch nicht möglich, daher werde ich ein Update bereitstellen.

Erstens ist der einzige Weg zu wissen, was "effizient" ist, zu messen, unter Berücksichtigung von Programmzeit und -raum sowie der Komplexität des Quellcodes und des Programmieraufwands.

Glücklicherweise ist diese eine ziemlich einfach.

Ich schrieb eine sehr einfache OSX-Anwendung. Das Modell besteht aus einem einzigen Attribut: identifier.

Nichts davon zählt, wenn Sie Ihr Attribut nicht als Index markieren. Es dauert viel mehr Zeit beim Erstellen des Geschäfts, aber es wird Abfragen viel schneller machen.

Beachten Sie auch, dass für ein binäres Attribut ein Prädikat schaffen, ist genau das gleiche wie eine für einen String zu erstellen:

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

Die Anwendung ist sehr einfach. Zuerst erstellt es N Objekte und weist dem Bezeichnerattribut eine UUID zu. Es speichert den MOC alle 500 Objekte. Wir speichern dann alle Identifikatoren in einem Array und mischen sie zufällig. Der gesamte CD-Stapel wird dann vollständig entfernt, um alles aus dem Speicher zu entfernen.

Als nächstes bauen wir den Stapel erneut, und dann über die Bezeichner iterieren und einen einfachen Abruf durchführen. Das Abrufobjekt wird mit einem einfachen Prädikat konstruiert, um dieses eine Objekt zu holen. All dies geschieht innerhalb eines Autorespulepools, um jeden Abruf so makellos wie möglich zu halten (ich bestätige, dass es eine Interaktion mit den CD-Caches geben wird). Das ist nicht so wichtig, da wir nur die verschiedenen Techniken vergleichen.

Binäre Kennung ist die 16-Byte für die UUID.

UUID Zeichenfolge ist eine 36-Byte-Zeichenfolge, das Ergebnis des Aufrufs von [UUID UUIDString], und es sieht so aus (B85E91F3-4A0A-4ABB-A049-83B2A8E6085E).

Base64 String ist eine 24-Byte-Zeichenfolge, das Ergebnis von Base-64 Codierung der 16-Byte-UUID-Binärdaten, und es sieht so (uF6R80oKSrugSYOyqOYIXg ==) für die gleiche UUID.

Anzahl ist die Anzahl der Objekte für diesen Lauf.

SQLite-Größe ist die Größe der tatsächlichen SQLite-Datei.

WAL Größe ist, wie groß die WAL (Write-Ahead-Logging-Datei) wird - gerade FYI ...

erstellen die Anzahl von Sekunden ist die Datenbank, einschließlich Einsparung zu erstellen.

Abfrage ist die Anzahl der Sekunden, um jedes Objekt abzufragen.

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 

Das erste, was zu beachten ist hier, dass die tatsächliche Größe der Datenbank ist viel größer als der gespeicherten Bytes (1.600.000 und 16.000.000) - die für eine Datenbank zu erwarten ist. Die Menge an zusätzlichem Speicher ist etwas relativ zur Größe Ihrer tatsächlichen Objekte ... diese speichert nur die Kennung, so dass der Prozentsatz des Aufwands höher ist.

Zweitens, auf die Geschwindigkeit Probleme, als Referenz, die gleiche 1.000.000 Objektabfrage, aber mit der Objekt-ID in der Abruf dauerte etwa 82 Sekunden (beachten Sie den krassen Unterschied zwischen diesem und existingObjectWithID:error: aufrufen, die satte 0,3065 Sekunden dauerte).

Sie sollten Ihre eigene Datenbank profilieren, einschließlich einer vernünftigen Verwendung von Instrumenten auf dem laufenden Code. Ich stelle mir vor, dass die Zahlen etwas anders sein würden, wenn ich mehrere Läufe machen würde, aber sie sind so nah, dass es für diese Analyse nicht notwendig ist.

Lassen Sie uns jedoch anhand dieser Zahlen Effizienzmessungen für die Codeausführung betrachten.

  • Wie erwartet, ist das Speichern der binären Roh-UUID-Daten in Bezug auf den Platz effizienter.
  • Die Erstellungszeit ist ziemlich nah (der Unterschied scheint auf der Zeit zu beruhen, die zum Erstellen der Zeichenfolgen und des zusätzlichen Speicherplatzes erforderlich ist).
  • Die Abfragezeiten scheinen fast identisch zu sein, wobei die Binärzeichenfolge ein wenig langsamer erscheint. Ich denke, das war das ursprüngliche Anliegen - eine Abfrage eines binären Attributs durchzuführen.
  • Binary gewinnt viel Platz, und es kann als ein enger Bezug sowohl zur Erstellungs- als auch zur Abfragezeit angesehen werden. Wenn wir nur diese betrachten, ist das Speichern der Binärdaten der klare Gewinner.

    Wie wäre es mit Quellcode Komplexität und Programmierer Zeit?

    Nun, wenn Sie eine moderne Version von iOS und OSX verwenden, gibt es praktisch keinen Unterschied, besonders mit einer einfachen Kategorie auf NSUUID.

    Allerdings gibt es eine Überlegung für Sie, und das ist die Leichtigkeit der Verwendung der Daten in der Datenbank. Wenn Sie binäre Daten speichern, ist es schwierig, eine gute visuelle Darstellung der Daten zu erhalten.

    Wenn also aus irgendeinem Grund die Daten in der Datenbank für Menschen effizienter gespeichert werden sollen, ist die Speicherung als Zeichenfolge die bessere Wahl. Sie sollten also eine base64-Kodierung (oder eine andere Kodierung) in Betracht ziehen - denken Sie daran, dass sie bereits in der Basis-256-Kodierung ist.

    FWIW, hier ist ein Beispiel Kategorie einen leichteren Zugang zum UUID als sowohl NSData und Base64-String zur Verfügung zu stellen:

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

    Guter Rat, danke. Es kann die Hälfte der Anstrengungen für Core Data sparen. Aber ich frage mich immer noch, wie die ASCII-Zeichenkette von Core Data zu SQLit gemappt wird. Ich denke, nur ein echter Test kann sagen. –

    +0

    Im Allgemeinen möchten Sie sicherstellen, dass alle Zeichenfolgen, die Sie durchsuchen, normalisiert sind, um Unicode auszuschließen. Anstatt die Groß-/Kleinschreibung nicht zu beachten, normalisieren Sie die Daten, um Unicode und Groß-/Kleinschreibung zu entfernen. Verwenden Sie < and > anstelle von BEGINSWITH usw. Es gibt großartige Vorschläge in den WWDC-Videos 2010, 2011 und 2012. Ich empfehle sie sehr. –

    +0

    hi @JodyHagins Können Sie die Namen von WWDC-Videos zu diesem Thema angeben? Es gibt zu viele von ihnen. Danke im Voraus. –