HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Speeding up storage of about 220000 values in Core Data

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
valuescorestorage220000aboutspeedingdata

Problem

I use this code on the app's first run to save about 220000 values into core data. The file Navaids.txt has about 200000 and Waypoints.txt has about 20000 lines, where I get data from each line and put it in an entity in core data.

I am still kind of new to CD but would like to see how I can improve this code on app's first launch to make it as fast as possible. Right now it takes about 10 seconds to run on an iPad but would like to see how I can improve.

Let me know if I should provide samples of my .txt files or if you need to see any of my delegate class.

```
if (self.firstRun == TRUE) {
NSString* navaidPath = [[NSBundle mainBundle] pathForResource:@"Navaids" ofType:@"txt"];
NSString* navContent = [NSString stringWithContentsOfFile:navaidPath encoding:NSUTF8StringEncoding error:NULL];
NSArray *navContentLines = [navContent componentsSeparatedByString:@"\r\n"];

NSString* wayptPath = [[NSBundle mainBundle] pathForResource:@"Waypoints" ofType:@"txt"];
NSString* wayptContent = [NSString stringWithContentsOfFile:wayptPath encoding:NSUTF8StringEncoding error:NULL];
NSArray *wayptContentLines = [wayptContent componentsSeparatedByString:@"\r\n"];

@autoreleasepool {
for (int i = 0; i < [wayptContentLines count]; i++) {
@autoreleasepool {

NSArray *brokenFix = [[wayptContentLines objectAtIndex:i] componentsSeparatedByString:@"|"];

newContact = [NSEntityDescription
insertNewObjectForEntityForName:@"Waypoints"
inManagedObjectContext:context];

[newContact setValue: [brokenFix objectAtIndex:0] forKey:@"name"];
[newContact setValue: [brokenFix objectAtIndex:1] forKey:@"latitude"];
[newContact setValue: [brokenFix objectAtIndex:2] forKey:@"longitude"];

}
}
}

@autoreleasepool {
for (int i = 0; i < [navContentLin

Solution

Ten seconds for reading/writing nearly half a million lines from/to disk is actually relatively decent.

The autorelease pool doesn't actually slow us down. Autorelease pool is handled by the preprocessor. So is ARC. Instead of us manually writing our release/retains, ARC, or autorelease pool, will do this for us. However, I actually would make a change to how you're handling the autorelease pools so that you're minimizing the amount of information you're keeping in RAM at any given time. For example:

@autoreleasepool {
    // read waypoints from file into memory
    // loop through waypoints array putting them into core data
}

@autoreleasepool {
    // read navaids from file into memory
    // loop through navaids array putting them into core data
}


Make no reference to waypoints outside the first autorelease pool and no reference to navaids outside the second autorelease pool and you'll be using approximately half as much RAM throughout this method.

Though... a cleaner way might be to just put each in their own method and then the same is accomplished without autorelease pools (which again, will not slow down your code).

We can however speed up your code. If we use a forin loop rather than a traditional for loop the code will run faster as we'll overall be passing significantly fewer messages. For example:

for (NSString *waypoint in wayptContentLines) {
    NSArray *brokenFix = [waypoint componentsSeparatedByString:@"|"];

    newContact = [NSEntityDescription insertNewObjectForEntityForName:@"Waypoints"
                                               inManagedObjectContext:context];

    [newContact setValue: [brokenFix objectAtIndex:0] forKey:@"name"];
    [newContact setValue: [brokenFix objectAtIndex:1] forKey:@"latitude"];
    [newContact setValue: [brokenFix objectAtIndex:2] forKey:@"longitude"];
}


Now, instead of calling to wayptContentLines twice per iteration (nearly a million times) (once to check length, once to grab the object at index), we're instead calling to the array less than once per iteration (the exact amount varies).

A forin loop in Objective-C reads several objects into a buffer then deals with the objects in the buffer before reading the objects in. I think on average it reads in about 8 objects, so you go from 2/i to 1/8i messages to wayptContentLines. In English, a for loops probably sends about 16 times as many messages to the array as a forin loop would... and over 400,000+ iterations, thats a LOT of messages and it will take up a lot of time.

Code Snippets

@autoreleasepool {
    // read waypoints from file into memory
    // loop through waypoints array putting them into core data
}

@autoreleasepool {
    // read navaids from file into memory
    // loop through navaids array putting them into core data
}
for (NSString *waypoint in wayptContentLines) {
    NSArray *brokenFix = [waypoint componentsSeparatedByString:@"|"];

    newContact = [NSEntityDescription insertNewObjectForEntityForName:@"Waypoints"
                                               inManagedObjectContext:context];

    [newContact setValue: [brokenFix objectAtIndex:0] forKey:@"name"];
    [newContact setValue: [brokenFix objectAtIndex:1] forKey:@"latitude"];
    [newContact setValue: [brokenFix objectAtIndex:2] forKey:@"longitude"];
}

Context

StackExchange Code Review Q#64316, answer score: 5

Revisions (0)

No revisions yet.