Le Dr Jon Harrop, que j'ai contacté par e-mail, a expliqué ce qui se passe:
Le problème est tout simplement que le programme a été optimisé pour Python. C'est souvent lorsque le programmeur est le plus familier avec un langage que les autres, bien sûr. Vous avez juste à savoir un ensemble différent de règles qui dictent la façon dont F# les programmes devraient être optimisé...
Plusieurs choses m'ont sauté aux yeux comme l'utilisation d'un "for i in 1..n ne" boucle plutôt que d'un "for i=1 to n do" de la boucle (qui est plus rapide en général, mais pas important ici), à plusieurs reprises de faire la Liste.mapi sur une liste d'imiter un index de tableau (qui a alloué intermédiaire des listes inutilement) et votre utilisation du F# TryGetValue pour le Dictionnaire qui alloue inutilement (l' .NET TryGetValue qui accepte une ref est plus rapide en général, mais pas tellement ici)
... mais le véritable assassin problème s'est avéré être votre utilisation d'une table de hachage pour mettre en œuvre une densité de la matrice 2D. À l'aide d'une table de hachage est idéal en Python parce que sa table de hachage de la mise en œuvre a été extrêmement bien optimisé (comme en témoigne le fait que votre code Python est en cours d'exécution aussi rapide que F# compilé en code natif!) mais les tableaux sont une bien meilleure manière de représenter des matrices denses, en particulier lorsque vous souhaitez une valeur par défaut de zéro.
Le plus drôle, c'est que lorsque j'ai codé cet algorithme, je NE utilisez un tableau -- j'ai changé la mise en œuvre d'un dictionnaire pour des raisons de clarté (en évitant la limite de tableau contrôles effectués le code plus simple et beaucoup plus facile de raisonner sur).
Jon a transformé mon code au dos :-)) dans son tableau de la version, et il fonctionne à 100 fois la vitesse.
Morale de l'histoire:
- F# Dictionnaire des besoins de travail... lors de l'utilisation de n-uplets clés, compilé F# est plus lent que interprétée Python tables de hachage!
- Évident, mais pas de mal de le répéter: Nettoyeur de code signifie parfois... beaucoup plus lent code.
Merci à vous, Jon -- beaucoup apprécié.
EDIT: le fait que le remplacement de Dictionnaire avec le Tableau fait F# enfin courir à la vitesse d'un langage compilé est prévu pour fonctionner, n'est-ce pas nier le besoin d'une correction dans le Dictionnaire de la vitesse (j'espère que F# personnes à partir de MS sont la lecture de ce). D'autres algorithmes dépendent de dictionnaires/tables de hachage, et ne peut pas être facilement changé à l'aide de tableaux; de faire des programmes de souffrir "interprète-vitesses" chaque fois que l'on utilise un Dictionnaire, est sans doute un bug. Si, comme certains l'ont dit dans les commentaires, le problème n'est pas F# mais avec .NET Dictionnaire, alors je dirais que c'est... un bug dans .NET!
EDIT2: La solution la plus claire, qui ne nécessite pas l'algorithme de passer à des tableaux (certains algorithmes ne pourront tout simplement pas être prête pour cela), c'est pour changer cela:
let optimalResults = new Dictionary<_,_>()
dans ce:
let optimalResults = new Dictionary<_,_>(HashIdentity.Structural)
Cette modification rend le code F# exécuté 2,7 x fois plus vite, donc finalement battre en Python (1,6 x plus rapide). La chose étrange est que les tuples par défaut utiliser la comparaison structurelle, donc, en principe, la comparaison faite par le Dictionnaire sur les touches sont les mêmes (avec ou sans Structurels). Dr Harrop théorise que la différence de vitesse peut être attribuée à virtuel dépêche: "autant que je sache, .NET fait peu pour optimiser virtuel envoi de distance et le coût du virtuel, l'expédition est extrêmement élevé sur du matériel moderne, parce que c'est un "calculé goto" qui saute le compteur de programme, à une imprévisible emplacement et, par conséquent, nuit à la direction de la prévision logique et sera presque certainement entraîner l'ensemble de la CPU pipeline d'être vidé et rechargé".
En clair, et comme suggéré par Don Syme (regardez en bas 3 réponses), "être explicite quant à l'utilisation des structurel de hachage lors de l'utilisation de référence de type de clés en conjonction avec le .NET collections". (Dr Harrop dans les commentaires ci-dessous également dit que nous devrions toujours utiliser des comparaisons Structurelles lors de l'utilisation .NET collections).
Chère équipe F# dans la SEP, si il y a un moyen pour résoudre ce problème, s'il vous plaît.