web-dev-qa-db-fra.com

Pourquoi ce code F # est-il si lent?

Une implémentation Levenshtein en C # et F #. La version C # est 10 fois plus rapide pour deux chaînes d'environ 1500 caractères. C #: 69 ms, F # 867 ms. Pourquoi? Autant que je sache, ils font exactement la même chose? Peu importe s'il s'agit d'une version ou d'une version de débogage.

EDIT: Si quelqu'un vient ici à la recherche spécifique de l'implémentation Edit Distance, il est cassé. Le code de travail est ici .

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]
124
Robert Jeppesen

Le problème est que le min3 La fonction est compilée comme une fonction générique qui utilise une comparaison générique (je pensais que cela utilise juste IComparable, mais c'est en fait plus compliqué - il utiliserait la comparaison structurelle pour les types F # et c'est une logique assez complexe).

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

Dans la version C #, la fonction n'est pas générique (elle prend juste int). Vous pouvez améliorer la version F # en ajoutant des annotations de type (pour obtenir la même chose qu'en C #):

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

... ou en faisant min3 as inline (auquel cas, il sera spécialisé dans int lors de son utilisation):

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

Pour une chaîne aléatoire str de longueur 300, j'obtiens les nombres suivants:

> 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
197
Tomas Petricek