Perché questo codice F # è così lento?


127

Un'implementazione di Levenshtein in C # e F #. La versione C # è 10 volte più veloce per due stringhe di circa 1500 caratteri. C #: 69 ms, F # 867 ms. Perché? Per quanto ne so, fanno esattamente la stessa cosa? Non importa se si tratta di una versione di rilascio o di un debug.

EDIT: Se qualcuno viene qui cercando specificamente l'implementazione Modifica distanza, è rotto. Il codice di lavoro è qui .

C # :

private static int min3(int a, int b, int c)
{
   return Math.Min(Math.Min(a, b), c);
}

public static int EditDistance(string m, string n)
{
   var d1 = new int[n.Length];
   for (int x = 0; x < d1.Length; x++) d1[x] = x;
   var d0 = new int[n.Length];
   for(int i = 1; i < m.Length; i++)
   {
      d0[0] = i;
      var ui = m[i];
      for (int j = 1; j < n.Length; j++ )
      {
         d0[j] = 1 + min3(d1[j], d0[j - 1], d1[j - 1] + (ui == n[j] ? -1 : 0));
      }
      Array.Copy(d0, d1, d1.Length);
   }
   return d0[n.Length - 1];
}

F # :

let min3(a, b, c) = min a (min b c)

let levenshtein (m:string) (n:string) =
   let d1 = Array.init n.Length id
   let d0 = Array.create n.Length 0
   for i=1 to m.Length-1 do
      d0.[0] <- i
      let ui = m.[i]
      for j=1 to n.Length-1 do
         d0.[j] <- 1 + min3(d1.[j], d0.[j-1], d1.[j-1] + if ui = n.[j] then -1 else 0)
      Array.blit d0 0 d1 0 n.Length
   d0.[n.Length-1]

7
Qual è il differenziale di prestazione usando inline?
Gradbot

Risposte:


202

Il problema è che la min3funzione è compilata come una funzione generica che utilizza il confronto generico (pensavo che questo usasse solo IComparable, ma in realtà è più complicato - userebbe il confronto strutturale per i tipi F # ed è una logica abbastanza complessa).

> let min3(a, b, c) = min a (min b c);;
val min3 : 'a * 'a * 'a -> 'a when 'a : comparison

Nella versione C #, la funzione non è generica (richiede solo int). Puoi migliorare la versione di F # aggiungendo annotazioni di tipo (per ottenere la stessa cosa di in C #):

let min3(a:int, b, c) = min a (min b c)

... o facendo min3come inline(nel qual caso, sarà specializzato intquando usato):

let inline min3(a, b, c) = min a (min b c);;

Per una stringa casuale strdi lunghezza 300, ottengo i seguenti numeri:

> levenshtein str ("foo" + str);;
Real: 00:00:03.938, CPU: 00:00:03.900, GC gen0: 275, gen1: 1, gen2: 0
val it : int = 3

> levenshtein_inlined str ("foo" + str);;
Real: 00:00:00.068, CPU: 00:00:00.078, GC gen0: 0, gen1: 0, gen2: 0
val it : int = 3

1
Perché F # non compila min3 come una funzione che accetta int? Conosce già abbastanza informazioni sul tipo al momento della compilazione per farlo. Funzionerebbe così se min3 fosse una funzione modello C ++, quindi sono un po 'perplesso sul perché F # non lo faccia.
sashang,

42
F # lo considera il più generico possibile, ad es. "Per tutti i tipi X che supportano il confronto". inlinefunziona come un modello C ++, che sarebbe specializzato in intbase al sito di chiamata.
Brian,

13
I modelli C ++ si comportano essenzialmente come F # inline. Il motivo per cui il comportamento predefinito è diverso è perché si basa su generici .Net gestiti dal runtime (e, probabilmente, non sono così grandi per la scrittura di codice numerico generico). L'uso del comportamento C ++ in F #, tuttavia, porterebbe a un eccesso di codice, poiché F # usa generici molto di più.
Tomas Petricek,

4
La semantica del modello C ++ può portare a un gonfiamento del codice anche in C ++ e la mancanza di un modo conveniente per passare all'utilizzo di un meccanismo di runtime per evitare che a volte sia una seccatura. Tuttavia, la paura del bloat del codice è normalmente irrazionale - in genere, i modelli C ++ funzionano bene.
Steve314,

@ Steve314: In genere è anche facile evitare rifattorizzando tutto il codice che non utilizza un tipo dipendente, quindi quel codice non viene duplicato per diverse istanze.
ildjarn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.