2011-09-01 3 views
7

mon application iPhone a une entité Words avec les attributs word, length et language. Les deux sont indexés: Entity and attributesSimple Core Data fetch est très lent

J'ai copié le cdatamodel et la base de données dans une application d'importateur séparée où il a été pré-rempli avec environ 400k mots dans différentes langues. J'ai vérifié l'importation en examinant le fichier SQLite, puis j'ai copié la base de données préremplie dans le projet iPhone.

D'abord je pensais que le prédicat (simple) était le problème. Mais même après avoir supprimé le prédicat de la demande de récupération, il faut un temps très long pour l'exécution:

2011-09-01 09:26:38.945 MyApp[3474:3c07] Start 
2011-09-01 09:26:58.120 MyApp[3474:3c07] End 

Voici ce que mon code ressemble:

// Get word 
NSLog(@"Start"); 
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Words" inManagedObjectContext:appDelegate.managedObjectContext]; 
[fetchRequest setEntity:entity]; 

NSError *error = nil; 
NSArray *fetchedObjects = [appDelegate.managedObjectContext executeFetchRequest:fetchRequest error:&error]; 
if (fetchedObjects == nil) { 
    //... error handling code 
} 

[fetchRequest release]; 
NSLog(@"End"); 
return fetchedObjects; 

Le nombre d'entrées dans la base de données un problème pour les données de base?


EDIT: Comme gcbrueckmann et jrturton ont fait remarquer, il est un bon point de mettre fetchBatchSize. Mais le temps chercher est encore peu satisfaisante:

  • 2 secondes avec un ensemble de prédicats:

    NSPredicate * prédicat = [NSPredicate predicateWithFormat: @ "longueur ==% d et la langue BEGINSWITH% @", longueur de mot, lng ] [fetchRequest setPredicate: predicate];

  • 7 secondes avec l'ensemble de la taille du lot:

    [fetchRequest setFetchBatchSize: 1];

  • 1 seconde avec une fois le prédicat et la taille des lots mis

Est-il encore un autre goulot d'étranglement?

+0

dans votre prédicat est la langue peut-être plus restrictive que la longueur, parfois l'ordre des contrôles sous-jacentes peuvent accélérer les choses aussi bien. par exemple dans ce cas si 60% des mots répondaient à vos critères de longueur mais que seulement 40% répondaient aux critères de langue, il serait préférable d'avoir la vérification de la langue en premier. L'autre chose pourrait être si vous avez besoin de cela plus rapidement serait de l'avoir pré-chargé, puis filtrer un tableau en mémoire pas sûr si votre application iphone peut gérer cela si. –

+0

Dans ce cas, la première requête compare des entiers (l'indexation rendra cela très rapide) et la seconde est une comparaison de chaînes (même une chaîne indexée ne sera pas rapide) - Je serais surpris si le réordonnancement de la requête aidait. Cependant, essayez-le - je serais intéressé de voir si cela a aidé! – deanWombourne

+0

Oh, j'ai oublié de le mentionner: j'ai déjà essayé d'échanger l'ordre du prédicat, ça n'accélère pas le fetch. – Norbert

Répondre

11

Puisque vous ne limitez pas le jeu de résultats de quelque manière que ce soit, l'extraction de 400 000 objets à la fois sera certainement un fardeau pour les données de base. Vous pouvez améliorer les performances de plusieurs façons:

La modification de la demande d'extraction fetchBatchSize limite le nombre d'objets que l'extraction conservera en mémoire à la fois. Cette fonctionnalité est complètement transparente pour votre application, donc ça vaut vraiment le coup d'essayer.

Si vous n'avez pas besoin d'objets à part entière, vous pouvez envisager de modifier la valeur resultType de la requête d'extraction à une valeur plus appropriée. Surtout si vous n'êtes intéressé que par certaines des valeurs d'un objet, l'utilisation de NSDictionaryResultType est une bonne idée. Les propriétés fetchLimit et fetchOffset permettent de limiter la plage de résultats si vous souhaitez gérer vous-même le traitement par lots. C'est une bonne idée si votre manipulation de chacun des objets résultat utilise beaucoup de mémoire car vous pouvez envelopper chaque lot dans un NSAutoreleasePool (ne soyez pas tenté de créer un pool de libération automatique pour chaque objet résultat).

Je suppose que 1 sec. peut être aussi rapide que cela se produit dans votre cas - même si vous avez recours à une base de données Sqlite simple. La seule optimisation supplémentaire que je peux penser est d'utiliser une table par langue (au lieu de mettre des mots de toutes les langues dans une seule table). Ceci, bien sûr, ne fonctionnera qu'avec Sqlite que si vous définissez des entités séparées pour toutes les langues, i. e. Prenez votre entité Words telle quelle et faites-la abstraire. Ajoutez ensuite des sous-entités telles que EnglishWord etc. Les objets provenant d'entités différentes sont stockés dans des tables distinctes. Donc, combiné avec les paramètres fetchBatchSize et predicate cela devrait fonctionner de manière similaire à l'approche Sqlite avec des tables séparées pour toutes les langues.

+0

'fetchBatchSize' est certainement un bon point. Mais malheureusement, il faut encore 2 secondes pour saisir un mot. – Norbert

+0

Est-ce que l'utilisation d'une base de données Sqlite de base est une option dans votre cas? Il semble que les objets existants ne sont pas modifiés, donc les données de base n'auront probablement pas (m) d'avantages par rapport à Sqlite. 400 000 est vraiment un grand ensemble de données sur l'iPhone. Serait-ce une option d'avoir une table par langue? – gcbrueckmann

+0

Oui, je pensais déjà à revenir à SQLite, mais je pensais qu'il pourrait encore y avoir un goulot d'étranglement que je ne vois pas. – Norbert

1

Cela ramènerait votre base de données complète 400k dans la mémoire qui semble beaucoup. Vous pouvez enquêter sur

setFetchBatchSize 
méthode

qui arrête le cadre de retourner des objets pleins pour tout dans votre demande de récupération, en supposant que vous n'avez pas besoin chaque objet retourné à être extraite du magasin en première instance de NSFetchRequest.

2

Vous faites BEGINSWITH - ce n'est pas une opération très rapide! Cependant, il y a un nombre fini de langues donc un emum aiderait probablement. Avoir un champ language_id qui est un entier indexé et l'utiliser dans votre prédicat.

Vous pouvez toujours enregistrer le nom de la langue ainsi et le retourner dans le cadre de l'objet récupéré, il suffit de ne recherche pas sur elle :)


PS Vous pouvez activer le débogage SQL en ajoutant « -com.apple. CoreData.SQLDebug 1 'en tant qu'argument passé au lancement (configurez ceci dans votre Scheme) - ceci pourrait vous aider à voir ce que SQL fait derrière la scène.

(voir this question pour plus de détails)

+0

'langue BEGINSWITH% @' a pris 600ms (moy); 'language ==% @' a pris 350ms (moy)! – Norbert

+1

Je crois que, dans ce sens, j'ai également vu des choses comme faire une comparaison de language> =% @ comme étant plus rapide qu'avec BEGINSWITH. Je veux dire que c'était dans les vidéos de données de base 2010 de la WWDC. –

+0

Si c'est encore l'égalité des chaînes, alors vous allez obtenir encore plus d'accélération si vous convertissez en comparant des entiers;) L'indexation de chaîne en SQL considère seulement un certain nombre de caractères dans la chaîne alors qu'indexer un entier est idéal! - http://dev.mysql.com/doc/refman/5.0/fr/create-index.html – deanWombourne