John Palmer
John Palmer

Reputation: 25516

Why is calling my C code from F# very slow (compared to native)?

So I wrote some numerical code in C but wanted to call it from F#. However it runs incredibly slowly.

Times:

For reference, the c code is

int main(int argc, char** argv)
{
    setvals(100,100,15,20.0,0.0504);
    float* dmats = malloc(sizeof(float) * factor*factor);
    MakeDmat(1.4,-1.92,dmats); //dmat appears to be correct
    float* arr1 = malloc(sizeof(float)*xsize*ysize);
    float* arr2 = malloc(sizeof(float)*xsize*ysize);
    randinit(arr1);
    for (int i = 0;i < 10000;i++)
    {
            evolve(arr1,arr2,dmats);
            evolve(arr2,arr1,dmats);
            if (i==9999) {print(arr1,xsize,ysize);};
    }
    return 0;
}

I left out the implementation of the functions. The F# code I am using is

open System.Runtime.InteropServices
open Microsoft.FSharp.NativeInterop

[<DllImport("a.dll")>] extern void main (int argc, char* argv)
[<DllImport("a.dll")>] extern void setvals (int _xsize, int _ysize, int _distlimit,float _tau,float _Iex)
[<DllImport("a.dll")>] extern void MakeDmat(float We,float Wi, float*arr)
[<DllImport("a.dll")>] extern void randinit(float* arr)
[<DllImport("a.dll")>] extern void print(float* arr)
[<DllImport("a.dll")>] extern void evolve (float* input, float* output,float* connections)

let dlimit,xsize,ysize = 15,100,100
let factor = (2*dlimit)+1
setvals(xsize,ysize,dlimit,20.0,0.0504)
let dmat = Array.zeroCreate (factor*factor)
MakeDmat(1.4,-1.92,&&dmat.[0])

let arr1 = Array.zeroCreate (xsize*ysize)
let arr2 = Array.zeroCreate (xsize*ysize)
let addr1 = &&arr1.[0]
let addr2 = &&arr2.[0]
let dmataddr = &&dmat.[0]
randinit(&&dmat.[0])
[0..10000] |> List.iter (fun _ ->
    evolve(addr1,addr2,dmataddr)
    evolve(addr2,addr1,dmataddr)
        )

print(&&arr1.[0])

The F# code is compiled with optimisations on.

Is the mono interface for calling C code really that slow (almost 8ms of overhead per function call) or am I just doing something stupid?

Upvotes: 6

Views: 859

Answers (1)

JaredPar
JaredPar

Reputation: 754745

It looks like part of the problem is that you are using float on both the F# and C side of the PInvoke signature. In F# float is really System.Double and hence is 8 bytes. In C a float is generally 4 bytes.

If this were running under the CLR I would expect you to see a PInvoke stack unbalanced error during debugging. I'm not sure if Mono has similar checks or not. But it's possible this is related to the problem you're seeing.

Upvotes: 11

Related Questions