J'ai récemment trouvé une présentation sur F# pour les programmeurs Python et après l'avoir regardé, j'ai décidé de mettre en œuvre une solution au "puzzle des fourmis" par moi-même.
Il existe une fourmi qui peut se déplacer sur une grille planaire. La fourmi peut se déplacer d'un espace à la fois vers la gauche, la droite, le haut ou le bas. Autrement dit, à partir de la cellule (x, y), la fourmi peut se rendre dans les cellules (x+1, y), (x-1, y), (x, y+1) et (x, y-1). Les points où la somme des chiffres des coordonnées x et y est supérieure à 25 sont inaccessibles à la fourmi. Par exemple, le point (59,79) est inaccessible car 5 + 9 + 7 + 9 = 30, ce qui est supérieur à 25. La question est la suivante : combien de points la fourmi peut-elle atteindre si elle commence à (1000, 1000), y compris (1000, 1000) elle-même ?
J'ai implémenté ma solution en 30 lignes de OCaml d'abord et je l'ai essayé :
$ ocamlopt -unsafe -rectypes -inline 1000 -o puzzle ant.ml
$ time ./puzzle
Points: 148848
real 0m0.143s
user 0m0.127s
sys 0m0.013s
Neat, mon résultat est le même que celui de l'implémentation de leonardo, en D et C++ . En comparant avec l'implémentation C++ de Leonardo, la version OCaml fonctionne environ 2 fois plus lentement que C++. Ce qui est correct, étant donné que Leonardo a utilisé une file d'attente pour supprimer la récursion.
Moi, alors. traduit le code en F# ... et voilà ce que j'ai obtenu :
Thanassis@HOME /g/Tmp/ant.fsharp
$ /g/Program\ Files/FSharp-2.0.0.0/bin/fsc.exe ant.fs
Microsoft (R) F# 2.0 Compiler build 2.0.0.0
Copyright (c) Microsoft Corporation. All Rights Reserved.
Thanassis@HOME /g/Tmp/ant.fsharp
$ ./ant.exe
Process is terminated due to StackOverflowException.
Quit
Thanassis@HOME /g/Tmp/ant.fsharp
$ /g/Program\ Files/Microsoft\ F#/v4.0/Fsc.exe ant.fs
Microsoft (R) F# 2.0 Compiler build 4.0.30319.1
Copyright (c) Microsoft Corporation. All Rights Reserved.
Thanassis@HOME /g/Tmp/ant.fsharp
$ ./ant.exe
Process is terminated due to StackOverflowException
Stack overflow... avec les deux versions de F# que j'ai dans ma machine... Par curiosité, j'ai ensuite pris le binaire généré (ant.exe) et l'ai exécuté sous Arch Linux/Mono :
$ mono -V | head -1
Mono JIT compiler version 2.10.5 (tarball Fri Sep 9 06:34:36 UTC 2011)
$ time mono ./ant.exe
Points: 148848
real 1m24.298s
user 0m0.567s
sys 0m0.027s
Étonnamment, il fonctionne sous Mono 2.10.5 (c'est-à-dire sans débordement de pile) - mais il prend 84 secondes, c'est-à-dire 587 fois plus lent qu'OCaml - oups.
Donc ce programme...
- fonctionne bien sous OCaml
- ne fonctionne pas du tout sous .NET/F#
- fonctionne, mais est très lent, sous Mono/F#.
Pourquoi ?
EDITAR: La bizarrerie continue - L'utilisation de "--optimize+ --checked-" fait disparaître le problème, mais seulement sous ArchLinux/Mono ; sous Windows XP et Windows 7/64bit, même la version optimisée de la pile binaire déborde.
Final EDIT : J'ai trouvé la réponse moi-même - voir ci-dessous.
0 votes
Je ne vois pas de question ou je l'ai juste trop lu. Il semble que ce ne soit qu'une diatribe, et une mauvaise en plus. Vous pourriez penser à essayer ceci avec les bons paramètres d'optimisation (générer des appels récursifs de queue me vient à l'esprit) - mais sans code, qui peut le dire sur un site de FAQ ?
0 votes
Le code est là, dans les liens vers pastebin.
0 votes
Mais il n'y a pas de question, ce qui est le but de stackoverflow. Je vote pour la fermeture de ce sujet.
0 votes
La question est implicite : pourquoi F# s'est comporté si différemment d'OCaml - et la réponse correcte était : "J'ai oublié --optimize+", que j'ai vu juste après avoir posté.
0 votes
Et c'est ce que j'ai laissé entendre dans mon commentaire
1 votes
@CarstenKönig Expert F# 2.0 dit : "... compilez votre code final en utilisant --optimize qui applique une optimisation maximale à votre code. C'est également le paramètre d'optimisation par défaut de fsc.exe." à la page 164. Cela ne signifie-t-il pas que ttsiodras avait déjà toutes les optimisations activées ?
0 votes
Allez-y, essayez - créez un projet F# dans VS. Vous obtiendrez par défaut un build de débogage sans que l'option d'optimisation du code ou de génération d'appels de queue ne soit cochée. J'ai simplement supposé qu'il utilisait VS (pourquoi ne le ferait-il pas ?) - et comme vous pouvez le voir dans sa propre réponse, il n'a pas non plus activé l'optimisation en premier lieu.
0 votes
En fait, les choses sont bizarres : même avec l'option --optimize+, le débordement de pile ne disparaît que sous ArchLinux/Mono - sous Windows, le programme déborde toujours de la pile, et comme @Keith l'a noté, l'élimination de la récursion de queue est activée par défaut. Qu'est-ce qui se passe ici ?
5 votes
L'appel récursif à walk n'est pas en position de queue. Cela explique pourquoi vous obtenez des débordements de pile. La raison pour laquelle vous n'obtenez pas de débordements de pile dans d'autres situations pourrait être que la pile est configurée pour être plus grande sur ces systèmes, ou que le JIT mono et le compilateur OCAML ont fait quelque chose d'assez intelligent.
1 votes
@ttsiodras : Est-il correct de résumer votre observation comme "fonctionne sur Linux", "déborde sur Windows" ? Les systèmes Linux ont généralement beaucoup plus de pile que Windows par défaut.
0 votes
Oui, ce résumé est correct - et plutôt inattendu, je pense, de voir que F# se comporte mieux sous Linux/Mono...
3 votes
Je pense que c'est une bonne question et c'est dommage qu'elle ait été fermée. Un commentaire initial de l'ordre de "soyez explicite sur ce que vous demandez" aurait réglé le problème. Ou, du moins, elle aurait dû être réouverte après que ttsiodras ait corrigé cela.
7 votes
Je reconnais que cette question est très intéressante et je vote pour la rouvrir. Il y a beaucoup plus à dire sur ce sujet que ne le permet l'espace de ce commentaire...
2 votes
On dirait que vous avez découvert la réponse. Pourquoi la prime ? Quelle est la question qui reste ?
0 votes
@ttsiodras qu'est-il arrivé aux performances d'ocaml vs F# après avoir rendu la queue du code récursif ? Sont-elles toujours aussi mauvaises ?
0 votes
@Daniel : La prime ne vient pas de moi :-)
0 votes
@Keith : Ma version tail-recursive du code fonctionne bien (en 0,5 seconde - mais je mesure avec "time ..." donc je mesure aussi le coût du démarrage). Dans l'ensemble, j'en suis assez satisfait.