Nous avons le plaisir de vous présenter le neuvième numéro de « Nitération », la lettre d’information du projet Nit.
Les statistiques combinées pour le mois d'août donnent:
$ git diff --shortstat v0.6.7..v0.6.8
326 files changed, 12389 insertions(+), 4064 deletions(-)
$ git log --first-parent v0.6.7..v0.6.8 | grep 'Pull-Request: #' | wc -l
44
Ont contribué (ou ont eu des patchs intégrés): Jean Privat, Alexis Laferrière, Frédéric Vachon, Lucas Bajolet.
Ce mois-ci, peut-être moins de contributions, mais des contributions relativement importantes et/ou attendues dont les constructeurs et les finaliseurs. De plus, c'est le 18 Août que la barrière psychologique des 500 PR fermées à été franchie.https://github.com/privat/nit/pulls?q=is%3Apr+is%3Aclosed
Certains ne les attendaient plus, mais ils sont là, les nouveaux constructeurs. 9d4906d
Pour résumer la spécification, l'idée est que le init n'a plus de paramètres et que la séquence var a = new A(5) est en réalité
var a = ALLOC(A)
a.b = 5
a.init
où a.b= est l'invocation d'un setter.
Pour rappel, l'intérêt de cette nouvelle spécification est de :
Un p'tit exemple:
class A
var i: Int
init
do
print i
end
end
var a = new A(5) # affiche 5
Dans l'exemple précédent, a.i=5 est invoqué avant a.init. donc i est correctement initialisé au moment du init.
Une p'tite sous-classe:
class B
super A
var j: Bool
init
do
print j
end
end
var b = new A(5, true) # affiche 5 et true
L'ordre des setters d'initialisation est relativement naturel.
Vu que les init et les setters font des effets de bords, il peut être important de comprendre l'ordre des invocations qui est : tous les setters (par ordre inverse de linéarisation) suivi du init (automatiquement linéarisé). Donc dans cet exemple on a:
a.i = 5
a.j = true
print 5
print true
Les setters d'initialisation sont des méthodes normales et redéfinissables. Pour l'instant, les seuls setters d'initialisation sont ceux associés aux setters automatiques des attributs non-initialisés.
Pour qu'un attribut ne soit pas associé à un setter d'initialisation, il y a donc deux façons:
noinitLa signature du new est issue de la liste des setters d'initialisation: ceux hérités suivis des nouveaux setters. En cas de conflit d'héritage multiple, une erreur est affichée et l'ordre des setters d'initialisation doit être explicité par le programmeur. Sauf que pour l'instant, il n'y a pas de moyen de contrôler explicitement la liste des setters d'initialisation (si quelqu'un a une idée de syntaxe élégante...).
Pour éviter les mauvaises surprises, les init sont automatiquement linéarisés. Cela est fait par l'injection d'un appel à super, si aucun super n'est présent. Un super explicite peut toutefois être intéressant lorsque l'on décide de faire du before ou du around.5e66595
class C
super A
init
do
i = 9000 # c'est pour illustrer, pas forcément une bonne pratique
super
end
end
var c = new C(5) # affiche 9000
print c.i # toujours 9000
Au cas où, l'annotation nosuper désactive l'ajout automatique de super implicite. À moins de savoir vraiment ce que vous faites, il ne faut pas l'utiliser vu que ça brise la linéarisation des init. 5e66595
Le code a été préventivement converti en contrôlant (via valeurs par défaut et annotations noinit) les setters d'initialisation.bbfd047
C'est un commit intéressant car lorsque la liste des setters d'initialisation est vide, l'ancienne spec des constructeurs et la nouvelle sont sémantiquement très proches. Ce qui fait que ce commit a pu être validé et intégré indépendamment du reste.
Il faut convertir le code qui utilise encore les vieux constructeurs. Reste aussi à trouver une syntaxe pour déclarer la liste des setters d’initiation.
Comme d'habitude avec Nit, on n'est pas encore complètement sûr de comment les gens vont utiliser cette nouvelle fonctionnalité (en bien ou en mal) ni quelles vont être les difficultés rencontrées. Donc on surveille.
Afin de gérer un peu plus correctement le nombre croissant d'annotations et les erreurs de typo associées, on détecte les annotations inconnues. b98139d
Les annotations sont séparées en deux groupes:
lazy) nécessaires à la bonne construction du modèleLe point d'entrée des programmes n'est plus Sys.main (le programme principal à la fin des modules) mais Sys.run une méthode normale de Sys qui par défaut invoque juste le main. dcc99d3
L'idée est de permettre à des modules avancés de contrôler le point d'entrée du programme.
Afin d'utiliser le langage Nit dans un cadre différent des programmes habituels, quelques problèmes simples d'IA ont été codés en Nit.
Ce travail a donné lieu à deux premières bibliothèques: ai::backtrack (d3f1f0f) et ai::search (8482b35), avec les programmes d'exemples qui vont bien (n-queens et n-puzzle).
L'objectif de ces bibliothèques est qu'elles soient relativement performantes, ce qui fait que lors du développement, de nombreuses bibliothèques standard ont vu leurs performances se faire améliorer.
Au final, les temps d'exécution et la mémoire consommée sont, de base, comparables à des programmes équivalents en Java. En activant les optimisations (comme -semi-global ou --global), les performances sont meilleures encore.
Toutefois, le profilage nous montre que le gros du temps dans ces programmes (parfois plus de 50% du temps) est passé dans le GC. Il reste donc encore de perspectives de gain.
L'un des plus vieux bogues référencé encore ouvert #69 est en passe d'être résolu. a1dd75c
Contrairement à la finalisation à la Java, l'idée ici est de ne réserver le processus qu'aux objets qui allouent des ressources autre que de la mémoire gérée par le GC.
Ces classes font pour la plupart partie d'enveloppes de bibliothèques, et doivent libérer des ressources systèmes ou de la mémoire allouée en C. Elles spécialisent alors l'interface Finalizable et implémentent la méthode finalize.
Un des intérêts du raffinement de classes est de pouvoir ouvrir des classes existantes indépendamment de leurs clients et instrumenter leurs services pour du profilage et de l'analyse. C'est que que propose le module hash_debug pour les hash-collections. 65ac643
L'idée est de raffiner les services d'ajout et d'accès aux collections de hachage pour collecter des statistiques (principalement les collisions), puis les présenter à la fin de l'exécution du programme.
Là ou ça devient vraiment intéressant, est qu'un tel raffinement est indépendant des clients et que Sys.run est raffiné pour injecter l'affichage automatique des statistiques à la fin du programme. Ce qui fait que hash_debug est autonome et peut être importé et utilisé via l'option -m.
Donc, pour savoir comment votre programme se comporte vis-a-vis des collisions dans vos collections de hashage, un simple -m hash_debug suffit à la compilation. On a rarement vu plus simple.
--no-check-* a été un peu nettoyée (bye bye --no-check-other, bonjour --no-check-null) et gagne l'option --no-check-all pour ceux qui veulent désactiver tous les tests dynamiques, gagner un maigre 6% et vivre dans la peur. 7ffb705nitserial. Lancez nitserial sur votre bibliothèque et générez du code de sérialisation/dé-sérialisation. 9e60f6f.class Java. 4bb08a7En utilisant des techniques de pointe en profilage, simulation et instrumentation, diverses choses ont été détectées et optimisées.
Dans les bibliothèques:
Array::to_s. 774ca99Dans le code généré:
Dans le code des outils:
Au final, en pratique, on gagne 15% sur le temps de compilation de nitg malgré les ajouts importants de fonctionnalité. Par contre on gagne 43% sur l'interprétation de l'interpréteur! Avant vs. Après
Quitte à chercher à compiler rapidement, pourquoi ne pas tricher?
Pour compiler rapidement il faut d'une part utiliser un compilateur performant, et d'autre part le faire travailler le moins possible.
Pour travailler le moins possible il faut compiler:
Pour un compilateur performant, il faut faire le minimum, donc nith (moins de phases que nitg), et le compiler avec un compilateur qui lui travaille vraiment, mais possiblement de façon lente, donc compilé avec les options :
Qu'est ce que ça donne sur ma machine (tout à chaud, meilleur temps de 5 ou 6 exécutions d'affilé)
Et pourquoi pas tricher encore plus en désactivant le GC?
OK. Un bootstrap n'est joli que quand un compilateur se compile lui-même de la même façon. Ça veux dire faire un choix entre travailler vite mais mal, ou bien mais lentement.
Sequence::prepend et compagnie. ba7c1aeLe répertoire des tests de régression a reçu un gros nettoyage.
Le serveur d'intégration continue, Jenkins, a également reçu pas mal d'améliorations, dont plusieurs cosmétiques.
De nouvelles vérifications ont été apportées à CIgithub, le job Jenkins qui surveille les pull requests sur github:
En passant, la barre psychologique du 1000-ème build de CIgithub a été atteinte. Ça fait pas mal de tests en un peu moins de 7 mois, date de mise en service du job.
Le test d'intégration qui surveillait nitcc a été généralisé aux autres outils et aux métriques de qualité. Pour la peine il a été renommé en nit test tools
Enfin, les 4 jobs de déploiement ont été fusionné en un seul: nit. Il intègre aussi, tant qu'à faire, des métriques de qualité.