RestKit/RestKit — pull request #2086 by carllindberg (2014-11-12 22:07:14+00:00)
Title: Use local variables to avoid repeated calls to methods

While profiling #2065, unsurprisingly objc_msgSend was a good percentage of the time. That is to be expected for any Objective-C code :-) However, there were three methods that called it a good more often than any others -- RKMappingOperation's two applyAttributes method and its main method.

While a method call is fast, it does take a little bit of time. In the test app, there are about 5000 objects, but since each of those has some child objects, the total number of objects mapped is about 60000. An RKMappingOperation gets allocated to hold the state for each of those mappings (which means a couple of other objects get allocated as well). Everything in that class is basically a hotspot. And since each object has multiple attributes (for the test app, between 3 and 4 on average), the attribute method gets called even more, closer to 200,000 times (194793 to be exact). The applyAttributeMappings method will loop that many times. Anything that gets apply to each attribute mapping is generally the hottest of the RestKit hotspots and should be optimized the most.

According to the profiler, the -applyAttributeMappings: method was spending 0.27 seconds just with objc_msgSend overhead (that does not count the implementation that it ends up calling), applyAttributeMapping:withValue: was 0.227 seconds, and the -main method was 0.195 . In looking at the implementations, there were places where the code was calling the same property method multiple times, and that sort of thing. Simply introducing a local variable to call the method once, then re-use the local variable, could then cut down on the number of method calls a lot. In most code, you won't measure it, but if a method is called 200000 times and you save say 10 method calls in the implementation, that is now a couple of million method calls avoided. Do that for three methods, it adds up more. At some point it does become measurable, so for extreme hotspots this sort of thing can really matter.

I added some local variables (not that many) for places where it looked like we were calling the same method multiple times in the common path (debug / trace log statements, error handling code, and other rare situations I didn't bother changing unless I created a local variable for other reasons). I did the three methods listed above, and a couple of relationship mapping methods as well, though the profiler didn't calls those out as much. It was a measurable increase. It's possible doing this in other places could also help, though I think these are the biggest bang.

After these, I see one method went from 0.27 to 0.185, and another went from 0.227 to 0.114 in the profiler.

Timings before:
(Device) Mapping 5000 students with relationship mapping: 15.021802
(Simulator) Mapping 5000 students with relationship mapping: 2.087246

and after:
(Device) Mapping 5000 students with relationship mapping: 14.274493
(Simulator) Mapping 5000 students with relationship mapping: 1.967516