3 Stimmen

Fixing prädizierte NSFetchedResultsController/NSFetchRequest Leistung mit SQLite Backend?

Ich habe eine Reihe von NSFetchedResultsControllers, die einige Tabellenansichten versorgen, und ihre Leistung auf Gerät war miserabel, in der Größenordnung von Sekunden. Da alles auf Haupt-Thread läuft, blockiert es meine App beim Start, was nicht toll ist.

Ich habe nachgeforscht und es stellte sich heraus, dass das Prädikat das Problem ist:

NSPredicate *somePredicate = [NSPredicate predicateWithFormat:@"ANY somethings == %@", something];
[fetchRequest setPredicate:somePredicate];

D.h. die Entität fetch, nennen wir sie "things", hat eine many-to-many Beziehung mit der Entität "something". Dieses Prädikat ist ein Filter, der die Ergebnisse auf die Dinge beschränkt, die eine Beziehung zu einem bestimmten "Etwas" haben.

Als ich das Prädikat zum Testen entfernte, sank die Abrufzeit (der erste performFetch:-Aufruf) (in einigen Extremfällen) von 4 Sekunden auf etwa 100 ms oder weniger, was akzeptabel ist. Das ist akzeptabel. Ich bin jedoch beunruhigt, da dies einen Großteil des Nutzens zunichte macht, den ich mir von Core Data und NSFRC erhofft hatte, die ansonsten ein leistungsfähiges Werkzeug zu sein scheinen.

Meine Frage ist also, wie kann ich diese Leistung optimieren? Verwende ich das Prädikat falsch? Sollte ich das Modell/Schema irgendwie ändern? Und welche anderen Möglichkeiten gibt es, dies zu beheben? Ist diese Art von Leistungseinbußen zu erwarten? (Es handelt sich um Hunderte von <1KB-Objekten.)

MIT DETAILS BEARBEITEN:

Hier ist der Code:

[fetchRequest setFetchLimit:200];
NSLog(@"before fetch");
BOOL success = [frc performFetch:&error];
if (!success) {
    NSLog(@"Fetch request error: %@", error);
}
NSLog(@"after fetch");

Aktualisierte Protokolle (früher hatte ich hier einige Anwendungsschwächen, die die Leistung beeinträchtigten. Dies sind die aktualisierten Protokolle, die in meiner aktuellen Umgebung so optimal wie möglich sein sollten):

2010-02-05 12:45:22.138 Special Ppl[429:207] before fetch
2010-02-05 12:45:22.144 Special Ppl[429:207] CoreData: sql: SELECT DISTINCT 0, t0.Z_PK, t0.Z_OPT, <model fields> FROM ZTHING t0 LEFT OUTER JOIN Z_1THINGS t1 ON t0.Z_PK = t1.Z_2THINGS WHERE  t1.Z_1SOMETHINGS = ? ORDER BY t0.ZID DESC LIMIT 200
2010-02-05 12:45:22.663 Special Ppl[429:207] CoreData: annotation: sql connection fetch time: 0.5094s
2010-02-05 12:45:22.668 Special Ppl[429:207] CoreData: annotation: total fetch execution time: 0.5240s for 198 rows.
2010-02-05 12:45:22.706 Special Ppl[429:207] after fetch

Wenn ich die gleiche Abfrage ohne Prädikat durchführe (indem ich die beiden Zeilen am Anfang der Frage auskommentiere):

2010-02-05 12:44:10.398 Special Ppl[414:207] before fetch
2010-02-05 12:44:10.405 Special Ppl[414:207] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, <model fields> FROM ZTHING t0 ORDER BY t0.ZID DESC LIMIT 200
2010-02-05 12:44:10.426 Special Ppl[414:207] CoreData: annotation: sql connection fetch time: 0.0125s
2010-02-05 12:44:10.431 Special Ppl[414:207] CoreData: annotation: total fetch execution time: 0.0262s for 200 rows.
2010-02-05 12:44:10.457 Special Ppl[414:207] after fetch

20-facher Unterschied in den Zeiten. 500ms ist nicht so groß, und es scheint keine Möglichkeit zu sein, es im Hintergrund-Thread zu tun oder anderweitig zu optimieren, die ich mir vorstellen kann. (Abgesehen von der Umstellung auf einen Binärspeicher, wo dies ein Nicht-Problem wird, also könnte ich das tun. Binärspeicher Leistung ist konsequent ~100ms für die oben genannten 200-Objekt-Predicated-Abfrage).

(Ich habe hier bereits eine andere Frage verschachtelt, die ich jetzt weggezogen ).

4voto

dk. Punkte 1836

Dies ist ein ständiges Problem für viele von uns, die versuchen, Core Data auf dem iPhone für größere Datenbanken oder komplexere Schemata zu verwenden. In meinem Profil finden Sie Links zu verschiedenen SO-Fragen zu diesem Thema - im Zusammenhang mit der Volltextsuche.

Nicht alle Anwendungen sind nur durch die Anwendung eines Prädikats auf eine to-many-Beziehung langsam, es steckt also mehr dahinter. Siehe Das Leistungsdokument von Apple . Set -com.apple.CoreData.SQLDebug 1.

Prüfen Sie gegebenenfalls, ob ein Index vorhanden ist. Vergewissern Sie sich, dass keine SQL-Funktionen (z. B. Format-/Typkonvertierungen) aufgerufen werden, die verhindern, dass Sqlite effizient verknüpft oder einen Index verwendet.

Wenn Sie viele Zeilen haben und viele Attribute in Ihrer Entität vorhanden sind, kann es zu Speicherproblemen kommen. Ziehen Sie in Erwägung, eine einzelne Entität in eine kleinere Entität mit einigen für die Suche verwendeten Attributen aufzuteilen und den Rest bei Bedarf hinzuzuziehen.

Wenn Ihr Prädikat komplexer ist als der to-many-Beziehungsteil, können Sie mit einer Suche in zwei Schritten experimentieren. So könnte die SQL-Suche z. B. ineffizient sein, indem sie zunächst eine Verknüpfung und dann eine Filterung vornimmt, anstatt eine Filterung und dann eine Verknüpfung. Dies würde jedoch NSFetchedResultsController zerstören. Dennoch könnte es etwas Licht auf das Problem werfen.

Ich würde nicht so etwas wie packen ObjectIDs in ein Attribut, um Ihre eigenen to-many Fremdschlüsselverweis zu machen. Klingt wie ein schrecklicher Hack, der Sie sicherlich davon abhalten soll, Core Data ernsthaft zu verwenden... doch die Vorteile von Core Data sind zahlreich.

Experimentieren Sie mit dem Entfernen oder Hinzufügen der umgekehrten Beziehung. In einem Fall habe ich festgestellt, dass meine Abfrage ohne die umgekehrte Beziehung viel schneller lief. Der Nachteil war, dass das Entfernen der umgekehrten Beziehung meine Datenbank aufblähte.

Lassen Sie uns wissen, was Sie finden.

0voto

Jaanus Punkte 17458

Die praktische Lösung für mich bestand darin, einfach zum Core Data-Binärspeicher zu wechseln, der eine viel bessere Leistung bietet. Ich habe nicht untersucht, ob ich die Leistung von Core Data SQLite mit meiner aktuellen Anwendung verbessern kann.

0voto

Marcus S. Zarra Punkte 46405

Bei einer binären Geschichte wird der gesamte Datensatz in den Speicher geladen, was bei einigen Geräten zu Speicherproblemen führt. Stattdessen sollten Sie Ihr Prädikat neu schreiben.

Da Ihre Beziehungen doppelseitig sind ( rechts ?) können Sie die Sache von der anderen Seite angehen und brauchen wahrscheinlich keine NSFetchedResultsController überhaupt nicht für diese Situation. Nehmen Sie an, Sie haben:

MyWidgetEntity <--->> SomethingEntity

Da Sie bereits einen Verweis auf eine Instanz von SomethingEntity haben, fragen Sie es einfach nach seinem Widget über:

id widget = [mySomethingInstance valueForKey:@"widget"];

In der Situation, in der Sie sich befinden:

MyWidgetEntity <<-->> SomethingEntity

Zugang zu allen zugehörigen Widgets erhalten Sie über:

NSSet *widgets = [mySomethingInstance valueForKey:@"widgets"];

Nein NSFetchedResultsController wird benötigt, weil man diese Menge in ein Array verwandeln und sortieren kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X