[Nitération 9] Nouvelles d'août

12 views
Skip to first unread message

Jean Privat

unread,
Sep 2, 2014, 10:40:04 AM9/2/14
to nit...@googlegroups.com

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

Nouveaux constructeurs

Certains ne les attendaient plus, mais ils sont là, les nouveaux constructeurs. 9d4906d

Spécification simplifiée

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 :

  • simplifier le code de 98% de constructeurs: plus besoin d'assigner manuellement les attributs ;
  • gérer mieux l'héritage multiple des constructeurs : les init sont des redéfinitions d'un init introduit dans Object;
  • simplifier l'injection de comportement dans les constructeurs : vu qu'on a pas à s'occuper de linéariser ou des paramètres;

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

Setters, attributs et linéarisation

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:

  • lui coller une valeur par défaut
  • lui coller l'annotation noinit

La 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

Conversion

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.

Travail à finir

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.

Annotations utilisateurs

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:

  • le premier des annotations primitives (comme lazy) nécessaires à la bonne construction du modèle
  • le second des annotations déclarées au niveau des modules, et bien évidemment importées, ceux-ci concernent principalement les annotations de plate-formes.

Sys.run

Le 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.

Un peu d'IA

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.

Finalisation des objets dans le GC

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.

Histoire à succès du raffinement : hash_debug

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.

Contrib et outils

  • La famille d'options --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. 7ffb705
  • Générateur de lib de sérialisation nitserial. Lancez nitserial sur votre bibliothèque et générez du code de sérialisation/dé-sérialisation. 9e60f6f
  • Amélioration de jwrapper, le générateur d'enveloppe automatique pour fichiers .class Java. 4bb08a7
  • Refactorisation et amélioration de la calculatrice gtk. 4d4faaf

Performances

En utilisant des techniques de pointe en profilage, simulation et instrumentation, diverses choses ont été détectées et optimisées.

Dans les bibliothèques:

Dans le code généré:

  • Court-circuitage des intervalles littéraux réactivés. 8df429d

Dans le code des outils:

  • Pas de revisite des annotation pour chaque phase. 1b97ef5
  • Optimisation monstrueuse de nitdoc. df1e08b

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

Tricher pour gagner...

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:

  • nith.nit : code plus petit que nitg avec les options
  • --erasure : moins de tables et de coloration
  • --no-check-all : pas de temps à perdre à générer des tests
  • --semi-global : inutile de compiler des machins morts, etc.
  • on mets pas --rta, c'est du travail rta.

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 :

  • --semi-global : plein d'optimisations
  • --no-check-all : pas de temps à perdre à exécuter des tests
  • --separate (implicite) : en fait y'a pas de différence notable avec --global ou --erasure a la place
  • --rta (implicite avec --separate) : pour nourrir --semi-global.

Qu'est ce que ça donne sur ma machine (tout à chaud, meilleur temps de 5 ou 6 exécutions d'affilé)

  • ./nitg nitg.nit : 7.34s (temps de référence)
  • ./nitg nith.nit --semi-global --no-check-all : 5.76s (-21%)
  • ./nith --erasure --no-check-all --semi-global nith.nit -o nith2 : 3.02s (-59%)

Et pourquoi pas tricher encore plus en désactivant le GC?

  • NIT_GC_OPTION=large time ./nith --erasure --no-check-all --semi-global nith.nit -o nith2 : 1.34s (-81%)

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.

  • NIT_GC_OPTION=large time ./nith2 --erasure --no-check-all --semi-global nith.nit -o nith2 : 1.75s (-76%)

Bibliothèque

  • Queue et cie ont été retravaillés pour être plus génériques. 32616e8
  • Sequence::prepend et compagnie. ba7c1ae
  • Enveloppe pour sendmail, et donc envoyer des emails. 6cf8346
  • Vérification de l'existence de groupes et utilisateurs Unix f014d59
  • Strings et UTF-8, la saga continue. f935c5f1cae0ed
  • Android: enveloppe pour les intentions (qui permettent à une application de lancer d'autres applications). 73c6404
  • mnit: répare un problème avec la lib opengles et les versions récentes de mesa. 4aa9ba6

Tests et intégration continue

Le répertoire des tests de régression a reçu un gros nettoyage.

  • On ne teste plus certains contrib plusieurs fois. d1ca8f4
  • Nettoyage des sav des tests et ajout de scripts pratiques. eb35f7e
  • Tests de la lib complète (et non juste quelques fichiers). 5388a6b

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:

  • vérification des signed-off-by.
  • vérification des espaces blancs.
  • vérification des licences.
  • vérification que chaque Makefile fonctionne. 6cc4c5c
  • vérification que le répertoire de travail reste propre après les divers tests.
  • vérification sur tous les engins pour les nouveaux tests ou ceux modifiés.
  • Surveillance des métriques de qualité: tasks (TODO et FIXME) et warnings
  • Surveillance du résultat individuel des commandes.

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é.

Reply all
Reply to author
Forward
0 new messages