8

What is the performance of testing whether a reference-type variable in C# is a null-pointer (like if (x == null) ...) compared to testing for an integer being smaller than zero or even a bool being false?

Are there other issues know regarding such null-pointer tests, e.g. is garbadge produced?

I do hundred of these tests for every frame of a game and I was wondering if these could cause problems or could be implemented more efficiently?

spajce
  • 7,044
  • 5
  • 29
  • 44
ares_games
  • 1,019
  • 2
  • 15
  • 32
  • Why there is an `xna` tag? – Soner Gönül Jan 11 '13 at 12:53
  • I do not expect much of a difference, my guess is that you will optimize more/better by not repeating the same test redunantly than by replacing reference by int checks. – Emond Jan 11 '13 at 12:55
  • 1
    You should try it, just write some performance tests – ken2k Jan 11 '13 at 12:55
  • 4
    "Premature optimization is the root of all evil.".... If I hear that quote one more time.... – Inisheer Jan 11 '13 at 12:55
  • 2
    @venneto The OP has stated he is making a game in C#. Therefore, it is CRITICAL that you do not create garbage each frame as it causes the GC to run. Therefore, you could consider this NOT to be premature optimization as this could directly change a design decision. – Inisheer Jan 11 '13 at 12:57
  • 1
    [Why are you even asking us?](http://ericlippert.com/2012/12/17/performance-rant/) – SWeko Jan 11 '13 at 12:58
  • @Inisheer but before a n analysis shows this to be the bottle neck it is premature to optimize it. Eric Lippert who knows the compiler inside out claims to be hopelessly wrong 1/3 of the time when guessing at performance issues – Rune FS Jan 11 '13 at 13:00
  • 1
    @Inisheer: Yes, exactly! Possible performance problems are secondary. The garbadge-production part of my question is also what justifies the XNA tags ;-) I am currently tracking down the last bytes of memory that are allocated during gameplay... – ares_games Jan 11 '13 at 13:02
  • @SWeko: Sometimes there are inherent and obvious performance differences between way you implement things. I am not fully aware how a null-pointer test is carried out internally so I was not sure about that, so I asked. Of course I could replace all tests in my source code and measure the difference by myself but that would take me days. – ares_games Jan 11 '13 at 13:06
  • 2
    @pad_ares: 1. Make it. 2. Make it run. 3. Make it run fast. In that order :) – SWeko Jan 11 '13 at 13:12
  • 5
    I think a lot of these people who are saying to optimize later have never designed a game in a managed language. – Inisheer Jan 11 '13 at 13:25
  • @SWeko: I think it makes perfect sense to already early decide as wisely as possible how to implement certain things. To me this deas not seem to be premature but can save a lot of work down the road (at least I made this experience). – ares_games Jan 11 '13 at 16:54

6 Answers6

14

Nullity tests are likely to be equivalent to simple "equal to 0" tests. They're very, very cheap - mere hundreds per frame should be completely insignificant unless you've got a frame rate in the millions :)

You should profile your app to find out where the time is actually being spent - it's a lot more productive than just guessing. Ideally, you should also try to write some benchmarks so that you can not only measure current performance, but also notice if it gets significantly worse due to any particular change.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • excellent point about tracking the performance and not just measurring it from time to time – Rune FS Jan 11 '13 at 13:01
  • if this branch is always false or true, its performance impact will be greatly diminished by dynamic branch prediction. so I never hesitate to put these kind of safety checks. @ares_games – M.kazem Akhgary Jan 09 '18 at 00:10
4

Testing a value for null is not a complicated operation that needs type checking or something like that, and there is no memory allocation involved.

Disassembling the statement if (x == null) gives:

00000030  cmp         qword ptr [rsp+20h],0
00000036  jne         000000000000004A

I.e. the test is implemented as a simple integer comparison of the pointer value.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
2

Doing intensive checks like you are may have some performance degradation, however, that can only be measureable by your own standards against your particular app.

Based on what the point of the test is, there is possibly other ways you could be smarter about what & when you check. However, without knowing more about your application it would be too difficult to try conjure up an answer to this.

James
  • 80,725
  • 18
  • 167
  • 237
  • I see no evidence of *intensive* checks here. – Jon Skeet Jan 11 '13 at 12:57
  • @JonSkeet "*I do hundred of these tests for every frame of a game*" - I would say that classifies as *intensive* based on how many frames there are per second. – James Jan 11 '13 at 12:59
  • I disagree. Do you have any *idea* how much work most games are likely to do per frame? 100 very cheap tests is unlikely to be significant *at all* unless you're expecting a frame rate in the hundreds of thousands. Suppose it's 200fps. That's 20,000 nullity checks per second. Even on a very low-powered computer, that's not going to be significant. – Jon Skeet Jan 11 '13 at 13:05
  • @JonSkeet 20,000 nullity checks per second (for one small part of an app) is intensive **IMO** - yours obviously differs. However, I don't write game applications which is why I said "*that can only be measureable by your own standards against your particular app*". My point being - there might be smarter ways of doing things. – James Jan 11 '13 at 13:12
2

Probably subjective - but a null check is equivalent an equal-to-zero check, and just as quick. So I don't think you should be worrying about this.

Equally - unless you have performance problems why bother playing with it.

Also equally, if you do have performance problems, it's most likely that you'll be able to garner performance from complex branches of code rather than eliminating a few null checks.

That said, for conditional code, a potential performance improvement (however it would seriously need to be benchmarked) might be to use delegates for the different branches of logic that are set as the result of one or more conditions changes - but I'd be surprised if such a solution actually improves performance in the general case - especially for your 'is null' scenario. So, by that I mean something like this:

if([condition])
{
  Foo();
}
else
{
  Bar();
}

If, say, [condition] involves a local variable _obj (in your case _obj == null) - you can replace with something like this (but be very very wary of threading issues):

private Action _logic;
private object _obj;
public Object Obj {
  get { return Obj; }
  set {
    _obj=value;
    if([condition])
      _logic = () => Foo();
    else
      _logic = () => Bar();
  }
}

And now in any code where you previously have checked [condition] to do the branching you now simply do:

_logic();

This kind of thing gains most improvements when the [condition] is complex and, crucially, has been proven to be taking up a lot of processor time through profiling. Using delegates will also carry a slight overhead over the conditional branching, but if that overhead is less then the execution of the [condition] then it can make a difference, especially if those checks are being performed very frequently.

There are other variations of this too, most commonly function lookup tables derived from a value instead of choosing a branch of code based on an equality check (which is how large switch/case statements can be implemented - delegates added to a Dictionary keyed by the enum/value being checked - which avoids multiple checks of the value).

Ultimately, though, without the due-diligence of profiling (before and after, of course), carrying out such optimisations is fundamentally pointless.

Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
1

No problems (performance or otherwise) with if (x == null).

Henrik
  • 23,186
  • 6
  • 42
  • 92
1

This should absolutely be no issue - all test you mention will usually take a single clock cycle. If there is a performance impact by conditional branches it is usually caused by unpredictable or at least hard to predict branching behaviour fouling the branch predictor and requiring abortion of the speculatively executed branch.

Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143