views:

20

answers:

1

Hi.

I'm creating an iphone/ipad app that basically reads XML documents and creates tableviews from objects created based on the xml. The xml represents a 'level' in a filesystem. Its basically a browser.

Each time i parse the xml documents i update the filesystem which is mirrored in a core-data sqllite database. For each "File" encountered in the xml i attempt to get the NSManagedObject associated with it.

The problem is this function which i use to get/create either a new blank entity or get the existing one from database.

+(File*)getOrCreateFile:(NSString*)remotePath 
        context:(NSManagedObjectContext*)context
 {
        struct timeval start,end,res;
        gettimeofday(&start,NULL);
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"File" inManagedObjectContext:context];
        [fetchRequest setEntity:entity];
        [fetchRequest setFetchLimit:1];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"remotePath == %@",remotePath];
        [fetchRequest setPredicate:predicate];

        NSError *error;
        NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
        [fetchRequest release];

        File *f;
        if ([items count] == 0) 
            f = (File*)[NSEntityDescription insertNewObjectForEntityForName:@"File" inManagedObjectContext:context];
        else 
            f = (File*)[items objectAtIndex:0];

        gettimeofday(&end, NULL);
        [JFS timeval_subtract:&res x:&end y:&start];
        count++;
        elapsed += res.tv_usec;
        return f;

}

For eksample, if i'm parsing a document with 200ish files the total time on a iPhone 3G is about 4 seconds. 3 of those seconds are spent in this function getting the objets from core data.

RemotePath is a unique string of variable length and indexed in the sqllite database.

What am i doing wrong here? or.. what could i do better/different to improve performance.

A: 

Executing fetches is somewhat expensive in Core Data, though the Core Data engineers have done some amazing work to keep this hit minimal. Thus, you may be able to improve things slightly by running a fetch to return multiple items at once. For example, batch the remotePaths and fetch with a predicate such as

[NSPredicate predicateWithFormat:@"remotePath IN %@", paths];

where paths is a collection of possible paths.

From the results, you can do the searches in-memory to determine if a particular path is present.

Fundamentally, however, doing fetches against strings (even if indexed) is an expensive operation. There may not be much you can do. Consider fetching against non-string attributes, perhaps by hasing the path and saving the hash in the entity as well. You'll get back a (potentially) larger result set which you could then search in memory for string equality.

Finally, do not make any changes without some performance data. Profile, profile, profile.

Barry Wark
In the datamodel each folder has a one to many relationship with each child(folder/file). I ended up using [NSFetchRequest setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObject:@"children"] and this returned each child as a fault and then each time instead of calling the other function i searched the now in memory set of children using a NSPredicate. The 4 seconds from the original post is now 1.5ish.
Kim Bowles Sørhus