Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to minimize the costs for allocating and initializing an NSDateFormatter?

I noticed that using an NSDateFormatter can be quite costly. I figured out that allocating and initializing the object already consumes a lot of time.
Further, it seems that using an NSDateFormatter in multiple threads increases the costs. Can there be a blocking where the threads have to wait for each other?

I created a small test application to illustrate the problem. Please check it out.

  • http://github.com/johnjohndoe/TestNSDateFormatter
  • git://github.com/johnjohndoe/TestNSDateFormatter.git

What is the reason for such costs and how can I improve the usage?


17.12. - To update my observation: I do not understand why the threads run longer when processed parallel compared to when the run in serial order. The time difference only occurs when NSDateFormatter is used.

like image 459
JJD Avatar asked Dec 14 '10 17:12

JJD


4 Answers

Note: Your example program is very much a micro-benchmark and very effectively maximally amplifies that cost of a date formatter. You are comparing doing absolutely nothing with doing something. Thus, whatever that something is, it will appear to be something times slower than nothing.

Such tests are extremely valuable and extremely misleading. Micro-benchmarks are generally only useful when you have a real world case of Teh Slow. If you were to make this benchmark 10x faster (which, in fact, you probably could with what I suggest below) but the real world case is only 1% of overall CPU time used in your app, the end result is not going to be a dramatic speed improvement -- it will be barely noticeable.

What is the reason for such costs?

NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyyMMdd HH:mm:ss.SSS"];

Most likely, the cost is associated with both having to parse/validate the date format string and having to do any kind of locale specific goop that NSDateFormatter does. Cocoa has extremely thorough support for localization, but that support comes at a cost of complexity.

Seeing as how you wrote a rather awesome example program, you could fire up your app in Instruments and try the various CPU sampling instruments to both understand what is consuming CPU cycles and how Instruments works (if you find anything interesting, please update your question!).

Can there be a blocking where the threads have to wait for each other?

I'm surprised it doesn't simply crash when you use a single formatter from multiple threads. NSDateFormatter doesn't specifically mention that it is thread safe. Thus, you must assume that it is not thread safe.

How can I improve the usage?

Don't create so many date formatters!

Either keep one around for a batch of operations and then get rid of it or, if you use 'em all the time, create one at the beginning of your app's run and keep around until the format changes.

For threading, keep one per thread around, if you really really have to (I'd bet that is excessive -- that the architecture of your app is such that creating one per batch of operations will be more sensible).

like image 162
bbum Avatar answered Jan 01 '23 03:01

bbum


I like to use a GCD sequential queue for ensuring thread safety, it's convenient, effective, and efficient. Something like:

dispatch_queue_t formatterQueue = dispatch_queue_create("formatter queue", NULL);
NSDateFormatter *dateFormatter;
// ...
- (NSDate *)dateFromString:(NSString *)string
{
    __block NSDate *date = nil;
    dispatch_sync(formatterQueue, ^{
        date = [dateFormatter dateFromString:string];
    });
    return date;
}
like image 43
shawkinaw Avatar answered Jan 01 '23 03:01

shawkinaw


Using -initWithDateFormat:allowNaturalLanguage: instead of -init followed by -setDateFormat: should be much faster (probably ~2x).

In general though, what bbum said: cache your date formatters for hot code.

(Edit: this is no longer true in iOS 6/OSX 10.8, they should all be equally fast now)

like image 43
Catfish_Man Avatar answered Jan 01 '23 03:01

Catfish_Man


Use GDC dispath_once and you're good. This will ensure syncing between multiple threads and ensure that the date formatter is only created once.

+ (NSDateFormatter *)ISO8601DateFormatter {
    static NSDateFormatter *formatter;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        formatter = [[NSDateFormatter alloc] init];
        formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
    });
    return formatter;
}
like image 35
leviathan Avatar answered Jan 01 '23 01:01

leviathan