开发者

Fast integer ABS function

int X = a-b;
int d = Math.Abs(X);

I am pretty sure that .NET doesn't do inlini开发者_开发百科ng. So, will I do if(), or is there some other less-known trick?


I did some performance tests, to find out whether you can actually save time using something besides the standard Math.Abs.

The results after executing all of these 2000000000 times (with i from -1000000000 to +1000000000, so without overflows):

Math.Abs(i)                    5839 ms     Factor 1
i > 0 ? i : -i                 6395 ms     Factor 1.09
(i + (i >> 31)) ^ (i >> 31)    5053 ms     Factor 0.86

(These numbers vary a bit for different runs)

Basically you can get a very slight improvement over Math.Abs, but nothing spectacular.

With the bit hack you can shave of a little of the time required for Math.Abs, but readability suffers severely.
With the simple branch you can actually be slower. Overall not worth it in my opinion.

All tests where run on a 32 bit OS, Net 4.0, VS 2010, Release mode, no debugger attached.

Here is the actual code:

class Program
{
    public static int x; // public static field. 
                         // this way the JITer will not assume that it is  
                         // never used and optimize the wholeloop away
    static void Main()
    {
        // warm up
        for (int i = -1000000000; i < 1000000000; i++)
        {
            x = Math.Abs(i);
        }

        // start measuring
        Stopwatch watch = Stopwatch.StartNew();
        for (int i = -1000000000; i < 1000000000; i++)
        {
            x = Math.Abs(i);
        }
        Console.WriteLine(watch.ElapsedMilliseconds);

        // warm up
        for (int i = -1000000000; i < 1000000000; i++)
        {
            x = i > 0 ? i : -i;
        }

        // start measuring
        watch = Stopwatch.StartNew();
        for (int i = -1000000000; i < 1000000000; i++)
        {
            x = i > 0 ? i : -i;
        }
        Console.WriteLine(watch.ElapsedMilliseconds);

        // warm up
        for (int i = -1000000000; i < 1000000000; i++)
        {
            x = (i + (i >> 31)) ^ (i >> 31);
        }

        // start measuring
        watch = Stopwatch.StartNew();
        for (int i = -1000000000; i < 1000000000; i++)
        {
            x = (i + (i >> 31)) ^ (i >> 31);
        }
        Console.WriteLine(watch.ElapsedMilliseconds);


        Console.ReadLine();
    }
}


The JIT performs inlining in some circumstances. I don't know whether it inlines Math.Abs or not... but have you verified that this is actually a performance problem for you? Don't micro-optimize until you know that you need to, and then measure the performance gain from something like:

int d = X > 0 ? X : -X;

to verify that it's really worth it.

As noted by Anthony, the above won't (normally) work for int.MinValue, as -int.MinValue == int.MinValue, whereas Math.Abs will throw an OverflowException. You can force this in the straight C# as well using checked arithmetic:

int d = X > 0 ? X : checked(-X);


For what it's worth, absolute value of a 32-bit signed, 2's complement format int is usually implemented like this:

abs(x) = (x^(x>>31))-(x>>31)


I just see if it is less than zero and multiply by -1

int d = (X < 0) ? (-X) : X;


See http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs for how to compute absolute value without branching.

Whilst .Net supports inlining, I doubt that Math.Abs() would be considered a candidate for inlining by the compiler. Here's the implementation of the int overload, courtesy of Reflector.

public static int Abs(int value)
{
  if (value >= 0)
    {
      return value;
    }
  return AbsHelper(value);
}

private static int AbsHelper(int value)
{
  if (value == -2147483648)
  {
    throw new OverflowException(Environment.GetResourceString("Overflow_NegateTwosCompNum"));
  }
  return -value;
}

The overloads for other integral types are similar. The float and double overloads are external calls whilst the decimal overload uses its own implementation, which constructs a new instance. Ouch!


C# does inline Math.Abs. This works:

int x = 12;
int y = 17;
int z = Math.Abs(x - y);
Console.WriteLine(z); //outputs 5


C# does inline Math.Abs(), here is the C# and assembly code (generated using the online tool SharpLab) of Math.Abs:

C#:

public int test(int n){
    return Math.Abs(n);
}

Assembly:

L0000: push ebp
L0001: mov ebp, esp
L0003: test edx, edx
L0005: jge L000d
L0007: neg edx
L0009: test edx, edx
L000b: jl L0011
L000d: mov eax, edx
L000f: pop ebp
L0010: ret
L0011: call System.Math.ThrowAbsOverflow()
L0016: int3


If you know that it is about the difference in for example a minimization problem you could use: a<b?b-a:a-b

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜