Why is this code slow?

Salih Dincer salihdb at hotmail.com
Mon Mar 25 03:20:36 UTC 2024


On Sunday, 24 March 2024 at 22:16:06 UTC, Kdevel wrote:
> The term containing the `pow` invocation computes the 
> alternating sequence -1, 1, -1, ..., which can be replaced by 
> e.g.
>
> ```d
>    immutable int [2] sign = [-1, 1];
>    n += sign [i & 1] / (i * 2.0 - 1.0);
> ```
>
> This saves the expensive call to the pow function.

I also used this code:
```d
import std.stdio : writefln;
import std.datetime.stopwatch;

enum ITERATIONS = 1_000_000;
enum BENCHMARKS = 20;

auto leibniz(bool speed = true)(int iter) {
   double n = 1.0;

   static if(speed) const sign = [-1, 1];

   for(int i = 2; i < iter; i++) {
     static if(speed) {
       const m = i << 1;
       n += sign [i & 1] / (m - 1.0);
     } else {
       n += pow(-1, i - 1) / (i * 2.0 - 1.0);
     }
   }
   return n * 4.0;
}

auto pow(F, G)(F x, G n) @nogc @trusted pure nothrow {
     import std.traits : Unsigned, Unqual;

     real p = 1.0, v = void;
     Unsigned!(Unqual!G) m = n;

     if(n < 0) {
         if(n == -1) return 1 / x;
         m = cast(typeof(m))(0 - n);
         v = p / x;
     } else {
         switch(n) {
           case 0: return 1.0;
           case 1: return x;
           case 2: return x * x;
           default:
         }
         v = x;
     }
     while(true) {
         if(m & 1) p *= v;
         m >>= 1;
         if(!m) break;
         v *= v;
     }
     return p;
}

void main()
{
     double result;
     long total_time = 0;

     for(int i = 0; i < BENCHMARKS; i++)
     {
         auto sw = StopWatch(AutoStart.no);
         sw.start();

         result = ITERATIONS.leibniz;//!false;

         sw.stop();
         total_time += sw.peek.total!"nsecs";
     }

     result.writefln!"%0.21f";
     writefln("Avg execution time: %f\n", total_time / BENCHMARKS 
/ 1e9);
}
```

and results:

> dmd -run "leibnizTest.d"
> 3.141594653593692054727
> Avg execution time: 0.002005

If I compile with leibniz!false(ITERATIONS) the average execution 
time increases slightly:

> Avg execution time: 0.044435

However, if you pay attention, it is not connected to an external 
library and a power function that works with integers is used. 
Normally the following function of the library should be called:

> Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure 
> nothrow
> if (isFloatingPoint!(F) && isFloatingPoint!(G))
> ...

Now, the person asking the question will ask why it is slow even 
though we use exactly the same codes in C; rightly. You may think 
that the more watermelon you carry in your arms, the slower you 
naturally become. I think the important thing is not to drop the 
watermelons :)

SDB at 79


More information about the Digitalmars-d-learn mailing list