Creating NSMutableArray with NSDictionary slow elements
This piece of code is a method that creates an array for use by several other classes. The input is an array from a CoreData selection of type NSDictionaryResultType.
The 3 fields are strings that I need to split into arrays, so the components are SeparatedByString.
The resulting array, _dataProductionArray, works fine --- BUT: This piece of code takes FULL 5 SECONDS to process about 32,000 records.
Any help pointing out the egregious bugs that are causing this slow performance would be greatly appreciated!
NSMutableArray *dataArray = [NSMutableArray array];
int j = 0;
int maxNumMonths = 0;
for (id obj in _dictionaries) {
if ([_dictionaries[j] [@"month"] length] >0 ) {
// get production values
NSArray *aItems = [_dictionaries[j] [@"prodA"] componentsSeparatedByString:@","];
NSArray *bItems = [_dictionaries[j] [@"prodB"] componentsSeparatedByString:@","];
NSArray *monthItems = [_dictionaries[j] [@"month"] componentsSeparatedByString:@","];
NSMutableArray *productionAArray = [NSMutableArray array];
NSMutableArray *productionBArray = [NSMutableArray array];
int monthLoop = 1;
for (NSNumber *month in monthItems) {
if (monthLoop <= MONTHS_OF_PRODUCTION) {
if ([month intValue] == monthLoop) {
[productionAArray addObject:[aItems objectAtIndex:monthLoop-1]];
[productionBArray addObject:[bItems objectAtIndex:monthLoop-1]];
productionCount ++;
if (monthLoop > maxNumMonths)
maxNumMonths = monthLoop;
}
}
monthLoop++;
}
NSDictionary *arrayItem = @{@"name":_dictionaries[j] [@"name"],
@"type":[NSString stringWithFormat:@"%@",_dictionaries[j] [@"type"]],
@"height":[NSString stringWithFormat:@"%@",_dictionaries[j] [@"height"]],
@"aArray":productionAArray,
@"bArray":productionBArray,
};
[dataArray addObject:arrayItem];
}
j++;
}
_dataProductionArray = [NSArray arrayWithArray:dataArray];
source to share
I can see a few optimizations you could make in the loop, but I'm not sure how much that helps (especially if the compiler does them anyway). The root problem is that 32k is a lot of iterations.
Need all 32k results at once? You could make a significant improvement in the user experience by doing this work lazily , since the user interface requires a transformed entry.
This approach was to make dataProductionArray a volatile dictionary indexed by NSNumber. Then instead of ...
// replace this
self.dataProductionArray[128];
// with this
[self dataProductionAtIndex:@128];
This new getter method calls the code you wrote lazily, like ...
- (id)dataProductionAtIndex:(NSNumber *)index {
// replace dataProductionArray with dataProductionDictionary
id result = self.dataProductionDictionary[index];
if (!result) {
result = [self getDataAt:index];
self.dataProductionDictionary[index] = result;
}
return result;
}
Then getDataAt:
is a simple refactor of the code you posted, except instead of looping through 32k elements, it only works for one index that is passed ...
- (id)getDataAt:(NSNumber *)index {
int j = [index intValue];
// no loop, just skip to iteration j
NSArray *aItems = [_dictionaries[j] [@"prodA"] componentsSeparatedByString:@","];
NSArray *bItems = [_dictionaries[j] [@"prodB"] componentsSeparatedByString:@","];
// and so on, then at the end, don't save arrayItem, just return it
NSDictionary *arrayItem = @{@"name":_dictionaries[j] [@"name"],
@"type":[NSString stringWithFormat:@"%@",_dictionaries[j] [@"type"]],
@"height":[NSString stringWithFormat:@"%@",_dictionaries[j] [@"height"]],
@"aArray":productionAArray,
@"bArray":productionBArray,
};
return arrayItem;
}
PS - Volatile vocabulary is a good data structure for lazy evaluation. The next level of complexity is NSCache, which acts like a mutable dictionary and also manages memory ( ref class here ).
source to share
Your for loop is inactive. Just write
for (NSDictionary* dict in _dictionaries)...
and use dict instead of _dictionaries [j]. One method call is saved each time.
stringWithFormat:
creates a new line every time. Can't you just add the element and not turn it into a string?
Instead of fetching all the elements in productionAArray
and out productionBArray
, create NSIndexSet
, fill it in a loop - or better yet, using a block - and create the arrays in one go.
source to share