Monday, February 11, 2013

Destinations - KAYAK Hack Week Project 2012

One of the cool things about working at KAYAK is hack week. One week out of the year employees take a break from their regular duties to work on a fun project of their choosing. It is a great opportunity to work on something different and with people you might not otherwise work with. At the end of the week each group presents a short video describing what they did.

Last year I worked with Mike Bernardo, Ben Peebles-Mundy, and Stewart Ulm. We built Destinations - an iPhone app that lets you browse the vacation photos of your Facebook friends. It was a blast working with these guys on it. In just four days we had a working prototype and on the last day we produced an Apple-like commercial for it. I was super proud of the work we did on this one. Check out the video.




Wednesday, February 6, 2013

JSONTransformer - Storing objects in Core Data as JSON

Core Data allows you to store a handful of primitive types (integers, decimals, strings, dates, etc..) but you can also store more complex objects by using the attribute type "transformable". Such objects are persisted by using a "transformer" to convert it to and from NSData (which is what actually gets stored).

Core Data's default transformer is based on archiving / NSKeyedArchiver and as my last post showed NSKeyedArchiver can be incredibly slow compared to other persistence techniques such as JSON-ifying.


Luckily Core Data allows you write custom transformers so you can use any technique you like to convert your objects to and from NSData. It is quite trivial then to write a simple transformer that converts between JSON using JSONKit.


#import "JSONTransformer.h"
#import "JSONKit.h"

@implementation JSONTransformer

+ (BOOL) allowsReverseTransformation {
    return YES;
}

+ (Class) transformedValueClass {
    return [NSData class];
}

- (id) transformedValue:(id)value {
    return [value JSONData];
}

- (id) reverseTransformedValue:(id)value {
    return [value objectFromJSONData];
}

@end


If you are storing an NSDictionary or NSArray in Core Data I'd expect this transformer to give your app performance gains similar to those described in my last post over the default archiving transformer. Specifically for large data I'd expect you to see roughly:
  • 3X reduction in storage size
  • 3X read improvement
  • 10-15X write improvement
Remember that for this to work your NSDictionary or NSArray must contain only simple objects that can be represented as JSON.


Monday, February 4, 2013

Persisting Objects: NSArchiver vs JSON

There are many ways to store data persistently in iOS. One common technique is to archive (serialize) objects using an NSArchiver. Archiving has always seemed slower than I expected and recently I began to wonder if it might be faster (or slower) to instead convert an object to JSON and write that out instead?

Tonight I ran a simple experiment to get a rough and unscientific approximation. You should consider this anecdotal.

The test was pretty straightforward. First I loaded up an NSDictionary with lots and lots of data and then measured the time it takes to write it and read it using both archiving and JSON-ifying. I also measured the size of the output files to see how they compare.

For archiving I simply used +[NSKeyedArchiver archiveRootObject:(id)rootObject toFile:(NSString *)path] and +[NSKeyedArchiver unarchiveObjectWithFile:(NSString *)path];

For JSON I used JSONKit and basic methods to read and write an NSString to and from a file.

The difference in the results was much larger than I expected. Using JSON was the clear winner in my tests. First it produced a file that was 3X smaller than the binary archive. Reading the data was about 3X faster too. The largest performance gain came from writing the data. Converting an object to JSON and writing it out was about 10-15X faster than using NSKeyedArchiver.

Below are the results on 4 different pieces of hardware (one of which was the iPhone simulator on a MBP).

The bottom line here is that if you are archiving "simple" objects (objects that can be easily represented as JSON, no cycles, etc...) you should strongly consider storing JSON instead of archiving.


Size of output file - 3X improvement

NSArchiver JSON
28 MB 10 MB


Time to write to disk - 10-15X improvement

NSArchiver (archive to file) JSON (convert object to JSON + write to file)
iPhone Sim on MBP 3.58 seconds .23 seconds
iPhone 5 19.68 seconds 1.29 seconds
iPhone 4S 36.66 seconds 3.3 seconds
iPhone 4 62.65 seconds 4.51 seconds


Time to read from disk - 3X improvement

NSArchiver (unarchive from file) JSON (read file + convert JSON to object)
iPhone Sim on MBP 1.4 seconds .4 seconds
iPhone 5 7.84 seconds 2.78 seconds
iPhone 4S 15.29 seconds 5.83 seconds
iPhone 4 25.18 seconds 7.9 seconds