Software:
News ToolsReg Shops |
Don't Forget The 'C' in Objective-CPart 2: Runtime efficiency issues in Mac Cocoa programmingPublished Friday 25th May 2007 16:07 GMT Last time round, we looked at the way an unnamed developer had used Cocoa routines to chop up a simple C-string in order to determine whether or not it contained a particular, named OpenGL extension name. A few folks pointed out that if, hypothetically speaking, some new extension name gets devised that happens to be a superstring of the extension name we're after, my simple This time, I want to dig a little deeper by looking at the code generated by gcc in response to your carefully crafted Objective-C code. I'm hoping to demonstrate that you will often get more than you bargained for. To begin with, consider a simple method invocation such as: NSCursor * cursor = [NSCursor crosshairCursor]; This returns an instance of the cross-hair cursor. On an Intel machine, this will generate the code shown below. Listing One 00001e60 movl 0x0000501c,%eax crosshairCursor 00001e65 movl %eax,0x04(%esp,1) 00001e69 movl 0x0000502c,%eax NSCursor 00001e6e movl %eax,(%esp,1) 00001e71 calll _objc_msgSend The The Objective-C message dispatcher is reasonably efficient, especially second time around. This is because once it's seen a particular message selector for a particular class; it caches the address of the corresponding method making subsequent lookups more efficient. Note: If you want to get a better idea of Rather than focusing on execution speed, I want to look here at the size of generated code. In Listing One, we've eaten up 22 bytes in making that method call. Not a big deal you might say, and you'd be right. But it amazes me how often I see code that looks like this: Str1 = [[NSUserDefaults standardUserDefaults] stringForKey: @"myStr"]; Int1 = [[NSUserDefaults standardUserDefaults] integerForKey: @"myInt"]; Float1 = [[NSUserDefaults standardUserDefaults] floatForKey: @"myFloat"]; ..etc.. This sort of code pattern is so ubiquitous in Cocoa programming that I expect to be universally criticised for criticising it. But I ask you: what's the point in spending another 22 bytes of code to retrieve the same If you've got a complex application that needs to initialise a whole slew of user preferences, you can easily chew up several hundred bytes with repeated calls to retrieve the shared defaults object. Personally, I'd make the call once, store the Of course, the nay-sayers will complain, "But we've always done it that way", "I've got a quad-core development machine", "Who cares about a few hundred extra bytes?", yada, yada, yada. That's fine. Presumably, those same folks sprinkle their code liberally with gratuitous calls to the Moving swiftly on, take a look at the seemingly innocent line of code below: NSRect insetRect = NSInsetRect ([self frame], 2.0, 2.0); The gcc compiler absolutely loves rectangles, or any data structure for that matter. Once you give it an Listing Two 00002902 movl 0x08(%ebp),%ecx 00002905 leal 0x00002754(%ebx),%eax frame 0000290b movl (%eax),%eax 0000290d leal 0xb8(%ebp),%edx 00002910 movl %eax,0x08(%esp,1) 00002914 movl %ecx,0x04(%esp,1) 00002918 movl %edx,(%esp,1) 0000291b calll _objc_msgSend_stret 00002920 subl $0x04,%esp 00002923 movl 0xb8(%ebp),%eax 00002926 movl %eax,0xe8(%ebp) 00002929 movl 0xbc(%ebp),%eax 0000292c movl %eax,0xec(%ebp) 0000292f movl 0xc0(%ebp),%eax 00002932 movl %eax,0xf0(%ebp) 00002935 movl 0xc4(%ebp),%eax 00002938 movl %eax,0xf4(%ebp) 0000293b leal 0xa8(%ebp),%edx 0000293e leal 0x00000728(%ebx),%eax 00002944 movl (%eax),%eax 00002946 movl %eax,0x18(%esp,1) 0000294a leal 0x00000728(%ebx),%eax 00002950 movl (%eax),%eax 00002952 movl %eax,0x14(%esp,1) 00002956 movl 0xe8(%ebp),%eax 00002959 movl %eax,0x04(%esp,1) 0000295d movl 0xec(%ebp),%eax 00002960 movl %eax,0x08(%esp,1) 00002964 movl 0xf0(%ebp),%eax 00002967 movl %eax,0x0c(%esp,1) 0000296b movl 0xf4(%ebp),%eax 0000296e movl %eax,0x10(%esp,1) 00002972 movl %edx,(%esp,1) 00002975 calll _NSInsetRect 0000297a subl $0x04,%esp 0000297d movl 0xa8(%ebp),%eax 00002980 movl %eax,0xd0(%ebp) 00002983 movl 0xac(%ebp),%eax 00002986 movl %eax,0xd4(%ebp) 00002989 movl 0xb0(%ebp),%eax 0000298c movl %eax,0xd8(%ebp) 0000298f movl 0xb4(%ebp),%eax 00002992 movl %eax,0xdc(%ebp) There are a few things to explain in the code listing above, which I'll highlight by referring to a hex address. To begin with, Cocoa methods such as This would be ok if gcc behaved itself, but – like I said – it loves fooling around with data structures. When the call to The rectangle then gets copied again (!) when it's moved onto the stack prior to the call to You're probably thinking that I'm cheating here; surely gcc doesn't really generate that much code, does it? Ok, I am cheating – a little. Listing Two was created with compiler optimisation turned off – it's the setting you'd get when using the Debug configuration under XCode. If you switch to the Release configuration and rebuild, the above code snippet will shrink to 118 bytes, which is better, but definitely not awesome. The compiler still insists on having NSRect-returning routines place the returned function result in one place, and then pointlessly copy it some place else. But at least with optimisation turned on, the compiler is smart enough to use the esi, ebx, ecx and edx registers as an intermediate store for the four rectangle fields, thus saving on some memory reads. Note: Incidentally, you'd be surprised just how many commercial applications are released using the Debug configuration. The majority of apps that I've peeked inside show the same sloppy code generation as is typified in Listing Two. What's the bottom line here? The biggest obstacle to runtime efficiency is that we're passing around the rectangle by value rather than by reference. As I've mentioned, the routines that return an Finally, here's a little teaser for you experienced Cocoa-heads. Going back to Listing One, you may notice that the "self" pointer (either a pointer to an object instance, or to a class if we're dealing with a class method such as 9 comments posted — Comment period finished Nice articlePosted: 05:26 26th May 2007 Here we go again.Posted: 06:02 26th May 2007 Actually, now that I think of it...Posted: 06:22 26th May 2007 Eureka? ......aka In the Beginning,Posted: 08:35 26th May 2007 RE: Quiz QuestionPosted: 21:56 27th May 2007
Track this type of story as a custom Atom/RSS feed or by email.
|
|
||||||||
Top 20 stories • All The Week’s Headlines • Archive • Search