Big performance penalty for checking for NaN's or Infinity - by mviss

Status : 

  Fixed<br /><br />
		This item has been fixed in the current or upcoming version of this product.<br /><br />
		A more detailed explanation for the resolution of this particular item may have been provided in the comments section.

Sign in
to vote
ID 498934 Comments
Status Closed Workarounds
Type Bug Repros 0
Opened 10/20/2009 5:43:03 PM
Access Restriction Public


If you do math on arrays of double that contain large numbers of NaN's or Infinities, there is an order of magnitude performance penalty.

I first noticed the NaN problem when I had a large array of doubles, most of which were NaN, and I had a loop that was searching for the maximum value. With the array filled with NaNs, it took around 10s to search instead of ~300 ms.  When I looked at the disassembly, it was using the FCOMIP instruction rather than FUCOMIP.  The first raises the floating point exception for any type of NaN, while the second only raises the exception for signaling NaNs.  I’m not sure why this is done, as so far as I know .Net never throws floating point exceptions.  I’m not sure that FUCOMIP would fix the problem, but it seems likely.

As a workaround, I added a test for NaN to the search. However, this does not solve the problem as the .NET implementation of Double.IsNaN seems to be something like:

bool IsNaN(y)
      return y != y

This ends up using the same FCOMIP comparison instruction, thus even trying to check for NaN causes a major slowdown if lots of NaNs are present!

I ended up writing my own check for NaN:

      static public unsafe bool IsNaN(this double value)
         // Exponent of all ones, mantissa non-zero
         return ((*(ulong*)&value) & 0x7fffffffffffffffL) > 0x7ff0000000000000L;

It's not just the comparisons that have the performance problem. I've also seen it with other array operations, such as multiplying by a scalar.  

1) Make IsNaN() fast, like the one I suggest.
2) Don't use the instructions that raise floating point exceptions.
Sign in to post a comment.
Posted by Microsoft on 11/17/2009 at 1:53 PM
Thanks Marlin. The IsNan suggestion is excellent. We're making a change to replace IsNaN(d){return d!=d;} with a bit pattern check. Unfortunately, the fix won't be available in .NET 4 but it should be available in a post .NET 4 release.

Thanks again.
Kevin Frei
CLR Codegen Team
Posted by Josh [MSFT] on 11/17/2009 at 1:30 PM
Thank you for the feedback. We have improved the performance of the Single.IsNaN and Double.IsNaN routines for a future release of the .NET Framework.

Josh Free
Base Class Library Development
Posted by mviss on 11/10/2009 at 8:54 AM
Kevin, my original testing was done on a Pentium 4. If the use of FUCOM causes a slowdown on normal FP values, than I agree with your assessment. I would still urge you to consider request #1: making the IsNaN faster.

Marlin Viss
Posted by Microsoft on 11/9/2009 at 8:25 PM
Thanks for the feedback. I put together a test using some assembly code and found that while FUCOM is dramatically (60-100x) faster than FCOM for comparing NaN’s on an Intel Core i7, it is not any faster than FCOM on a Core 2 (both instructions are 60-100x slower when operating on NaN’s), nor an AMD Phenom (both instructions are 20-30% slower when operating on NaN’s). And FUCOM is significantly (20-30%) slower than FCOM for non-NaN values. Thus your request would cause the common case to get quite a bit slower, while the rare case would only really improve for Core i7’s, which are just not that common right now. Even if Core i7’s were the majority of the market, penalizing the common case of normal FP values by 20-30% to improve the performance of NaN comparison doesn’t seeem to make a lot of sense for a general purpose JIT code generator.

Kevin Frei
CLR Codegen Team
Posted by Microsoft on 10/22/2009 at 3:21 AM
Thank you for your feedback, We are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(