8

I'm working on a n image processing library which extends OpenCV, HALCON, ... . The library must be with .NET Framework 3.5 and since my experiences with .NET are limited I would like to ask some questions regarding the performance.

I have encountered a few specific things which I cannot explain to myself properly and would like you to ask a) why and b) what is the best practise to deal with the cases.

My first question is about Math.pow. I already found some answers here on StackOverflow which explains it quite well (a) but not what to do about this(b). My benchmark Program looks like this

Stopwatch watch = new Stopwatch();  // from the Diagnostics class
watch.Start();
for (int i = 0; i < 1000000; i++)
    double result = Math.Pow(4,7)   // the function call
watch.Stop()

The result was not very nice (~300ms on my computer) (I have run the test 10 times and calcuated the average value).

My first idea was to check wether this is because it is a static function. So I implemented my own class

class MyMath
{
    public static double Pow (double x, double y)   //Using some expensive functions to calculate the power
    {
        return Math.Exp(Math.Log(x) * y);
    }

    public static double PowLoop (double x, int y)  // Using Loop
    {
        double res = x;
        for(int i = 1; i < y; i++)
            res *= x;
        return res;
    }

    public static double Pow7 (double x)            // Using inline calls
    {
        return x * x * x * x * x * x * x;
    }
}

THe third thing I checked were if I would replace the Math.Pow(4,7) directly through 4*4*4*4*4*4*4.

The results are (the average out of 10 test runs)

300 ms   Math.Pow(4,7)
356 ms   MyMath.Pow(4,7)    //gives wrong rounded results
264 ms   MyMath.PowLoop(4,7)
 92 ms   MyMath.Pow7(4)
 16 ms   4*4*4*4*4*4*4

Now my situation now is basically like this: Don't use Math for Pow. My only problem is just that... do I really have to implement my own Math-class now? It seems somehow ineffective to implement an own class just for the power function. (Btw. PowLoop and Pow7 are even faster in the Release build by ~25% while Math.Pow is not).

So my final questions are

a) am I wrong if I wouldn't use Math.Pow at all (but for fractions maybe) (which makes me somehow sad).

b) if you have code to optimize, are you really writing all such mathematical operations directly?

c) is there maybe already a faster (open-source^^) library for mathematical operations

d) the source of my question is basically: I have assumed that the .NET Framework itself already provides very optimized code / compile results for such basic operations - be it the Math-Class or handling arrays and I was a little surprised how much benefit I would gain by writing my own code. Are there some other, general "fields" or something else to look out in C# where I cannot trust C# directly.

Cœur
  • 37,241
  • 25
  • 195
  • 267
S. Richter
  • 269
  • 1
  • 3
  • 11
  • 7
    I think 4*4*4*4*4*4*4 will be evaluated at compile time hence the reason it's super fast. – Nick Mar 04 '11 at 10:13
  • I think for most people a 100ms or so isn't a big deal. C# often isn't first choice for such applications from most people. – Ian Mar 04 '11 at 10:15
  • 1
    Did you test with larger numbers? I would think that Math.Pow is optimized for larger exponents, and does stuff like: x^7 == x^{3+3+1} == {x ^ 3 + x^3 x}, which would mean it's running order is rougly O(log(n) ), whereas your solution is O(n), and would probably be much slower for large exponents. – markijbema Mar 04 '11 at 10:19
  • 1
    Both end tests (Pow7 and the statics) are compiler optimizable, and as such not as generic as the ones before. – TomTom Mar 04 '11 at 10:21
  • There really ought to be an overload to `Math.Pow` which takes an `int` exponent, since that is the most common case, and it would run faster. – Richie Cotton Mar 04 '11 at 10:50
  • I do believe that the Math-class is optimized / fast for something - be it large numbers or fractual potents. But for "non-special" cases (calculating Math.Pow( .., 2) ) or for low integer values in genereal it would be generally to write own code? In this particular case i want to calculate my floating-image (1280 * 1024 resolution) into Image^7 and I better not use the .NET-Framework at all then? Btw. I also ran Math.Pow(4, (int) 7) and result was slightly slower then Math.Pow(4,7) - probably because of the conversion. – S. Richter Mar 04 '11 at 10:55
  • @markijbema: No way. Math.Pow uses floating-point arithmetics, which will most likely be a single instruction, which the FPU will then probably (speculation, I'm not in hardware design) evaluate using Taylor expansion or something. – erikkallen Mar 04 '11 at 11:03
  • @erikkallen: You're right of course, I was thinking of the power of a matrix. – markijbema Mar 04 '11 at 11:57

3 Answers3

5

Two things to bear in mind:

  1. You probably don't need to optimise this bit of code. You've just done a million calls to the function in less than a second. Is this really going to cause big problems in your program?

  2. Math.Pow is probably fairly optimal anyway. At a guess, it will be calling a proper numerics library written in a lower level language, which means you shouldn't expect orders of magnitude increases.

  3. Numerical programming is harder than you think. Even the algorithms that you think you know how to calculate, aren't calculated that way. For example, when you calculate the mean, you shouldn't just add up the numbers and divide by how many numbers you have. (Modern numerics libraries use a two pass routine to correct for floating point errors.)

That said, if you decide that you definitely do need to optimise, then consider using integers rather than floating point values, or outsourcing this to another numerics library.

Richie Cotton
  • 118,240
  • 47
  • 247
  • 360
2

Firstly, integer operations are much faster than floating point. If you don't need floating point values, don't use the floating point data type. This generally true for any programming language.

Secondly, as you have stated yourself, Math.Pow can handle reals. It makes use of a much more intricate algorithm than a simple loop. No wonder it is slower than simply looping. If you get rid of the loop and just do n multiplications, you are also cutting off the overhead of setting up the loop - thus making it faster. But if you don't use a loop, you have to know the value of the exponent beforehand - it can't be supplied at runtime.

I am not really sure why Math.Exp and Math.Log is faster. But if you use Math.Log, you can't find the power of negative values.

Basically int are faster and avoiding loops avoid extra overhead. But you are trading off some flexibility when you go for those. But it is generally a good idea to avoid reals when all you need are integers, but in this case coding up a custom function when one already exists seems a little too much.

The question you have to ask yourself is whether this is worth it. Is Math.Pow actually slowing your program down? And in any case, the Math.Pow already bundled with your language is often the fastest or very close to that. If you really wanted to make an alternate implementation that is really general purpose (i.e. not limited to only integers, positive values, etc.), you will probably end up using the same algorithm used in the default implementation anyway.

MAK
  • 26,140
  • 11
  • 55
  • 86
  • 1
    While I agree with the general sentiment of your answer, I want to make one remark: Of course library functions function are optimized, but it is not always possible to optimize for all cases. Consider for instance sorting, where there are a lot of valid options for algorithms, and it really depends on your particular data which one is the best. – markijbema Mar 04 '11 at 11:57
  • @markijbema: That is why I used the word "general purpose" in my answer. If you can make extra assumptions about the data (e.g. they will always be integers, or be within a given range), of course you can make something that optimizes using that (e.g. counting sort instead of quicksort). That is the whole point of my answer, for general purpose functions, it is hard to match the library. – MAK Mar 04 '11 at 12:00
0

When you are talking about making a million iterations of a line of code then obviously every little detail will make a difference.

Math.Pow() is a function call which will be substantially slower than your manual 4*4...*4 example.

Don't write your own class as its doubtful you'll be able to write anything more optimised than the standard Math class.

cusimar9
  • 5,185
  • 4
  • 24
  • 30