Monday, 4 March 2019

How Fast is C# versus C benchmark maths

Walked the usual territory regarding my economic models. Making them bigger makes them very slow, could I speed them up writing them in C rather than C#. Have been here many times.

Long story short the most important speed up lies in the code algorithm efficiency itself.

An example with genetic algorithm some years ago. Tried C, tried a faster computer. But the 10x speed up came with how I added errors. Initially I was literally copying the DNA bit by bit and deciding whether to mutate it on each copy. Obviously very time consuming. "memcpy" followed by using Exponential Distribution to decide the gaps between the mutations and flipping just a handful of bits made this bottleneck trivial time wise.

Bottlenecks obey Pareto's Law or 80/20. Computer spends 80% of its time running through 20% of code.Write first, discover that 20% and optimise that. This is the best approach.

Anyway redid my usual Mandelbrot calculation to confirm this. Obviously this only tests basic ALU maths operations. But illustrative of which bits really benefit from moving to C.

The Function:

void Mandelbrot(int xn, int yn, double x0, double y0, double xi, double yi, int* r)
{
   double xp = x0, yp;
   for (int x = 0; x < xn; x++)
   {
      yp = y0;
      for (int y = 0; y < yn; y++)
      {
         int c = 0;
         double a = xp, b = yp, b1 = 0;
         while (c++ < 16384 && a * a + b * b < 4)
         {
            b1 = 2 * a * b + yp;
            a = a * a - b * b + xp;
            b = b1;
         }

         r[x * yn + y] = c;

         yp += yi;

      }
      xp += xi;
   }
}

C and C# are identical except that 'r' is passed 'ref r'.

The call in C, C#, and I called via a DLL to compare also:

C#

int[] r = new int[SIZE * SIZE];
Mandel(SIZE, SIZE, -1, -1, 2.0d / SIZE, 2.0d / SIZE, ref r);

C# unsafe DLL

[DllImport("mandelbrot.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern void Mandelbrot(int xn, int yn, double x0, double y0, double xi, double yi, int* r);

unsafe
{
   int arraysize = Marshal.SizeOf(typeof(int)) * SIZE * SIZE;
   int* r1 = (int*)Marshal.AllocHGlobal(arraysize).ToPointer();
     
   Mandelbrot(SIZE, SIZE, -1, -1, 2.0d / SIZE, 2.0d / SIZE, r1);
}

C

int* r = (int*)calloc(SIZE * SIZE, sizeof(int));
Mandelbrot(SIZE, SIZE, -1, -1, 2.0 / SIZE, 2.0 / SIZE, r);


Compiled with Visual Studio Community 2018. C compiled with Microsoft C++ compiler to release x64 with -O2 optimisation.
Executed on i5-3340M @ 2.70GHz with 6.00GB RAM

The results with SIZE = 2000 (that is r[4000000] i.e. 4e6 points from the set):

C#             98.5secs
C# DLL    94.6secs
C               94.8secs

Amazingly C# was only 4% slower!!! Lesson learned that with mathematics at least C# and C compilers and executables can't do much different.

ToDo: I should just check that because I'm not using the result the compilers might have cut corners. But they must have done the calculation else why did it take 1.5 minutes.

No comments:

"The Jewish Fallacy" OR "The Inauthenticity of the West"

I initially thought to start up a discussion to formalise what I was going to name the "Jewish Fallacy" and I'm sure there is ...