[OT] Re: Cocoa and messages to nil, revisited
[OT] Re: Cocoa and messages to nil, revisited
- Subject: [OT] Re: Cocoa and messages to nil, revisited
- From: Alastair Houghton <email@hidden>
- Date: Wed, 8 Aug 2007 12:12:09 +0100
On 8 Aug 2007, at 08:41, Marc Wan wrote:
and i'll say somethng mildly provocative here: if your
application's performance is so borderline that the 50 (pulled that
number out of a hat) instructions
It's far fewer than that. On PowerPC (which happened to be
convenient for me
to look at), it's 12 instructions or so.
(x 3, of course) that you're saving
on a 2GHz processor are so critical to your performance, you have way
more serious design issues that you should be focusing on.
On 8 Aug 2007, at 06:47, Daniel Jalkut wrote:
Performance on the level you describe is generally not worth
considering. (It's not worth it for the "people" who use nil
messaging as a pro-performance argument, either!)
On 8 Aug 2007, at 00:35, Jeff Laing wrote:
(As a performance junkie, its disappointing that people still think
that its
quicker to just send the message to nil, than to do "if (target)
[target
message];" since it doesn't matter *how* fast you make that message
dispatcher, it can't possibly be faster than the two instructions
that the
conditional takes, especially since deep in its bowels we can see the
message dispatcher doing the exact same test, but then taking a
much longer
execution path in either case)
Both Daniel and Marc are right; it's almost totally pointless from a
performance perspective. It's very unlikely that you will ever need
the extra performance, or indeed that you would gain overall if you
used this pattern throughout your application. But rather than
assuming, let's test it. Here are some numbers:
My Mac Pro (3GHz Dual-Core Xeon) reports the following:
Message sends take 5.34852 ns.
Nil sends take 6.01709 ns.
IMP sends take 2.67442 ns.
Function calls take 2.00583 ns.
Dylib calls take 2.67425 ns.
Virtual function calls take 2.33997 ns.
(These numbers are for a call and return to an empty function/method,
averaged over 10 billion calls [yes, billion].)
A nearby G5 (2.3GHz G5) reports:
Message sends take 15.6726 ns.
Nil sends take 9.14313 ns.
IMP sends take 4.35283 ns.
Function calls take 3.91874 ns.
Dylib calls take 7.83509 ns.
Virtual function calls take 5.87761 ns.
on the same code.
Obviously this is an artificial benchmark, but the long and short of
it is that message sends are fast. Not as fast as a plain C function
call, perhaps, though using an IMP you can get them to go as fast as
a call to a function in a dylib (on Intel), and faster than a call to
a function in a dylib (on PowerPC).
The difference between
if (foo) [foo bar];
and
[foo bar];
is very small; according to my measurements, on the G5 it increases
the time taken for a non-nil send by 1.3ns and reduces the cost of a
nil send by around 7.8ns. For this to be a net gain on the G5,
therefore, more than one in six of your message sends need to be to nil.
On the x86 machine, it increases the time taken for a non-nil send by
0.33ns and reduces the cost of a nil send by 5.7ns. For this to be a
net gain, more than one in seventeen of your message sends need to be
to nil.
Are one in six message sends in a typical application to a nil
object? I doubt it. How about more than one in seventeen? It's
possible, but in high-performance code (the only place this kind of
saving would ever matter) it seems unlikely.
Even if you removed the nil checks from the ObjC runtime, you're only
going to make the non-nil message sends faster by a *tiny* amount,
and for what? You're going to have to add tests *all over the place*
for nil, and performance critical code won't ever care anyway because
it will be using IMPs or C functions. So all you've done is made it
more likely that the end user will lose their data when a nil does
turn up unexpectedly... whereas your program might have behaved
oddly, but would probably have let them save their work, it will now
crash instead.
Perhaps you could have some sort of error handling routine that
triggered on a call to nil, but then the runtime needs to check for
nil again, so you've erased the saving. On top of that, you've made
all of your code more complicated in the process, and as a result
you'll now be paying a penalty because branch prediction won't work
as well with your thousands of "if" statements as opposed to the one
branch in the runtime.
Anyway, I think the above demonstrates more than adequately that
there is no credible performance argument for what you're proposing.
Frankly, even a lot of supposedly high-performance code doesn't need
to shave off the few nanoseconds that you might save, and that's only
if messages to nil are more common than the figures above, or if
you've modified the runtime *and* accept that messages to nil will
cause an outright crash.
And as Daniel said, the argument in the other direction, though it
has at least some justification (in that there will be fewer
conditional branches in ordinary code) is equally silly because you
simply don't need those few nanoseconds. In the few cases where you
might care, you won't be writing your code that way anyway.
On 8 Aug 2007, at 07:18, Jeff Laing wrote:
I don't want to discuss this in terms of language. Its not an
Objective-C issue, its a Cocoa issue, a style problem.
It *is* an Objective-C language issue, because it's a design
*feature* of the language that sending to nil is a no-op. It didn't
get that way by accident; it was designed that way so it could be
used exactly how Cocoa programmers (and the Cocoa frameworks) *are*
using it.
Even if it was a style issue, and even if you hadn't already been
told that it was off topic, it would still be obnoxious of you to
post it to the Cocoa list because style "issues" are the kind of
thing likely to trigger an argument.
The discussion about nil moved to the objc-lang list four days ago
(and has since mutated into a discussion about the pros and cons of
exceptions). You're most welcome to join in on that list if you like.
I *know* where I have had problems developing with the Cocoa
frameworks, and a large number of those problems were because
message to nil
could not be detected
If this is happening to you a lot, you are doing something wrong.
The most common reasons for this problem that I can think of are (a)
where you've declared e.g. an NSMutableArray member variable and
never actually initialised it, and (b) not connecting things in nib
files.
Both of these are mistakes that you can learn to avoid (for the most
part), and both of them can be caught by adequate testing. Certainly
I don't find this to be a major problem, or even a common problem, in
code I'm working on.
Kind regards,
Alastair.
p.s. To the rest of the people on the list: my apologies if this is a
bit long. Hopefully it is at least an interesting demonstration that
optimization, whether in a Cocoa app or otherwise, needs to be
carefully weighed-up rather than being based upon supposition.
--
http://alastairs-place.net
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden