2012-03-14 28 views
6

Así que escribí un código numérico en C pero quería llamarlo desde F #. Sin embargo, funciona increíblemente lento.¿Por qué llamar mi código C desde F # es muy lento (en comparación con el nativo)?

tiempos:

  • gcc -O3: 4 segundos
  • gcc -O0: 30 segundo
  • código FSharp que llama al código gcc optimizada: 2 minutos 30 segundos.

Como referencia, el código C es

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; 
} 

Salí a cabo la ejecución de las funciones. El F # código que estoy utilizando es

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]) 

el código F # se compila con optimizaciones sucesivamente.

¿La interfaz mono para llamar al código C es realmente tan lenta (casi 8ms de sobrecarga por llamada de función) o solo estoy haciendo algo estúpido?

+1

¿Probó el código en Windows? Podrían ser muchas cosas – leppie

+7

A un lado: en F # 'float' significa realmente' double' que es de 8 bytes. En C, generalmente 'float' tiene 4 bytes. Puede tener una coincidencia de firma pinvoke allí. – JaredPar

+1

@JaredPar: esa fue la respuesta, sospecho que la conversión flotante hizo que los parámetros cambiaran a unos que causaron una ejecución mucho más lenta. El tiempo de ejecución de F # ahora es prácticamente idéntico al de la C. Existe alguna forma de verificar estas discrepancias de firmas? –

Respuesta

11

Parece que parte del problema es que está utilizando float tanto en el lado F # como en el lado C de la firma PInvoke. En F # float es realmente System.Double y, por lo tanto, tiene 8 bytes. En C a float, generalmente tiene 4 bytes.

Si esto se ejecutara bajo CLR esperaría que usted vea un error de desequilibrio de la pila PInvoke durante la depuración. No estoy seguro de si Mono tiene controles similares o no. Pero es posible que esto esté relacionado con el problema que estás viendo.

Cuestiones relacionadas