Nous avons le plaisir de vous présenter le troisième numéro de l'année de « Nitération », la lettre d’information du projet Nit.
Les statistiques combinées pour le mois de mars 2015 donnent:
$ git diff --shortstat v0.7.2..v0.7.3
164 files changed, 7188 insertions(+), 2363 deletions(-)
$ git log --first-parent v0.7.2..v0.7.3 | grep 'Pull-Request: #' | wc -l
44
Ont contribué (ou ont eu des patchs intégrés): Jean Privat, Alexis Laferrière et Julien Pagès
Ce mois-ci un grand nombre de commits furent principalement axés sur les performances du compilateur nitc.
Le travail a eu lieu à trois niveaux:
src/ lorsque ces optimisations ont lieu sur du code partagé.Pourquoi travailler à optimiser le compilateur? Car c'est à date le programme le plus gros et le plus complet, donc assez représentatif d'un gros tas de code. C'est aussi le plus utilisé donc les gains profiteront à tous les utilisateurs du langage.
Les optimisations n'ont pas été faites au hasard. C'est en utilisant valgrind (et dans une moindre mesure perf) que l'on a détecté les parties du code qui se démarquent comme coûteuses.
Diverses optimisations de code faciles.
Meilleures abstractions pour manipuler les types primitifs ce qui permet de factoriser et cacher des services.
MType::is_c_primitive 89cf332Amélioration du temps passé à faire de la coloration en travaillant avec la hiérarchie des classes pour colorier les propriétés et types liés.
poset_from_mtypes used for type coloring. 2091a6fRend optionnels certains attributs pour que leurs initialisations ne coûtent que si l'on s'en sert.
Évite la sur-création d'itérateurs de tableaux en réutilisant les instances quand c'est possible.
Optimise certains services de FlatBuffer par court-circuitages et copies bas niveau.
Court-circuite les cas fréquents de Int::to_s.
Int::to_s shortcuts 0 and 1 f53967aÉvite de forcer chaque écriture dans les fichiers (flush), ce qui avait un impact non négligeable sur les temps noyau et réel.
La plupart des méthodes internes sont non-redéfinissables, donc autant les inliner.
Indique que l'évaluation des once et la construction des String littéraux est le chemin rare, pour optimiser les cas futurs où ceux-ci sont déjà évalués/construits
Applique le tagging des types primitifs tel que le faisait PRM. Le gain dans nitc est raisonnable (-5%) mais pour certains benches comme lib/ai/examples/queens.nit on gagne -20% d'un coup.
Implémentation optimisée des super-chaînes (celles avec des valeurs expansées dedans, comme "bonjour {nom}"). L'idée est d'éliminer les allocations inutiles en travaillant directement sur des NativeString que l'on essaye, en plus, de réutiliser.
En partant, le compilateur du mois précédent (v0.7.2) était à date l'un des compilateur Nit les plus efficaces qui travaillait vite pour produire du code cible optimisé. C'est lui que l'on va utiliser pour mesurer les performances. La machine de test est un HP EliteBook 840 avec bicoeur i5-4300U 1.90GHz, 8Go de mémoire et un disque SSD. Le compilateur C utilisé est gcc 4.9.2 (Debian 4.9.2-10).
Pour raccourcir les comparaisons et aller droit à l'essentiel, les mesures sont faites en compilant une version du compilateur avec lui-même (on ne compile donc pas exactement la même chose), l'idée est ici de mesurer globalement ce qui sépare v0.7.2 de v.0.7.3 pour quelqu'un qui développe le compilateur. Les nombres correspondent aux meilleurs résultats d'une dizaine d'exécutions d'affilée (à chaud). En gros le résultat d'une série de time ./nitc nitc.nit.
Pour nitc version 0.7.2, on a:
real 0m8.527s
user 0m7.268s
sys 0m1.704s
maxresident 626968k (with GC)
maxresident 1228988k (without GC)
valgrind 20.145 GIr (milliards d'instructions machine exécutées)
Pour nitc version 0.7.3, le gain est non négligeable.
real 0m5.087s (-40%)
user 0m5.024s (-31%)
sys 0m0.464s (-73%)
maxresident 566564k (with GC) (-10%)
maxresident 820416k (without GC) (-33%)
valgrind 11.650 GIr (-42%)
Les points d'optimisations restants sont peu nombreux (et parfois complexes); on peut citer:
FileWriter#write fait un appel inutile à to_c_string, qui fait une allocation et un boxing d'une NativeString (donc deux allocations)BufferedReader#read_all qui fait des manipulations inutiles et coûteuses de buffersD'autres points d'optimisations sont à mettre en œuvre mais dont il est difficile de prévoir à l'avance le gain.
Une nouvelle construction syntaxique permet de déclarer un couple de getter-setter facilement, ce qui est pratique dans les interfaces.
La syntaxe utilise le mot clé var et l'annotation abstract.
Ainsi, les deux classes suivantes sont équivalentes
interface Foo
var a: Object is abstract
end
interface Bar
fun a: Object is abstract
private fun a=(a: Object) is abstract
end
La proposition est controversée, on va voir ce qu'il advient.
Dans de nombreux objets, plusieurs services de base comme == ou to_s ont une implémentation basée sur les attributs. Traditionellement, il n'est pas possible d'implémenter de façon générale ces méthodes car la liste spécifique des attributs est nécessaire.
Une approche, inspirée de la façon dont la sérialisation est conçue, consiste à offrir un service automatique (au niveau méta dans le compilateur) qui collecte et retourne les attributs dans un Map (la clé étant leur nom). L'annotation pour profiter est auto_derivedu module deriving, le service s'appelle derive_to_map (le nom deriving est une référence au mécanisme de Haskell).
Au niveau de la bibliothèque, le module deriving fournit plusieurs services (par mixin de classes) qui utilisent derive_to_mapdans leur implémentation. Actuellement, deux classes sont implémentées (en fait ce sont des interfaces): DeriveEqual pour les servies == et hash; et DeriveToS pour le service to_s.
import deriving
class A
auto_derive # annotation pour avoir un `derive_to_map` automatique
super DeriveToS # hérite un `to_s` implémenté via `derive_to_map`
super DeriveEqual # hérite `==` et `hash` implémentés via `derive_to_map`
var an_int: Int
var a_string: String
end
var a = new A(5, "five")
assert a.to_s == "an_int:5; a_string:five"
var a2 = new A(5, "five")
assert a == a2
assert not a.is_same_instance(a2)
a2.an_int = 9000
assert a != a2
Un mini-noyau réflexif a été implémenté: il permet de manipuler des objets représentant les classes, de construire des hiérarchies de méta-classes sans perte d'information des types. Il est implanté entièrement au niveau de la lib et ne nécessite que le service primitif class_name qui retourne le nom du type dynamique d'un objet (normalement utilisé pour débogage).
L'intérêt d'un tel module reste assez limité car il n'y a aucun support du moteur d'exécution: les objets représentant les classes ne connaissent pas les propriétés, ni ne participent à la création des instances. Aussi, il est nécessaire d'avoir au moins une instance pour obtenir l'objet représentant la classe, ce qui limite encore les possibilités d'utilisation. Enfin, le noyaux réflexif génère une infinité potentielle de types (car on peut toujours demander la méta-classe d'une méta-classe), ce qui brise RTA, et donc nitc --separate et nitc --global; seuls l'interpréteur et nitc --erasure survivent.
Finalement, c'est plus une preuve de concept qu'autre chose, la seule utilisation qui semble faisable serait pour stocker des attributs partagés.
meta as a user-level empty shell for meta-classes 1d30ecdNouveaux services et nettoyages variés.
Nouvelle interface Cloneable pour standardiser la copie d'objets.
Bibliothèque de générateur de bruit.
Amélioration de app.nit et mnit.
La documentation du système de sérialisation a été grandement améliorée.
Gestion plus saine (et conforme) des signaux.
Amélioration du plugin vim, en particulier la complétion via omnifunc.
nitunit sait tester les tests unitaires extraits de la documentation des groupes mais aussi ceux extraits de fichiers markdown autonomes (pages wiki, etc.).
Pour la machine virtuelle, optimisation de l'accès aux variables locales et amélioration de l'organisation des modules.
Le support de iOS et de la FFI avec Objective C s'améliore.
Quelques ajouts et nettoyages pour les benchs.
Quelques correctifs de bogues. Pas tant que ça, et quelques uns introduits par des commits de cette version v0.7.3 ; c'est plutôt bon signe.