https://www.dropbox.com/s/efatwr0pozsargb/PyCon%20mypy%20talk%202016.pdf?dl=0
Une équipe de dropbox, dont GvR fait partie, présente l'état des lieux du typage statique en Python. La présentation est très complète avec le pourquoi, le comment, l'historique et le futur.
Le pourquoi : à des fins de documentation, pour trouver des bugs, pour les perfs, pour les besoins de Dropbox (perf et upgrade du code de py2 à py3)
Le comment : la syntaxe, possible en py3 et py2, utilisable tout de suite avec http://mypy-lang.org/ dynamique toujours possible par défaut
L'historique : depuis 1998, avec les travaux de Jim Fulton (zope.interface)
Le futur : performance, intégration avec les éditeurs, inférence de type etc…
Sur le coup j'ai cru que c'était une présentation de Go ! Sans dec, je suis vraiment impressionné par cette approche graduelle, cette évolution mûrement réfléchie et sans précipitation.
Je pense que cela va aider à migrer du code vers py3, mais n'ayant pas encore essayé moi-même je n'en dirai pas plus…
# A force...
Posté par Zenitram (site web personnel) . Évalué à -10.
A force de reprendre toutes les idées du C/C++, on va se retrouver avec du C/C++… pas plus joli, pas plus réfléchi, juste à la forme différente.
Ca vous semble vraiment plus lisible / beau / mettre ici tout adjectif hype que :
J'ai l'impression surtout qu'on a posé une lib standard et remplacé des point-virgules par des tabulation/retour à la ligne forcés, à force de vouloir un "nouveau langage différent mais qu'on veut rapprocher des langage d'avant finalement car en fait ça marche pas si bien que ça d'être différent plus simple".
PS : faudrait pour le fun que je benche les deux algos, pour voir si Python arrive à s'approcher de la perf de C.
[^] # Re: A force...
Posté par lolop (site web personnel) . Évalué à 10.
Impression fausse. Ça reste du "duck typing" à la base avec un langage de script très dynamique, ça marche bien mais pour quoi c'est fait, mais c'est améliorable… d'où l'ajout des annotations de types. Mais elles restent optionnelles, et ne sont pour le moment pas utilisées à l'exécution.
Et le C va évidemment écraser le Python pour peu qu'il y ait quelques boucles avec des expressions de calcul. On n'utilise pas Python pour des perfs en calcul (pour ça on code en C/C++/Fortran et on plug du Python par dessus pour les enchaînements de haut niveau, les synchros multiprocess, les logs, etc).
Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN
[^] # Re: A force...
Posté par vermillon . Évalué à 4.
Pour ce qui est des performances, l'optimisation du code grâce au typage statique est prévue dans le futur, pour l'instant ça ne change rien.
Cela dit, je doute qu'ils arrivent aux performances du C et comme tu dis, s'ils veulent du C, ils n'ont qu'à écrire du C.
De mémoire (pas de référence, désolé), Clojure a fait pareil: tout le monde s'est mis à vouloir ajouter des annotations de types parce que "ah ben en fait, le typage statique c'est utile", puis au bout d'une paire d'années, beaucoup en ont eu marre, ça demandait trop d'effort, rendait le code moins compréhensible, pour un gain faible.
Y a-t-il des langages où le typage statique est optionnel et pour lesquels cela a été un succès?
[^] # Re: A force...
Posté par Mimoza . Évalué à 8.
Comment ça ? Je comprend pas. Justement il y a bien moins d’ambigüité .
[^] # Re: A force...
Posté par vermillon . Évalué à 0.
Le choix de l'adjectif est mauvais, je pense que j'aurais voulu dire "lisible", "aéré". Bref, c'est plus chargé visuellement et c'est plus difficile à lire.
(à part ça, je comprends l'intérêt du typage explicite dans d'autres langages)
[^] # Re: A force...
Posté par Mimoza . Évalué à 2.
Mouai, sachant que la déclaration est fait au début ça ne surcharge pas tant que ça l'écriture. Pas convaincu pour le coup
[^] # Re: A force...
Posté par Moonz . Évalué à 3.
Objective-C : il y a le type
id
qui veut dire « n’importe quel objet ». En pratique, les développeurs préfèrent généralement typer explicitement.[^] # Re: A force...
Posté par groumly . Évalué à 2.
Le compilo te gueule beaucoup dessus si tu fais ça, ça aide pas.
Et ça aide encore moins si tu traites les warnings comme des erreurs.
Ya un débat un peu à la con en ce moment chez les anciens, sur le typage strict de Swift, comme quoi c'est la fin du monde par rapport à la flexibilitéWW les bugs dormants d'objective c, donc j'imagine qu'il en a qui utilisent régulièrement le kvo, id et autres horreurs de ce genre.
Linuxfr, le portail francais du logiciel libre et du neo nazisme.
[^] # Re: A force...
Posté par Moonz . Évalué à 2. Dernière modification le 31 mai 2016 à 09:56.
Ça fait des années que j’ai plus touché à Objective-C, mais à l’époque non seulement ça ne faisait aucun warning, mais en plus c’était indispensable pour certaines constructions (
+(id)alloc
et-(id)init
, de tête)[^] # Re: A force...
Posté par groumly . Évalué à 2.
-alloc et -init sont des fonctions "magiques" dans le sens où le compilo les connais et peut les traiter à part (Objc est plein de trucs comme ça).
Appeler une fonction sur id, c'est bon (tant que le selector est définit qq part dans un .h en scope). Par contre passer un id en paramètre, ou le retourner, en place d'un truc type différemment (en gros, downcast implicite), et tu te prends un warning il me semble.
Notes aussi que tout coder en id peut jouer des tours assez vilains à bas niveau, si ton sélecteur rentre en conflit avec un autre du même nom, mais qui retourne un struct plutôt qu'un pointeur. Le compilo peut générer du mauvais code dans ce cas, en fonction duquel il choisi.
Linuxfr, le portail francais du logiciel libre et du neo nazisme.
[^] # Re: A force...
Posté par Spack . Évalué à 8.
J'ai essayé mais le point blocant pour moi est qu'il faut mixer les déclarations dans le code et les déclarations dans les commentaires ce qui oblige à :
Ça laisse un goût de proposition à l'arrache où l'on joint les deux bouts comme on peut.
[^] # Re: A force...
Posté par kantien . Évalué à 1. Dernière modification le 30 mai 2016 à 14:38.
Ou alors ils pourraient implémenter un système d'inférence de type à la Hindley-Milner ce qui leur permettrait de faire du duck-typing avec un typage statique et fort. :-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: A force...
Posté par barmic . Évalué à 3.
C'est ce qu'on appel le typage structurel, non ?
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: A force...
Posté par kantien . Évalué à 2. Dernière modification le 30 mai 2016 à 16:03.
D'après ce que wikipédia dit des systèmes structurels de types cela a l'air de correspondre.
En tout cas, moi je trouve la syntaxe concrète plus esthétique. Je ne trouve pas que le recours aux accolades pour délimiter les blocs et l'usage du point-virgule a tout bout de champ soit particulièrement joli. De plus, mettre un objet de type
int
dans une expression booléenne est surprenant sur le plan du typage (la transtypage0 = false
ettrue
pour les entiers non nuls a son charme pour certain, moi je n'aime pas le mélange des genres). Le code qui s'approche de la manipulation des registres dans sa forme, c'est assez bas niveau : je préfère celui qui mime le principe même de l'algorithme d'Euclide pour le calcul dupgcd
(on calcul le reste de la division euclidienne des opérandes, s'il est nul on renvoie le diviseur, sinon on poursuit avec le calcul dupgcd
du diviseur et du reste : la traduction c'est l'affaire du compilateur). Enfin niveau performance, si l'on compile en natif, on se retrouve proche du C/C++.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: A force...
Posté par copapa . Évalué à 1.
Mais que se passe-t-il si je donne à la fonction pgcd sans annotations des objets pour lesquels sont définis les opérations mod et comparaison à 0 (une classe polynôme par exemple) ? Est ce que le systême d'inférence va me mettre des bâtons dans les roues parce qu'il a fait de mauvaise hypothèses ?
[^] # Re: A force...
Posté par Guillaum (site web personnel) . Évalué à 4.
Le système d'inférence ne fait pas d'hypothèse, seulement des observations et une propagation de contraintes.
Dans le cas de Haskell, si tu écris une fonction pgcd, supposons comme ceci :
(Note, je l'appelle
gcd'
cargcd
est déjà pris par une fonction qui fait la même chose… ;)Le type se déduit de façon suivante :
(>)
surb
et(>)
est définie dans la typeclassOrd
mod a b
, le type demod
estIntegral t => t -> t -> t
, donc on conclue quea
etb
sont dans la type classeIntegral
.Integral
étant plus restrictif queOrd
, alors la seule contrainte c'est quea
etb
soient de typeIntegral
gcd'
est soit le même quegcd'
(cas 1), soit le même quea
(cas 2).On déduit donc le type suivant :
gcd' :: Integral t => t -> t -> t
.À partir du moment où tu passes n'importe quel type qui implement
Integral
, alors tu n'auras pas de soucis. Si tu passes un autre type, cela ne marchera pas, mais bon, c'est normal, ton type ne sait pas faire…Le seul problème ici c'est que, au sens de pas mal de monde,
Integral
est trop restrictif, puisque que pour implementerIntegral
il faut implementIntegral
et toutes ses sous typeclass, dontReal
,Enum
,Num
,Ord
etEq
avec, au minimum :Integral
:quotRem
(div et mod en gros),toInteger
Real
:toRational
Enum
:toEnum
,fromEnum
Num
:(+)
,(*)
,abs
,signum
,fromInteger
,negate
ou(-)
Ord
:(<=)
Eq
:(==)
ou(/=)
Ce qui fait beaucoup de bordel à implementer pour calculer un pgcd sur un type custom. La hiérarchie de type pour les nombres en Haskell est trop restrictive à mon gout, mais bon, personne n'est parfait ;) (En pratique cela ne pose rarement de problème sauf quand tu es un fou d'architecture par les types, mais à ce moment là Haskell n'est plus assez puissant pour tes besoins…)
De plus, il n'est pas obligatoire mais fortement conseillé de suivre des règles lors de l'implementation de ces classes (genre s'assurer que
a + b == a - (negate b)
).Cependant, contrairement à de nombreux langage, ce mécanisme impose une certaine cohérence. Quand tu vois
a / b
, tu sais que c'est l’opérateur/
de la classeFractional
et tu peux t'attendre a un comportement. Ce n'est pas le cas en python ou C++ ou le/
peut être surchargé en n'importe quoi. Par example, certains s'en servent pour concaténer des chemins de fichiers. C'est une restriction mais j'ai appris à l'aimer car cela rend le code très explicite.[^] # Re: A force...
Posté par kantien . Évalué à 1.
En OCaml il n'y as pas de typeclasses comme en Haskell (il y a des travaux pour rajouter cela au langage) et on ne peut pas surcharger les opérateurs. Les opération
mod
et la comparaison à0
ne fonctionnent que sur les typesint
et donc le système d'inférence identifie que les paramètres de la fonction sont nécessairement de typeint
.Néanmoins on peut définir une module qui implémente le type polynôme avec sa propre fonction
mod
et sa valeurzero
.Pour reprendre le cas du
pgcd
si je compare la valeur à un float comme0.0
le système se plaindra :Pour comparer avec python :
là où avec l'inférence de type l'erreur sur
g
est determiné statiquement à la compilationSapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: A force...
Posté par Yth (Mastodon) . Évalué à 6.
Réponse : Oui.
Parce qu'on ne cherche pas à coder un PGCD en python avec du typage statique, mais à faire des choses bien plus complexes. Le PGCd c'est un exemple simple et facile à présenter pour montrer le comportement, c'est tout.
Yth.
[^] # Commentaire supprimé
Posté par Anonyme . Évalué à 2.
Ce commentaire a été supprimé par l’équipe de modération.
[^] # Re: A force...
Posté par flan (site web personnel) . Évalué à 4.
En prenant un cas caricatural et en oubliant tous les autres avantages (lib standard, écosystème, absence de compilation, …), ça ne change pas grand-chose, forcément.
[^] # Re: A force...
Posté par reno . Évalué à 2.
Au "petit détail" près qu'en Python les int sont des "big int" non?
Adieu les débordements entiers si fun en C/C++..
# Rigolo
Posté par barmic . Évalué à 10.
C'est rigolo car il y a d'un coté les langages statiques/bas niveau qui cherchent à donner l’expressivité utile pour permettre décrire des parties de ton code de très haut niveau (=> avec une grande abstraction) et de l'autre tu as les langages très haut niveaux/dynamiques qui eux cherchent à te donner les clefs pour gagner en performance là où tu en a vraiment besoin.
Je ne sais pas qu'elle est LA bonne approche (s'il y en a une), mais c'est rigolo à voir.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: Rigolo
Posté par LeBouquetin (site web personnel, Mastodon) . Évalué à 10.
Pour faire du python depuis plus de 8 annnées en techno principale, en équipe et en solo, en développement et en scripting, le vrai problème est la maintenance et la compréhension du code.
L'absence de typage statique / annontation complique la maintenance du code car tu ne sais pas ce que tu peux / ce que tu ne peux pas faire, ce que tu dois / ce que tu ne dois pas faire.
J'utilise autant que faire se peut python 3 avec les annotations, et lorsqu'on travaille en équipe, ça simplifie énormément la reprise de code de l'un par l'autre.
Quant aux problématiques de performance, elles ne sont généralement pas résolues en python.
#tracim pour la collaboration d'équipe __ #galae pour la messagerie email __ dirigeant @ algoo
[^] # Re: Rigolo
Posté par Albert_ . Évalué à 2.
c'est pas le probleme avec tous les langages cela?
Parceque cela n'est pas fait pour…
Un des problemes que moi je rencontre avec python c'est le global lock qui fait chier sa mere pour passer sur de gros systeme et donc tu es oblige d'utiliser un autre langage pour paralleliser.
[^] # Re: Rigolo
Posté par G.bleu (site web personnel) . Évalué à 3.
Pourrais-tu donner plus de détails sur les problèmes que te posent le gil ? De mon expérience ça se contourne facilement (y'a qu'à voir les YouTube/Dropbox qui ont des systèmes en python avec des exigences de stabilité énormes). En plus les modules de multithreading/multiprocessing sont assez bon et permettent par exemple de remplacer le threads par des processus quand on veux du calcul parallèle.
[^] # Re: Rigolo
Posté par Albert_ . Évalué à 1.
OpenMP et python ca fait deux a cause du GIL.
# typage statique automatique ?
Posté par bobo38 . Évalué à 2. Dernière modification le 30 mai 2016 à 12:09.
Ce qui serait intéressant, c'est de faire de typage dynamique en phase de prototypage, ET de lancer un script qui rajouterait une couche de typage statique une fois le code prêt pour un commit. Ça donnerait une couche supplémentaire de vérification, sans se taper le typage statique à la main.
Ça doit être automatisable, non ?
[^] # Re: typage statique automatique ?
Posté par Guillaum (site web personnel) . Évalué à 5.
En pratique il y a l'inférence de type qui fait cela très bien. Dans un langage comme Haskell, tu peux ne jamais écrire un bout de type, mais avoir une inférence qui te calcul le type tout seul et le vérifie à la compilation.
Par example une fonction qui calcul la somme des éléments d'une liste :
Note, dans la vraie vie je ne l'écrirais pas comme cela, mais plutôt :
Enfin bon, le compilateur infère tout seul le type de
sum
comme étantNum a => [a] -> a
où dit autrement, une fonction qui prend une liste dea
([a]
) et renvoie una
avec la contrainte que lea
est un truc qui suit une "interface"Num
.De mon point de vue limité, cela donne la facilité d'écriture du code, tu ne te tracasses pas avec les types, mais au final tu as les outils du typage statique qui sont présents.
[^] # Re: typage statique automatique ?
Posté par barmic . Évalué à 3.
Si je ne m'abuse ce n'est pas une interface, mais bien du typage structurel (c'est à dire, du « duck typing » statique). Tu n'a pas besoin de suivre une interface, tu as juste les bonnes méthodes. C'est très différent, parce que ça te permet de ne pas avoir à modifier le type que tu utilise pour utiliser la méthode en question.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: typage statique automatique ?
Posté par Aluminium95 . Évalué à 1.
Je ne comprends pas ce que tu veux dire … Il faut bien un jour dire comment traiter cet objet non ? En tout cas en haskell, il existe une « classe »
Num
(qui est plutôt une interface en fait), et tu peux dire que n'importe quel type est unNum
du moment qu'un certain nombre de fonctions de base sont définies … pour ce type particulier.En pratique :
[^] # Re: typage statique automatique ?
Posté par barmic . Évalué à 3.
D'une part que ce lien n'est pas défini préalablement (tu n'a pas défini ton type comme étant une spécialisation du
Num
).D'autre part, et c'est là que je me suis fourvoyé, je pensais que tu n'avais pas besoin de définir d'interface
Num
(que ta méthode pourrait prendreNum
ouBar
), mais pour ça je présume qu'il faut explicitement dire qu'elle prend l'un ou l'autre.Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: typage statique automatique ?
Posté par Guillaum (site web personnel) . Évalué à 2.
C'est vraiment des interfaces, en haskell cela se nomme des
typeclass
. Sauf qu'elles sont définie de façon découplée du type associé, mais l'association est explicite.Par example, si je définis :
Et que j'ai le type :
Alors je peux associer la typeclass au type avec :
Tu n'as pas besoin de définir que la fonction prend un
Num
ou unBar
, c'est l'inférence de type qui le fait tout seul (mais tu peux lui forcer la main en faisant quelque chose de plus restrictif). Donc en effet c'est en quelque sort du duck-typing statiquement typé. Example :aura comme type
foo :: Shape t => t -> Float
Ici n'importe quelle type qui implémente l'interface
Shape
fonctionne avec foo`.[^] # Re: typage statique automatique ?
Posté par barmic . Évalué à 4.
Je suis un peu déçu, je pensais que c'était inféré.
Oui je me suis mal exprimé, ce que je voulais dire c'est qu'une méthode prend un et un seul type par paramètre et que si on cherche à la rendre polymorphique, il faut définir un type somme.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: typage statique automatique ?
Posté par Guillaum (site web personnel) . Évalué à 4.
Il y a méprise. Ce qui est inferé c'est le type ou la classe (i.e. "interface" nécessaire d'une fonction). Mais par contre il faut bien dire ce que fait ton interface pour un type particulié. Par example si j'ai une interface
Surface
avec une fonctionsurface
et que j'ai un typeRectangle
, il faut bien que j'écrive quelque part la fonctionsurface
duRectangle
… Par contre quand je vais écrire une fonction qui utilisesurface
, son type sera inféré comme prenant un type d'interfaceSurface
en paramètre.Oui et non.
Les types sommes sont une manière d'encapsuler plusieurs trucs dans un seul type, c'est grosso modo une union typé.
Tu peux aussi définir ta fonction dans une typeclass, c'est à dire qu'elle prendra un type qui match la typeclass (i.e. l'interface). Elle est donc plus ou moins polymorphique.
Elle peut aussi être polymorphique car on se fout des types que du passes. Example d'une fonction
swap (a, b) = (b, a)
, celle-ci prend un tuple de type(t, t')
et retourne un tuple de type(t', t)
, en ce sens elle est polymorphique.[^] # Re: typage statique automatique ?
Posté par Aluminium95 . Évalué à 2.
Peut-être est-ce un abus de langage, mais par interface, je voulais dire comme en Java par exemple
Suivi de
Dans ce document. Dans l'exemple en haskell, il suffit de faire une première substitution
classe -> type
puisinterface -> classe
pour avoir quelque chose d'équivalent.Ou alors comme les
protocols
en Objective-C (bien que je ne sois pas sûr que cela représente la même chose).En fait, il n'y a pas de surcharge en haskell (ou presque) : deux fonctions qui ont le même nom doivent être dans des espaces de nommage différents, sinon le compilateur râle. Or, si on veut définir une fonction sur les
Bar
et une fonction sur lesFoo
il faut deux types différents pour la fonction, et donc on n'y arrivera pas … (enfin, pas à ma connaissance).Pour ne pas parler dans le vide voilà un exemple concret
Dans l'exemple ci-dessus, la fonction polymorphe dit qu'il existe pour le type
a
une fonctionaffiche :: a -> String
. Si on veut être polymorphe sur plusieurs classes avec des fonctions qui sont communes … comment choisir ? Soit on crée une classe parente qui regroupe les éléments communs, soit on ne sait pas ce qu'on veut vraiment faire.[^] # Re: typage statique automatique ?
Posté par Guillaum (site web personnel) . Évalué à 3.
Petite remarque pour ceux qui ne lisent pas l'haskell couramment. La fonction
afficherListe
prend une liste dea
et renvoie une String.a
marche pour n'importe quelAffichable
, mais il faut bien noter que la liste ne peut contenir qu'un seul type et le même.En gros, si on a
foo
etbar
des valeurs de typeFoo
etBar
, eux même instances deAffichable
, on peut faireafficheListe [foo, foo, foo]
ouafficherListe [bar, bar, bar]
mais pasafficherListe [foo, bar]
.En gros ce n'est pas l'équivalent des appels virtuels en C++ ou de l'héritage en python, tout est résolu à la compilation et une instance spécifique de
afficherListe
est générée pour chaquea
possible. (Ce n'est pas complètement vrai ;)La notion de polymorphisme à l'execution (i.e. héritage virtuel) est plus complexe en haskell…
[^] # Re: typage statique automatique ?
Posté par barmic . Évalué à 4.
Oui, oui c'est juste que je pensais que l'inférence de type était capable de faire ce lien de lui même. Comme c'est le cas avec les lambdas de Java justement.
On passe la référence à la première méthode à la méthode
bar
et lui vérifie qu'elle peut être utilisée comme unMyType
.Un type somme permet ça, non ?
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: typage statique automatique ?
Posté par Aluminium95 . Évalué à 1. Dernière modification le 30 mai 2016 à 20:23.
Par surcharge j'entends « déclarer plusieurs fonctions avec des types différents mais qui ont le même nom et la fonction la plus spécifique1 est choisie à l'évaluation » (sous-typage). Ce truc (qui est faisable) n'est pas directement dans haskell, mais oui, on peut toujours l'émuler avec un type somme, mais ça devient très vite lourd quand même : il faut gérer manuellement dans chaque fonction chaque cas, ou alors faire des fonctions indépendantes pour chaque « sous-type » et puis faire une grosse fonction qui détermine quelle fonction appliquer …
1 : et là il faut savoir ce que l'on fait. On peut décider de prendre la plus spécifique sur le terme d'entrée et générique sur le terme de sortie, ou l'inverse, enfin, il y a un choix à faire.
[^] # Re: typage statique automatique ?
Posté par Axioplase ıɥs∀ (site web personnel) . Évalué à 4.
Ah ?
Pour avoir écrit du Haskell professionnellement, je dirais que je ne suis pas d'accord. En Haskell, quand tu commences à écrire du code un peu compliqué, tu dois écrire tes types bien souvent si tu veux que ça compile. En revanche, ce problème s'est beaucoup moins posé en OCaml…
[^] # Re: typage statique automatique ?
Posté par Guillaum (site web personnel) . Évalué à 2. Dernière modification le 01 juin 2016 à 18:09.
Tu as la chance de faire du haskell professionnellement, j'aimerais bien ;)
De mon expérience amateur, je met des types sur les "top level", mais c'est tout. Il y a deux-trois cas tordus, quand tu commences à abuser de typeclass ou
OverloadedLists
/OverloadedStrings
, mais en pratique cela ne m'a jamais trop embeté.Mais il est clair que ma remarque était en comparaison avec par exemple C++ où tu met des types partout.
(Edit: la raison qui fait que ce problème existe moins en OCaml est surement du justement à l'absence de typeclass, donc l'absence des ambiguïtés qui peuvent en résulter).
# On s'en bat le steak
Posté par goabbear . Évalué à -6.
Python marche bien sans typage, là c'est se faire ch… pour rien. Tu veux des perfs, tu écris en C. Tu sais pas le type que va te balancer la fonction? C'est que le dev ne l'a pas bien documentée, et à la rigueur tu fais un test, tu lis le code et tu comprends. Franchement, le typage statique, on s'en bat le steak
[^] # Re: On s'en bat le steak
Posté par xcomcmdr . Évalué à 10.
Pas du tout. Ça permet de se rendre compte de beaucoup d'erreurs à la compilation.
Je préfère un programme qui refuse de compiler qu'un programme qui va planter uniquement quand je fais ceci ou cela (il faut que j'y pense, que je test, et cela à chaque fois. Pourquoi ne pas faire travailler la machine ?)
"Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 3.
Effectivement, quel est l'intérêt de laisser compiler un code qui de toute façon ne pourra que planter à l'exécution ?1 et cela, tout en refusant de le compiler parce qu'il est mal indenté… Il y a vraiment des choix de design que je ne comprends pas dans ce langage. :-/
ce n'est pas comme s'il n'existait pas des techniques éprouvées depuis des décennies pour éviter ce genre de désagréments. ↩
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par flan (site web personnel) . Évalué à 7.
Avant même la compilation : dès l'écriture du code. Pourquoi compiler du code alors que la compilation échouera forcément ?
Quand je code en Python, j'utilise PyCharm qui fait de l'inférence de type assez efficace (d'autant plus que je l'aide pas mal à ce sujet) et me force à respecter la PEP008. C'est maintenant rarissime que j'obtienne des erreurs de type au lancement.
[^] # Re: On s'en bat le steak
Posté par Guillaum (site web personnel) . Évalué à 2.
Rarissime c'est trop ;) De mon coté, je suis moins chanceux / doué et je ne compte plus le nombre de fois où je lance un code python qui se vautre immédiatement sur une
TypeError
, où un mauvais nombre d'argument à une fonction, où une typo dans le nom d'une fonction ou même une erreur d'imbrication (Je pensais que j'avais un object mais en fait je manipulais une liste d'objets). Et le pire c'est quand cela ne se vautre pas immédiatement ;)Pour ne pas me contenter d'un troll, quel type d'erreurs obtient-tu au lancement qui ne sont pas des erreurs de type ? Plus le typage est avancé, plus de nombreuses erreurs de "logique" peuvent être captées par le système de type lors de la compilation, enfin du moins c'est l’expérience que j'ai.
[^] # Re: On s'en bat le steak
Posté par flan (site web personnel) . Évalué à 3.
Malheureusement, l'inférence de type n'est pas parfaite, surtout avec les bibliothèques tierce-partie qui n'aident pas toujours beaucoup (voire rarement) à ce sujet :(
Ça serait pas mal de n'avoir que les erreurs de type, mais il y a tant d'autres raisons… l'algo peut être faux, on peut oublier de vérifier certaines conditions, les cas limites (j'essaie d'utiliser hypothesis pour ça), mauvaise gestion des exceptions, …
Bon, globalement j'ai quand même bien plus de chances que ça fonctionne dès le premier lancement que l'inverse depuis que je fais les choses proprement (PyCharm + indications de type + tests + hypothesis + intégration continue).
[^] # Re: On s'en bat le steak
Posté par Juke (site web personnel) . Évalué à 2.
D'ailleurs ça serait pas mal qu'hypothesis sache lire les annotations et en deduise tout seul les types.
[^] # Re: On s'en bat le steak
Posté par Guillaum (site web personnel) . Évalué à 7.
Je pensais comme cela il y a quelques temps. J'ai fais 7 ans de python acharné sur des projets libres ou sur mes projets persos et j'étais convaincu qu'une bonne couverture de test était la solution à tous les problèmes. J'ai fais des présentation en entreprise où à l'université à ce sujet. J'ai appliqué cela pendant 6 ans en entreprise. Puis un jour j'ai découvert que la couverture de test à 100 % n'existait pas, que souvent du code n'avait pas été documenté parce que pas le temps et que de toute façon, même avec des tests et de la documentation, le refactoring en python me péterait toujours à la gueule en production dans le petit bout de code bien tordu qui appelait la fonction que j'avais raté. C'est l'example donné dans la présentation sur mypy de ce journal, quand tu as une method
dosomething
sur un objet et que un grep dans ta base de code te donne 150 instances dedosomething
, lequelles tu dois modifier ?Puis j'ai découverts ce qu'apporte le typage statique, en haskell principalement (mais j'ai fais un peu de rust et de ocaml). Quand tu refactor tu as une quantité d'erreur que tu ne peux plus faire du fait du typage statique. Un bon système de type évite aussi des erreurs immonde qui peuvent arriver avec un mauvais système de type (comme des casts implicites entre des matrices et des booléens, histoire vécue qui m'a fait perdre 10% de performance pendant 6 mois sur un logiciel "haute performance"). Un bon système de type évite aussi les null pointer exception. Un bon système de type te permet de modéliser avec les types et une fois que tu as trouvé les bonnes abstractions, tu peux écrire du code très rapidement. Et un bon système de type ne râle que quand tu fais des bêtises.
Après, un mauvais système de type, c'est juste lourd et contraignant, cela râle tout le temps pour rien, et malheureusement on se tape beaucoup de mauvais système de type comme example de ce qu'est le typage. Si votre idée d'un système de type est celle présentée dans Java ou C++, je vous invite vraiment à vous refaire une idée en allant voir ce qui se fait à coté. Rust est un bon point d'entrée, Haskell est énorme mais fait peur. Ocaml c'est sympa.
Bonne découverte.
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 2.
Honnêtement, pour ce qui est de leur système de types, c'est du pareil au même entre les deux; en dehors de l'absence de typeclasses en OCaml mais plus pour longtemps (modular implicits). Leur système prend leur inspiration dans la correspondance de Curry-Howard (ou correspondance preuve-programme, correspondance formule-type), ce qui en fait un système très expressif mais très exigeant : quand le compilateur gueule, c'est comme un mathématicien qui se plaint que la preuve d'une énoncé n'est pas correcte.
Là où Haskell peut faire peur, c'est que comme il fait du fonctionnel pur il faut recourir à des monades pour les effets de bords : ça peut effrayer au premier abord. OCaml de son côté permet de mélanger les paradigmes impératif, fonctionnel et objet; et faire de la réutilisation de code (comme pour le paradigme objet) via les variants polymorphes (je ne sais pas s'il y a un équivalent en Haskell).
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par Guillaum (site web personnel) . Évalué à 3.
C'est ce qui m'a séduit dans Haskell.
Non, pas l'impression que ce soit possible en effet.
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 8.
Je peux comprendre, les effets de bords et les valeurs mutables ça peut avoir des comportements surprenants :
La valeur optionnelle, qui est mutable, est initialisée une fois et partagée par toutes les instances ! \o/
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par Nairwolf . Évalué à 3.
Je suis entrain de me former à Python. Comment expliquer ce comportement, et comment faudrait-il modifier le code pour avoir un bon fonctionnement ?
Merci ;)
[^] # Re: On s'en bat le steak
Posté par n0wic . Évalué à -6. Dernière modification le 01 juin 2016 à 15:01.
Pour l'explication j'imagine que la raison c'est que feuille est déclarée à l'intérieur de la classe alors que dans le code que tu as écris c'est un argument de init() donc il est appelé en dehors de ta classe.
Ce qui fait que les deux objets on les arguments en communs.
Dans certains cas cela peut être un avantage.
[^] # Re: On s'en bat le steak
Posté par n0wic . Évalué à -6.
la ligne self.feuille = feuille ne sert a rien désolé
[^] # Re: On s'en bat le steak
Posté par barmic . Évalué à 4.
Rien à voir avec la classe. On peut reproduire le problème avec juste une méthode :
Les paramètres optionnels sont évalué une seule fois lors de la définition de celle-ci ensuite tu te trimbale toujours la même référence1. Tu obtiens aussi des comportements marrants si tu fais un
time.time()
.Dans le cas générale2 les valeurs par défaut des paramètres en python devraient être immuables (donc pas de structure de données que tu modifie, pas de valeur qui dépendent d'un contexte) et le reste du temps tu donne la valeur
None
et tu gère cette valeur dans le reste de ton code.Note : ça reproduit le comportement des variables statiques du C, mais il vaut probablement mieux utiliser les générateurs (avec
yield
) si tu as ce genre de besoins. ↩c'est à dire tout le temps sauf quand tu sais pourquoi tu ne le fais pas ↩
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: On s'en bat le steak
Posté par n0wic . Évalué à -8.
None est une valeur et elle est définie lors du premier appel de la méthode, c'est donc un empreint au caractère d'héritage propre aux langages objets.
En revanche je ne comprend pas ton code qui ressemble a l'héritage multiple des classes car il renvoit un objet de type class list qui semble être dépendant le structure immuable que tu as crée et pourtant en passant issubclass() et isinstance() je n'ai aucun retour positif.
[^] # Re: On s'en bat le steak
Posté par barmic . Évalué à 3.
Je vois plus ça comme le
static
de C, mais peut être. Ce que je voulais montrer c'est que le problème existe dans la manière que python gère les valeurs par défaut des paramètres que ce soit des méthodes d'une classe ou une fonction.Hein ? Il n'y a rien d'immuable.
[]
est une liste vide de typelist
et doncfoo
renvoie aussi un objet de type list (dans les exemples que j'ai donné).Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: On s'en bat le steak
Posté par flan (site web personnel) . Évalué à 3.
La méthode recommandée est :
```
[^] # Re: On s'en bat le steak
Posté par Florent Fourcot . Évalué à 3.
« x is None » et « x == None » donnent effectivement le même résultat, mais pas exactement les mêmes performances :
L'opérateur is est plus simple et plus rapide, il ne vérifie que l'égalité entre deux id.
[^] # Re: On s'en bat le steak
Posté par flan (site web personnel) . Évalué à 2.
Oui, c'est d'ailleurs pour ça que j'ai mis « x is None » ; cependant il n'est possible que parce qu'il n'y a qu'une seule instance de
None
.[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 3.
Le comportement observé est du au fait que les arguments optionnels ne sont évalués qu'une seule fois lors de la définition de la fonction. S'ils sont mutables, et qu'on les affecte comme attribut aux objets de la classe, ils ne sont alors pas propre aux objets mais deviennent des variables de classe. Pour éviter cela, si ce n'est pas ce que l'on souhaite, il faut utiliser la solution de flan et utiliser la valeur
None
comme paramètre par défaut, puis tester dessus pour avoir des listes vides différentes pour chaque objet de la classe.En OCaml, par exemple, les arguments optionnels sont implicitement représentés par le type polymorphe
'a option
:Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par GuieA_7 (site web personnel) . Évalué à 3.
Dans ce cas particulier, j'aime bien aussi :
Défaut:
- il y a forcément une copie, même si elle est potentiellement inutile (et du coup ça illustre bien comment, par exemple, les notions de propriétaire/temps de vie de Rust sont intéressantes pour des projets gros et devant être performants).
Avantages:
- puisque la liste nous appartient on peut la modifier sans trop de crainte (évidemment si les objets contenus sont mutable ils pourraient être modifiés de l'extérieur malgré ça).
- la fonction accepte du coup n'importe quel objet itérable (comme un générateur par exemple). Dans ton code
items
doit posséder une méthodeappend()
.- même si ça ne dispense pas d'un commentaire sur l'API, la signature est plus explicite.
Dans le cas où 'items' n'a pas vocation à être très long (et qu'on n'a pas un 2ème argument du même genre), on pourrait aussi faire:
du coup l'appel à la fonction serait
Test(1, 2, 3)
plutôt queTest([1, 2, 3])
.Comme quoi le "one way to do it" c'est (heureusement) une intention plus qu'une réalité :)
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 1.
C'est même plus propre par rapport à ma solution et au problème initial : si on passe une liste dans l'appel au constructeur, on peut se retrouver au même désagrément sur les effets de bords.
Là où avec ta solution, en plus d'accepter plus de type comme paramètre du constructeur, l'attribut est un peu mieux décorrélé (mais pas totalement) de l'argument.
Je ne sais pas ce que sont les notions de propriétaires/temps de vie en Rust, mais je dirais pour ma part : comme quoi le fonctionnel pur (ou l'observationnellement immuable) ça a son intérêt ! :-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par GuieA_7 (site web personnel) . Évalué à 2.
Oui c'est ce que je disais dans mon commentaire avec "si les objets contenus sont mutable ils pourraient être modifiés de l'extérieur malgré ça"
Tout à fait, d'ailleurs en Rust les variables sont immuables par défaut. Mais comme c'est un langage système qui vise aussi les performances optimales, il y a moyen de déclarer explicitement des variables mutables. Et le système d'emprunt de références va tout de même garantir qu'un seul endroit du code a la responsabilité de modification à un instant T. Ce qu'on perd en souplesse (le compilateur est encore plus hargneux, puisqu'en plus des règles sur types, on doit respecter les règles sur les références—et le système de typage est moins puissant qu'en Haskell/OCaml), on le gagne en performance.
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 1.
J'avais bien compris, mais je préférais illustrer par un exemple pour que ceux qui se demandent comment éviter ces effets de bords puissent avoir une illustration du problème. Résultat cela m'a amené à regarder s'il n'y avait pas une fonction de copie profonde d'une structure en Python, et voilà :
copy.deepcopy()
:-)La décorrélation est totale ! \o/
C'est un système de lock pour les problèmes d'accès concurrents ? Ou si plusieurs structures se partagent une référence, seule une peut la modifier jusqu'à ce qu'elle soit désallouée et une autre en prend le contrôle, et ainsi de suite ? (j'essaye de traduire les notions de propriétaire/temps de vie).
Il y a des travaux pour améliorer encore celui d'OCaml pour pouvoir faire du polymorphisme ad-hoc et de la surcharge d'opérateur en utilisant son système de module et les foncteurs. Et on peut aussi manipuler des références (mais là c'est sans garde-fou) et faire de la programmation système en OCaml (la seule limite actuelle, c'est le GC qui ne gère pas les accès concurrents sur la major heap ce qui pose problème pour le parallélisme, cf multicore OCaml).
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par GuieA_7 (site web personnel) . Évalué à 2.
Non pas du tout. Alors n'étant (encore :) ) pas un spécialiste du langage, mon explication va être un peu légère. En gros tout est fait à la compilation (un lock serait fait au runtime), où le compilateur, via son borrow checker, vérifie plusieurs choses comme:
- les temps de vie des objets. Par exemple si on retourne une référence sur un attribut d'un objet, alors le temps de vie de cette référence ne peut excéder celui de l'objet lui-même. Le borrow checker peut donc détecter des cas où on essaye de stocker cette référence en dehors du scope de vie de l'objet lui-même (un cas de dangling pointer comme on peut en trouver en C/C++).
- lorsqu'on passe une référence mutable à une fonction, on lui délègue la responsabilité de cette référence, et on ne peut plus utiliser cette référence dans la suite du code.
En pratique cela oblige à écrire le code de manière à satisfaire le borrow checker (sachant qu'il est régulièrement amélioré pour être moins stupide—il y a des fois où il est évident pour l'humain qu'il n'y a pas de problème, mais ça ne l'est pas pour le borrow checker). L'écriture des structure de données classiques est donc tout un art (heureusement le boulot est déjà fait !).
Au final c'est un peu comme quand les langages fonctionnels, qui en promouvant l'immuabilité et l'abandon des variables globales par exemple (ce que Rust fait aussi), obligent à penser son code d'une manière naturellement plus robuste.
J'espère avoir été clair.
Ocaml permet de faire de la programmation système (ce qui ne veut pas dire grand chose, mais disons qu'un langage compilé nativement, avec de bonnes performances, qui permet de manipuler threads/sockets…, rentre dans cette appellation), mais à l'instar de Go par exemple, il ne peut pas être qualifié de langage système à cause de la présence obligatoire du GC (attention je n'ai jamais dit que tu avais affirmé que c'était un langage système—mais la précision me tient à cœur), tandis que Rust peut se passer complètement de runtime. Ça n'en fait pas un meilleur langage, c'est juste qu'ils ont des desseins différents.
Au passage, un petit article, que j'ai lu récemment, sur les problèmes qu'un GC peut poser (ici en Haskell): https://blog.pusher.com/latency-working-set-ghc-gc-pick-two/
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 1. Dernière modification le 05 juin 2016 à 10:38.
Je vois un peu mieux le principe du borrow checker, je regarderai peu être plus en détail la façon dont Rust gère les références. Cela étant, au début de Rust il y avait un GC qu'ils ont abandonné, mais l'idée de remettre de la gestion automatique est toujours présente, cela semble être néanmoins compliqué. Il y a deux articles sur le sujet sur le blog d'un des membres de l'équipe du compilateur :
Sinon quelle différence fais-tu entre un langage qui peut faire de la programmation système et un langage système ? Il y a des personnes qui écrivent des noyaux, sous la forme d'unikernel, en OCaml :
Il y a bien des problèmes de latence liés au GC qui peuvent apparaître (contrairement à la gestion totalement manuelle du C par exemple) mais cela se gère aussi en adaptant son fonctionnement à l'application. Je ne connais pas le fonctionnement du GC de Haskell (mais d'après l'article c'est similaire à celui de OCaml), mais pour celui de OCaml ce chapitre de Real World OCaml en détail bien le fonctionnement.
Le premier commentaire de l'article que tu cites renvoie sur un benchmark (dont je n'ai pas estimé la pertinence) qui mesure la latence d'un serveur web qui stocke une hashtable (pour le serveur web en OCaml, il utilise d'ailleurs une bibliothèque développée par l'équipe de MirageOS) :
J'ai eu une polémique récemment sur le coup de la latence du GC de OCaml en rapport à ce benchmark du benchmarkgame de chez Debian. J'avais mal estimé le rôle du GC dans les mauvais résultats : comme pour le cas de ton article, il y a des objets trop gros qui restent vivants trop longtemps. En adaptant la taille de la minor heap on obtient de meilleurs résultats (j'utilise aussi la bibliothèque functory, qui fait une sorte de MapReduce, pour le parallélisme ) :
avec ce code, sur ma machine à 4 cœurs, j'obtiens comme résultat standard avec un
time btree 20
:là où avec le code C le plus performant je suis dans cet ordre de grandeur :
c'est pas si mal et bien mieux que le
25.82 s
du meilleur code OCaml du test. D'autant que, n'étant pas moi même programmeur (je suis mathématicien et logicien, pas informaticien), il est fort probable qu'un spécialiste du langage puisse encore améliorer cela.Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par GuieA_7 (site web personnel) . Évalué à 3.
Alors quelques précisions:
- le GC qu'il y avait au début de Rust n'étant pas un vrai GC, mais du comptage de références (parce que le travail n'était pas terminé).
- l'utilisation du GC a toujours pensée comme optionnelle : en gros on déclarait une référence comme étant "garbage collectable" (le langage a bien changé mais c'était dfans mon journal sur Rust 0.7 ).
- même quand le GC était partie intégrante du langage, le but était de s'en servir le moins possible, et par exemple la bibliothèque standard a vite atteint l'objectif de 0 GC.
- avec le temps l'équipe s'est aperçu qu'elle pouvait aller plus loin que ce qu'ils pensaient au début, et que le système de typage permettait de sortir le GC et de le penser comme une dépendance externe, sans besoin d'une syntaxe dédiée.
La gestion est déjà automatique, via le système de référence que j'ai (mal) expliqué, mais un GC optionnel serait un confort pour certaines partie de code. Ce qui complique la tâche ce sont les prérogatives assez élevées qu'imposent les buts du langage ('You pay for what you use' entre autres), quand Haskell/Ocaml/Java/Python… n'ont pas à se soucier de tels problèmes (mais ils en ont d'autres évidemment).
La différence peut être mince, comme tu le soulignes avec les unikernels, mais je dirais qu'un langage système permet de maîtriser suffisamment parfaitement le comportement du code pour pouvoir écrire du code temps-réel (au sens temps réel dur, pas au sens globalement performant).
C'est très gentil de t'être donné la peine d'écrire un commentaire aussi argumenté, mais tu prêches un convaincu :) . Je sais très bien que Haskell ou OCaml ont de très bonnes performances dans la majorité des cas. Mais justement le lien que j'ai donné explique bien que Haskell va donner de très bon débit, mais va avoir des problèmes de latences ; ton benchmarck au final est équivalent à tester un débit moyen, mais ne va pas du tout tester les problèmes de latences, parce que 1) c'est pénible à tester 2) ce n'est pas ton problème.
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 2. Dernière modification le 05 juin 2016 à 21:43.
C'est globalement ce que j'en avais compris des deux articles du membre de l'équipe chargée du compilateur. L'idée étant de pouvoir rajouter un smart pointer
Gc<T>
dont l'intention serait similaire àRc<T>
. Si la gestion automatique est basée sur du comptage de références, je comprends pourquoi la politique de gestion est drastique via le borrow checker (comme éviter des cycles dans le graphe des pointeurs qui sont gérés par des éphémérons dans le cas du GC d'OCaml).Mais dans ce cas ne faut-il pas une gestion totalement manuelle de la mémoire pour faire ce genre de chose ? Un langage tel que Rust peut-il s'attaquer à ce genre de problématique avec son mode de gestion automatique ?
Effectivement mon benchmark ne montre que le débit moyen, j'utilise
time
, mais parce que n'étant pas programmeur je ne connais pas les outils pour faire de la mesure de latence. En réalité, la modification que j'ai apporté au code OCaml de départ est plus un sketch of proof pour régler des problèmes de latence liés au GC. Ce qui fait que je suis passé de25 s
à5.5 s
est que j'ai fait disparaître une action du GC qui est responsable de la latence observée dans l'article que tu as cité sur le GC de Haskell : un parcours de la major heap.Le tas est composé de deux memory pool : une minor heap et une major heap. La première sert pour les objets à courte durée de vie et est très efficace pour ce qui concerne les temps d'allocation et de désallocation mémoire. Lorsqu'elle est pleine le GC nettoie et transfert les structures encore en vie vers la major heap, puis à chaque fois qu'il fait cela, il nettoie par tranche cette major heap (de manière incrémentale) : c'est ce processus en mode stop the world qui prend plus du temps et génère de la latence. Dans mon code, j'ai adapté la gestion de la mémoire pour ne pas y avoir recours. Cela se passe en trois temps :
Sans cela j'avais un grand nombre de major collection, qui sont responsables des latences observées dans le fonctionnement du GC, là où maintenant je n'en ai plus aucune. ;-)
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par GuieA_7 (site web personnel) . Évalué à 2.
Je pense que oui, parce que la gestion est certes automatique, mais prédictible (en plus pour les applications vraiment temps réelle l'allocation dynamique est proscrite en générale, et Rust permet ça aussi grâce aux allocateurs personnalisés).
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 1.
Dans ce cas, ce n'est peut être pas totalement hors de portée de OCaml non plus. Dans cette direction, outre sa date de sortie ainsi que le premier exemple qui peut engendrer un stack overflow si on ne fait pas attention et n'est pas très optimisé, il y a cet article qui donne des pistes de réflexions.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par barmic . Évalué à 2.
Il y a un outillage autour d'Ocaml pour mesurer ce genre de choses ?
C'est très intéressant (merci !). En fait c'est un gc générationnel assez classique :)
Ce qui est marrant c'est ta solution (en tout cas pour moi). Dans le monde JVM, on va plutôt essayer de faire passer ses objets dans une vielle génération et à limiter la création de nouveaux objets. Pour ça on va utiliser un pool d'objets, que l'on va réutiliser autant que possible. Ainsi on se protège des problèmes de débit réduisant la pression sur le débit (il y a d'autres gains potentiels comme le fait de réduire la fragmentation mémoire).
Il est possible de jouer sur les tailles de génération de la JVM, mais ça se fait de manière externe. Ce sont des paramètres à passer à la JVM :
-XX:NewRatio=
pour indiquer le ratio entre la young et la old génération-XX:NewSize=
taille minimale de la young génération-XX:MaxNewSize=size
taille maximale de la young générationLa doc est là pour ceux qui ça intéressent : https://docs.oracle.com/cd/E19900-01/819-4742/abeik/index.html
Du coup c'est des paramètres pour toute la JVM sur toute sa durée de vie. Tu ne peux pas régler ça pour une partie de ton programme.
D'ailleurs comment se passe une redéfinition plus petite de la minor heap en Ocaml ? Il fait un stop the world et envoie tout ce qu'il faut dans la major heap ?
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 2.
N'étant pas développeur, ce n'est peut être pas très conventionnel ce que j'ai fait : mais ça marche ! :-P
Disons que je me suis adapté au contrainte du test, normalement on évite de faire autant d'allocation dans du code réel. Selon le cas pratique qui amène ce genre de calcul, je dirais même que l'on peut totalement se passer d'allocation. C'est du code qui pourrait ressembler à un interpréteur d'un langage algébrique très très mal optimisé. En pratique, on ferai plutôt comme dans ce journal avec une solution en C++ (comparer le premier commentaire du journal avec le code de ma fonction
worker
;-).En OCaml que ce soit avec du bytecode ou du code natif, on peut aussi gérer le fonctionnement du GC par des variables d'environnement : voir la doc à la variable OCAMLRUNPARAM (c'est ce que j'ai fait au départ avant de le mettre en dur dans le code).
Une redéfinition de la taille de la minor heap entraîne une collection mineur : il nettoie, envoie ce qui est encore en vie dans la major heap, et redimensionne. Documentation du module GC.
En fait j'ai opté pour cette solution parce que l'algorithme du test consiste à allouer un arbre binaire parfait de profondeur
d
(la fonctionmake
) puis à faire un checksum dessus (la fonctioncheck
) et répéter cela sur de nombreux arbres (le paramètreiter
). Alors j'ai adapté la taille de la minor heap (le memory pool le plus efficace) pour qu'elle puisse contenir deux arbres les plus profonds qu'il y aura à allouer. :-)Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par kantien . Évalué à 2.
Apparemment c'est encore léger et ce n'en est qu'au début, il y a des efforts à faire de ce côté (c'est apparu avec le nouveau compilateur 4.03 et les efforts de Damien Doligez pour réduire le worst-case latency).
Il y a un message (de gasche ;-) sur la mailing list : Measuring GC latencies for OCaml program dans lequel il renvoie à son article de blog plus complet Measuring GC latencies in Haskell, OCaml, Racket en réaction au billet pointé par GuieA_7 sur les problèmes de latence du GC de Haskell.
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: On s'en bat le steak
Posté par steph1978 . Évalué à 5.
Parle pour toi.
J'adore écrire en Python. Mais il est parfois frustrant de tomber sur une erreur au runtime parce qu'on a fait une faute de code qui aurai été détecter à la compilation (et donc par l'IDE si tu en utilises un).
Tu peux vouloir de bonne perf sans forcément vouloir les perfs du C et surtout sans te compliquer la vie à écrire du C.
Oui au typage statique ou à l'inférence de type !
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.