views:

174

answers:

3

I've inherited a Visual Studio/VB.Net numerical simulation project that has a likely inefficient calculation. Profiling indicates that the function is called a lot (1 million times plus) and spends about 50% of the overall calculation within this function. Here is the problematic portion

Result = (A * (E ^ C)) / (D ^ C * B) (where A-C are local double variables and D & E global double variables)

Result is then compared to a threshold which might have additional improvements as well, but I'll leave them another day

any thoughts or help would be appreciated

Steve

+1  A: 

One easy speed up is that

Result = (A/B) * (E/D)^C

At least you are doing one less exponent. Depending on what C is, there might be faster ways. Like if C is a small integer.

edit: adding proof to show this is faster

    public static void main(String[] args) {
    StopWatch sw = new StopWatch();

    float e = 1.123F;
    float d = 4.456F;
    float c = 453;
    sw.start();
    int max = 5000;
    double result = 0;
    for (int a = 1; a < max; a++) {
        for (float b = 1; b < max; b++) {
            result = (a * (Math.pow(e, c))) / (Math.pow(d, c) * b);
        }
    }
    sw.split();
    System.out.println("slow: " + sw.getSplitTime() + " result: " + result);
    sw.stop();
    sw.reset();

    sw.start();
    result = 0;
    for (int a = 1; a < max; a++) {
        for (float b = 1; b < max; b++) {
            result = a / b * Math.pow(e/d, c);
        }
    }

    sw.split();
    System.out.println("fast: " + sw.getSplitTime() + " result: " + result);
    sw.stop();
    sw.reset();
}

This is the output

slow: 26062 result: 7.077390271736578E-272
fast: 12661 result: 7.077392136525382E-272

There is some skew in the numbers. I would think that the faster version is more exact (but that's just a feeling since i can't think of exactly why).

mlathe
+1  A: 

The exponent operator (Math.Pow) isn't very fast, there is no dedicated CPU instruction for calculating it. You mentioned that D and E are global variables. That offers a glimmer of hope to get it faster, if you can isolate their changes. Rewriting the equation using logarithms:

log(r) = log((a x e^c) / (b x d^c))
       = log(a x e^c) - log (b x d^c)
       = log(a) + log(e^c) - log(b) - log(d^c)
       = log(a) + c*log(e) - log(b) - c*log(d)
       = log(a) - log(b) + c x (log(e) - log(d))
result = exp(r)

Which provides this function to calculate the result:

  Function calculate(ByVal a As Double, ByVal b As Double, ByVal c As Double, ByVal d As Double, ByVal e As Double) As Double
    Dim logRes = Math.Log(a) - Math.Log(b) + c * (Math.Log(e) - Math.Log(d))
    Return Math.Exp(logRes)
  End Function

I timed it with the StopWatch class, it is exactly as fast as your original expression. Not a coincidence of course. You'll get ahead by somehow being able to pre-calculate the Math.Log(e) - Math.Log(d) term.

Hans Passant
A: 

Well done for profiling. I would also check that A-C are different on every call. In other words, is it possible the caller is actually calculating the same value over and over again? If so, change it so it caches the answer.

MarkJ