Sortie de Mathematica 11

Mathematica est un logiciel à l’origine prévu pour le calcul symbolique (spécifiquement pour un public de mathématiciens, d’où son nom), mais qui s’est ouvert à bien d’autres domaines depuis lors : sur ces trente dernières années, son domaine d’action s’est largement étendu en dehors des mathématiques pures, notamment au niveau de la visualisation, du traitement du signal et des images, des statistiques et de l’apprentissage automatique. Avec le lancement de Wolfram Alpha, un « moteur de connaissances numérique », le logiciel a directement eu accès à une pléthore de données diverses, tant sur les éléments chimiques que la sociologie ou la finance, ce qui décuple encore ses capacités. Un résumé de ces domaines est présenté en première page de la documentation. Ces dernières années, les améliorations ont aussi porté sur la manière de déployer ses développements avec Mathematica : le format CDF permet d’exporter des documents (le CDF Player peut exécuter les commandes incluses), Wolfram Cloud propose d’exécuter du code Mathematica dans le nuage.

La version 11 de cet environnement complet propose pas moins de 555 nouvelles fonctions (alors que la première version n’en contenait que 551), sans compter les nouvelles fonctionnalités ajoutées aux fonctions précédentes. Cependant, la rétrocompatibilité est au cÅ“ur des développements : tout programme Mathematica écrit depuis la version 7 restera entièrement compatible avec les dernières versions (il n’y a plus eu de changement incompatible depuis lors) et bon nombre d’applications restent compatibles depuis la première version du logiciel, en 1988.

Cela est possible grâce au principe de cohérence dans la conception de l’interface des fonctions, dans le plus pur style fonctionnel : toute fonctionnalité est prévue pour rester dans la durée, ce qui permet de construire de nouvelles fonctionnalités par-dessus. Quand les développeurs n’ont pas achevé une fonctionnalité, mais qu’elle peut déjà être utile aux différents utilisateurs, elle est proposée sous une forme expérimentale : elle est utilisable, mais il n’y a aucune garantie que l’interface restera inchangée (il faut d’ailleurs inclure spécifiquement des modules, ils ne sont pas disponibles par défaut).

Améliorations esthétiques

En ouvrant Mathematica 11 la première fois, on remarque vite les changements apportés à l’interface graphique, même s’ils sont relativement mineurs. Les polices utilisées sont plus nettes et plus denses. L’autocomplétion a été améliorée, elle est plus contextuelle et fait des propositions y compris pour les options. De plus, chaque commande est maintenant annotée dans la langue de l’utilisateur, ce qui en facilite la compréhension pour ceux qui ne maîtrisent pas suffisamment l’anglais. Les messages d’erreur sont aussi plus instructifs et proposent également d’afficher la pile d’appel au moment de l’erreur.

Impression 3D

L’une des nouvelles zones de fonctionnalités de Mathematica est l’impression 3D. Les versions précédentes pouvaient déjà exporter des géométries en STL, mais la 11 propose toute la chaîne d’impression, depuis la création d’une géométrie 3D à son impression (par une machine locale ou par un prestataire externe) — en prenant en compte les limites techniques de l’impression 3D. Il devient ainsi possible d’imprimer tout graphique tridimensionnel, toute structure moléculaire, toute partie de la Terre (par exemple, la géographie autour d’une montagne).

Apprentissage automatique

Un des grands termes à la mode actuellement est l’apprentissage automatique (en anglais, machine learning), notamment tout ce qui a trait aux réseaux neuronaux et à l’apprentissage profond (Intel et NVIDIA se battent d’ailleurs sur la performance de leurs puces respectives pour l’apprentissage de réseaux neuronaux profonds). La version 10 de Mathematica avait déjà apporté une interface très simple d’utilisation pour bon nombre de cas, avec les fonctions Classify[] et Predict[] pour la classification et la régression (apprentissage supervisé), en donnant accès à une foultitude d’algorithmes sous une interface unifiée (régression linéaire, logistique, forêts aléatoires, plus proches voisins, réseaux neuronaux, etc.).

Cette nouvelle version apporte également une interface unifiée pour l’extraction de caractéristiques, la réduction de dimensionnalité, le groupement d’éléments, la détermination d’une loi de probabilité, l’apprentissage par renforcement. Une nouveauté est aussi la mise à disposition de modèles déjà entraînés, par exemple pour la classification d’images selon leur contenu.

Du côté des réseaux neuronaux, Mathematica s’appuie sur ses capacités symboliques de calcul pour proposer des réseaux représentés de manière symbolique : on peut alors associer n’importe quel genre de couche et visualiser les réseaux, mais aussi les entraîner sur des données (aussi sur GPU) et analyser leur performance, puisque ces réseaux sont stockés comme des graphes. Les couches de base sont déjà implémentées (combinaison linéaire d’entrées, fonctions d’activation sigmoïdes), tout comme les développements récents de l’apprentissage profond (autoencodeurs, réseaux convolutionnels). Les réseaux récurrents sont en cours de développement, mais pas encore prêts.

Géographie

Travailler sur la géographie de la Terre et d’autres planètes n’est pas toujours évident, puisqu’elle n’est pas plate. Mathematica ne fait pas que donner accès aux informations sur bon nombre de planètes, il dispose aussi de fonctions pour effectuer des calculs sur ces géométries particulières : il dispose d’une bonne collection de projections géographiques, pour proposer des calculs précis de géodésie. Ces capacités s’étendent aussi aux cartes, avec l’accès aux détails de chaque rue, mais aussi les frontières historiques de chaque pays et des images satellites de basse résolution.

Calculs numériques

Au niveau numérique, il devient possible de résoudre des équations différentielles (y compris aux dérivées partielles) par leurs valeurs propres dans n’importe quel domaine, y compris en spécifiant des conditions aux limites. Ces solveurs sont totalement génériques : il n’y a pas besoin de connaissances poussées en algorithmique numérique, Mathematica détermine seul une bonne manière de résoudre le problème donné. Ces systèmes fonctionnent en s’intégrant avec les fonctionnalités géométriques préexistantes, ce qui permet notamment d’étudier la réponse d’une peau de tambour qui aurait la forme des États-Unis.

Dans des domaines connexes, le calcul symbolique fait encore des progrès : il devient maintenant possible de résoudre de manière symbolique un grand nombre d’équations aux dérivées partielles qui apparaissent dans les manuels, mais aussi des équations mélangeant des dérivées et des intégrales. Côté numérique, les fonctions d’optimisation ont vu leur performance et leur robustesse améliorées, ce qui sert notamment aux fonctionnalités d’apprentissage automatique.

Programmation généraliste

Même si Mathematica n’a pas commencé comme un langage de programmation généraliste, il s’étend également dans cette direction. Ainsi, il peut maintenant travailler sur des séquences d’octets bruts reçus sur un socket, effectuer des mesures sur un réseau (Ping pour le moment). La cryptographie fait son apparition, tant symétrique qu’asymétrique.

Source et images : Today We Launch Version 11!.
Voir aussi : toutes les nouveautés depuis Mathematica 10.4, mise en contexte de certaines nouveautés.

Scala Native : accéder au bas niveau depuis Scala

Le langage Scala n’est pas dénué d’avantages, comme beaucoup d’autres : il est typé (comme C ou Pascal), mais ne force pas à indiquer les types explicitement dans le code (contrairement aux précités, mais comme Python) ; il n’impose pas une verbosité monstre (contrairement à Java), avec une syntaxe orientée productivité (comme Python) ; il est raisonnablement rapide (comme Java, mais nettement moins que C), mais permet d’exploiter le parallélisme très facilement (contrairement à Python).

Par contre, un des inconvénients de Scala est sa vitesse d’exécution, pas toujours suffisante : il faut compter le temps de démarrage de la JVM, puis les optimisations que le moteur JIT peut effectuer sur le code qui lui est soumis, sans oublier que le ramasse-miettes peut venir jouer. La sécurité que le langage offre peut aussi aller à l’encontre de la performance à l’exécution : il est impossible de s’échapper du bac à sable défini, à l’exception de quelques utilisateurs très expérimentés. Finalement, au niveau de l’accès aux bibliothèques natives préexistantes, leur réutilisation est presque impossible — après tout, ces bibliothèques natives pourraient causer des problèmes de sécurité !

Pour résoudre ces problèmes, une équipe de l’EPFL, déjà à l’origine de Scala, a lancé le développement de Scala Native. Ce compilateur Scala exploite LLVM, l’infrastructure de compilateurs derrière Clang, notamment. La syntaxe de cette variante de Scala ne diffère de l’originale que par quelques ajouts, dont l’objectif est de faciliter l’interopérabilité avec des bibliothèques natives, comme des structures C (avec l’annotation @struct sur une classe) ou encore l’allocation de mémoire sur le tas avec des pointeurs :

@struct class Vec ( // Définition d'une structure en C.
  val x: Double
  val y: Double
}
val vec = stackalloc[Vec] // Équivalent à un malloc() en C ou un new en C++ : vec est un pointeur sur le tas.

Pour accéder directement à des fonctions C (comme malloc()), il est aussi possible d’utiliser l’annotation @externe et directement définir la fonction comme utilisable :

@extern object stdlib {
  def malloc(size: CSize): Ptr[_] = extern
}
val ptr = stdlib.malloc(32)

Au niveau de l’implémentation, le projet Scala Native ne duplique pas l’analyse syntaxique du compilateur, mais l’utilise (ou bien dotty, la prochaine génération du compilateur Scala). Il profite donc d’une base de code bien établie, ainsi que d’optimisations éprouvées dans le contexte du code Scala.

À l’exécution, bien sûr, Scala Native exploite toujours un ramasse-miettes, le classique Boehm, simplement parce qu’il a l’avantage de déjà exister. Les développeurs notent qu’il ne peut que s’améliorer par rapport à cette implémentation générique. La bibliothèque standard Java est partiellement disponible, toute la bibliothèque standard C est accessible.

Ce générateur de code natif depuis Scala n’est pas encore prêt pour le plus grand nombre, il a, à peine, été annoncé ce mois-ci. Ses objectifs ne concernent pas les temps de compilation. Cependant, il montre que le langage Scala veut s’exporter de plus en plus, après un autre générateur de code JavaScript pour utiliser son code dans des navigateurs Web.

Source (dont image) : présentation à Scala NYC.
Voir aussi : le site officiel de Scala Native, le projet sur GitHub.

Un nouvel optimiseur de code pour Visual C++

Les compilateurs sont des programmes informatiques d’une importante complexité. Ils sont généralement structurés en une partie frontale, qui comprend le langage d’entrée, puis d’une partie arrière, qui s’occupe de traduire le programme d’entrée en du code binaire exécutable par le processeur. Les deux parties communiquent à l’aide d’une représentation intermédiaire (IR). Ainsi, pour qu’une même suite de compilateurs comprenne plusieurs langages et puisse générer du code pour plusieurs processeurs, il n’est pas nécessaire d’écrire un compilateur spécifique pour chaque paire langage-processeur : il suffit d’écrire les paires langage-IR et IR-processeur. Tout le travail effectué pour un processeur sera alors utilisable pour n’importe quel langage d’entrée.

Dès la génération du code intermédiaire, un compilateur utilise énormément de passes d’optimisation, afin de rendre le code plus rapide. Elle utilise des techniques comme la propagation des valeurs : si une variable a une valeur connue à un endroit du code (initialisation, condition…), alors cette valeur peut être propagée dans une série de calculs, qui seront alors effectués à la compilation au lieu de l’exécution. Ces passes sont effectuées en partie sur la représentation intermédiaire, pour les optimisations indépendantes du processeur, mais également sur du code machine déjà généré, pour tirer le meilleur parti possible du matériel.

Généralement, cette phase d’optimisation est extrêmement cruciale, étant donné que la représentation intermédiaire est suffisamment générale pour plusieurs types de processeurs et relativement simple dans les instructions disponibles : pour effectuer certaines opérations, il est parfois nécessaire d’écrire des dizaines d’instructions IR, alors que certains processeurs peuvent réaliser la même opération en une seule instruction. De plus, une représentation intermédiaire doit être prête pour les phases d’optimisation qui suivent, notamment en simplifiant toutes les étapes de raisonnement : dans les IR de type SSA, notamment, une variable ne peut être assignée qu’une seule fois, ce qui allonge d’autant plus le code.

Un nouvel optimiseur pour Visual C++

Le compilateur C++ de Microsoft, connu sous le nom de Visual C++, a longtemps été laissé dans un état proche de l’abandon. Depuis quelques années, les équipes de développement ont mis les bouchées doubles pour ramener le compilateur dans la course, avec une compatibilité avec les normes les plus récentes, au niveau de GCC ou Clang. Depuis lors, ils ont effectivement rattrapé en grande partie leur retard à ce niveau, mais pas encore pour les optimisations du code.

L’optimiseur actuel ne disposait que d’une série de transformations assez basiques, n’exploitant qu’une vue assez limitée des fonctions. Bon nombre d’optimisations fonctionnent en trouvant des motifs et en les remplaçant par une version améliorée, mais tous les motifs les plus utiles ne sont pas toujours implémentés. De plus, le code vectorisable n’est pas optimisé au meilleur de ses possibilités. Toutes ces limitations ont une origine historique : le compilateur C++ de Microsoft a des origines très anciennes, il provient de l’époque DOS où la mémoire était très limitée, ce qui limite les possibilités. Les efforts actuels portent principalement sur une modernisation du code, en éliminant les compromis d’un autre âge.

Le nouvel optimiseur exploite désormais une représentation intermédiaire SSA. Les algorithmes implémentés par-dessus peuvent ainsi être plus efficaces en temps par rapport aux approches par analyse du flux de données. Il se base sur une bibliothèque C++ avancée, exploitant les templates à foison, pour décrire les transformations à effectuer, ce qui a permis d’ajouter bon nombre d’optimisations simples en très peu de temps (surtout par rapport à l’infrastructure précédente).

Les développeurs de Visual C++ indiquent également avoir prêté attention à la correction de ces optimisations : il n’y a rien de plus désagréable que de passer des journées à déboguer son code pour trouver que, finalement, le problème n’est pas dû au code, mais bien au compilateur. Linus Torvalds a récemment torpillé GCC à ce sujet. Ici, les développeurs ont absolument cherché à éviter ces écueils, en testant leurs passes d’optimisation sur des programmes courants comme Chrome ou Firefox, puis sur des bibliothèques imposantes côté Microsoft comme CoreCLR ou Chakra.

Ils ont aussi employé des techniques de vérification formelle (à l’aide d’ALIVE et l’outil de démonstration automatique Z3, tous deux issus de Microsoft Research et aussi utilisés pour LLVM) et de la génération de code aléatoire avec Csmith (et C-Reduce pour réduire le code posant problème au strict minimum). Certains outils du projet LLVM ont pu être utilisés, puisque Visual C++ peut en exploiter les parties avant, comme Opt-fuzz, qui génère des expressions arithmétiques à optimiser.

Un exemple

Ces nouvelles optimisations peuvent avoir un effet assez radical sur certains bouts de code. Par exemple, pour un test de parité :

int test(int a) {
    return a % 2 != 0 ? 4 : 2;
}

Les précédentes versions du compilateur produisaient un code qui prenait entre cinq et dix cycles en x86, selon que tout le processeur est exploité (prédiction du branchement parfaite) ou non :

?test@@YAHH@Z PROC
    and   ecx, -2147483647
    jge   SHORT $LN3@test
    dec   ecx
    or    ecx, -2
    inc   ecx
$LN3@test:
    test  ecx, ecx
    mov   eax, 2
    mov   edx, 4
    cmovne eax, edx
    ret   0

Sauf que… Dans le test a % 2 == 0, le signe de a n’a aucune espèce d’importance, seul un bit est important : le modulo peut être remplacé par une opération logique, c’est-à-dire a & 1 == 0. Or, la comparaison implique un branchement dans le code, forcément lent sur les architectures x86 modernes : pour s’en débarrasser, la structure bool(a) ? C1 : C2 peut être remplacée par C2 + a*(C1-C2), c’est-à-dire exclusivement des calculs. Par conséquent, le nouveau code assembleur est le suivant :

?test@@YAHH@Z PROC
    and   ecx, 1
    lea   eax, DWORD PTR [rcx*2+2]
    ret   0

Celui-ci prend, à tous les coups, deux cycles d’horloge : par rapport à cinq à dix cycles, le gain est important, tant en rapidité qu’en taille de l’exécutable.

D’autres mécanismes effectuent une analyse statique du code, en précalculant autant de bits que possible dans les variables. Par exemple, lors d’une conversion vers un type plus grand, une série de bits est forcément à zéro, peu importe la valeur d’entrée. Ensuite, cette information peut être adaptée en fonction des opérations effectuées, ce qui peut guider le reste du processus.

int test(unsigned char a) {
    short b = a;    // b: 00000000________, a: ________
    b <<= 4;        // b: 0000________0000
    b |= 3;         // b: 0000________0011
    return b != 0;  // -> return true  
}

Impact sur la taille du code généré

L’impact de ces modifications sur le code généré n’est pas facile à établir : certaines optimisations diminuent forcément la quantité de code, le compilateur sera alors plus enclin à recopier in extenso le code de petites fonctions pour éviter le surcoût dû à tout appel de fonction (qui n’est négligeable que pour des fonctions plus grandes) — ce qui conduit à l’augmentation de la taille du code. Il n’empêche, les résultats sont intéressants : sur tout Windows, c’est-à-dire plus d’un gigaoctet, les gains sont de 438 Ko par rapport à l’optimiseur précédent (0,3 %) ; sur SQL Server, 46 Ko sur 64 Mo (0,8 %) ; sur Chakra, le nouveau moteur JavaScript, 10 Ko sur 6 Mo (1,7 %).

En analysant plus en détail l’impact au niveau des instructions, les plus lentes sont largement évitées (branchements, multiplications, divisions), remplacées par des opérations plus rapides (comme des déplacements conditionnels). Les temps de compilation ont été impacté de diverses manières : une augmentation de 1,7 % pour Chrome ou une diminution de 2,6 % pour le noyau Windows.

Et alors ?

Les travaux sur l’optimiseur viennent seulement d’être annoncés : l’architecture choisie permettra aux développeurs d’ajouter des optimisations supplémentaires rapidement (et certaines sont déjà en cours de planification). Cet optimiseur devrait arriver dès Visual C++ 2015 Update 3, mais le travail continuera sur cet aspect du compilateur, afin de le rendre compétitif par rapport à ses concurrents, voire de les dépasser. Il est d’ores et déjà possible de le tester.

Source : Introducing a new, advanced Visual C++ code optimizer.
L’image a été créée par l’auteur et est soumise au droit d’auteur.

Sortie de GCC 6.1 : C++14 activé par défaut, OpenMP 4.5

GCC vient de sortir sa version majeure annuelle, numérotée 6.1. Elle cumule les développements d’une année entière, avec des évolutions dans tous les domaines : côté C++, le compilateur se positionnera sur la norme C++14 par défaut, au lieu de C++98 auparavant, quelques fonctionnalités de C++17 ont été implémentées ; pour le domaine HPC, OpenMP 4.5 est complètement implémenté, les calculs peuvent être déportés sur des coprocesseurs Intel Xeon Phi « Knights Landing » et sur du matériel AMD par HSAIL ; l’implémentation de OpenACC 2.0a a été améliorée, avec une possible déportation sur du matériel NVIDIA par PTX. Au niveau matériel, les prochains processeurs d’AMD, basés sur l’architecture Zen, sont déjà pris en charge ; les plateformes ARM ont été le théâtre de bon nombre d’améliorations ; l’architecture PowerPC a reçu la compatibilité avec POWER9, la prochaine itération des processeurs d’IBM.

Côté C++

La précédente version majeure de GCC, numérotée 5.1, apportait les dernières touches à l’implémentation de C++14, en apportant des fonctionnalités comme la désallocation d’une partie d’un tableau, des constexpr plus généraux, des fonctions anonymes génériques.

Cette nouvelle version de GCC s’arme déjà pour C++17, avec par exemple, la définition d’attributs sur les énumérateurs ou encore des expressions utilisant l’opérateur fold (aussi nommé reduce ou autre, selon les modes) :

// Cette fonction somme tous ses arguments.
template<typename... Args>
  bool f(Args... args) {
    return (true + ... + args);
  }

Plus de détails dans la documentation.

Une nouvelle optimisation en C++ casse du code existant

Une nouvelle optimisation fait parler d’elle : la propagation de valeurs considère désormais que le pointeur this en C++ (qui pointe vers l’objet courant) est toujours initialisé (sa valeur n’est jamais nullptr). Ce point particulier n’a jamais été précisé dans une norme, les compilateurs sont donc libres quant à son interprétation — même si Qt 5 ou Chromium exploitaient l’implémentation précédente. Ce cas peut arriver pour des structures, comme un arbre binaire :

struct Node {
  Node * left;
  Node * right;
};

Pour traverser cet arbre en C, la manière la plus naturelle d’écrire l’algorithme est récursive. Pour traiter le cas d’une branche absente, la fonction commence par vérifier que le pointeur passé en argument est bien valide :

void in_order(Node* n) {
  if (! n) return;
  in_order(n->left);
  in_order(n->right);
}

En C++, la syntaxe est plus plaisante avec une fonction membre. Dans ce cas, l’argument de la fonction est passé de manière implicite, à travers le pointeur this :

void in_order() {
  if (this == nullptr) return;
  left->in_order();
  right->in_order();
}

Cependant, avec cette optimisation (permise par le standard C++), le premier test sera toujours faux, puisque, par hypothèse, this est toujours un pointeur valide… et ce code plantera lamentablement à l’arrivée d’une feuille. Heureusement, cette optimisation peut être désactivée avec un simple paramètre lors de la compilation (-fno-delete-null-pointer-checks) et l’absence de tel code peut aussi être vérifiée (-fsanitize=undefined).

Bien évidemment, une meilleure manière d’écrire le code vérifie directement chacun des deux pointeurs contenus dans la structure avant de continuer la récursion — ce qui évite en passant les problèmes potentiels avec cette optimisation :

void in_order() {
  if (left)   left->in_order();
  if (right) right->in_order();
}

Sources : GCC 6.1 Released, Why does the enhanced GCC 6 optimizer break practical C++ code?, GCC 6 Release Series: Changes, New Features, and Fixes, C++ Standards Support in GCC.
Merci à Winjerome pour ses conseils à la rédaction.

Sortie de LLVM 3.8

Avec à peu près un mois de retard sur le planning, la nouvelle version de la suite de compilation LLVM est sortie, numérotée 3.8. Elle inclut notamment le compilateur C et C++ Clang. Quelques points sont mis en évidence par les développeurs :

  • OpenMP est maintenant activé par défaut dans Clang (la norme en version 3.1 était disponible en préversion dès Clang 3.7, le travail continue pour OpenMP 4.0 puis 4.5) ;
  • la compatibilité avec les autres compilateurs : une gestion des exceptions compatible avec les compilateurs Microsoft sous Windows, du TLS (une zone de mémoire privée pour chaque fil d’exécution pour des programmes parallèles à mémoire partagée) compatible avec GCC ;
  • la compilation de LLVM se fait maintenant de manière privilégiée avec CMake (plutôt que les autotools) ;
  • le tutoriel Kaleidoscope a été retravaillé pour faciliter l’utilisation de la plateforme LLVM par de nouveaux développeurs (notamment pour gérer d’autres langages en entrée) ;
  • LLVM ne garantit plus la compatibilité avec les versions les plus anciennes de Windows, c’est-à-dire antérieures à Windows 7.

Calcul de haute performance

Pour le calcul de haute performance, cette nouvelle version apporte bien des nouveautés intéressantes. Un certain nombre de fonctionnalités d’OpenCL 2.0 est implémenté, notamment au niveau de la gestion des espaces d’adressage ou encore des types atomiques (compatibles avec ceux définis par C11) et d’images (des tableaux bidimensionnels, comme image2d_depth_t ou image2d_array_depth_t).

Du côté d’OpenMP, la version 3.1 de la norme est maintenant entièrement supportée. À l’exécution, ce ne sera plus la bibliothèque GCC OpenMP qui sera utilisée, mais bien celle de Clang, développée par Intel (et développée à partir de celle utilisée pour leurs compilateurs propriétaires). Le développement continue pour OpenMP 4.0 et 4.5, avec notamment les clauses map, num_teams et thread_limit ou encore les dépendances entre tâches.

Pour CUDA, la technologie propriétaire de calcul sur processeurs graphiques de NVIDIA, les fonctionnalités arrivent petit à petit (elles sont toujours considérées comme expérimentales). Le code de Google n’est toujours pas intégré à Clang, mais il peut maintenant s’interfacer avec cette version sans modification spécifique.

Source : [llvm-announce] LLVM 3.8 Release, Clang 3.8 Release Notes

Build2 : une chaîne de compilation et de distribution de code C++

Build2 a vu sa première version sortir ce mois-ci. Cet outil C++ est prévu comme une chaîne de compilation, un gestionnaire de paquets et une interface en ligne, principalement pour le monde C++ (même si les autres langages ne sont pas mis en avant, l’outil pourra les gérer). Il gère le même genre de problèmes que Conan : pour utiliser une nouvelle bibliothèque en C++, il faut gérer la compilation (qui peut varier fortement d’une plateforme à l’autre, d’un compilateur à l’autre) et les dépendances éventuelles, qui à leur tour viennent avec leurs particularités. Build2 propose ainsi une nouvelle syntaxe pour gérer les fichiers dictant le mode opératoire pour la compilation, comparable à make et CMake.

Concrètement, le projet est divisé en plusieurs morceaux : build2 pour le système de compilation, bpkg pour le gestionnaire de paquets et brep pour l’interface Web. Ce dernier est déjà à l’Å“uvre sur cppget.org, que les développeurs espèrent voir devenir la plateforme de référence pour les bibliothèques C++ (même s’il ne contient, à ce jour, que quelques paquets, tous issus de Build2).

bpkg est le point central pour tout utilisateur : il sert à créer un nouveau projet, à ajouter des dépôts pour la gestion des dépendances, puis à la compilation et à la gestion de la mise à jour de ces dépendances. Ensuite, le pilote b s’occupe de la compilation de tout projet C++, une fois les dépendances installées. Ainsi, la manière la plus courante de l’utiliser est d’utiliser ces commandes, de la création du projet à sa compilation, en passant par les dépendances :

$ bpkg create cxx config.cxx=clang++
created new configuration in ./

$ bpkg add https://pkg.cppget.org/1/alpha
added repository cppget.org/alpha

$ bpkg fetch
fetching cppget.org/alpha
fetching cppget.org/beta (complements cppget.org/alpha)
[...]
10 package(s) in 4 repository(s)

$ bpkg build bpkg
build libbutl 0.2.0 (required by bpkg libbpkg)
build libbpkg 0.2.0 (required by bpkg)
build bpkg 0.2.0
continue? [Y/n] y
[...]
bpkg-0.2.0.tar.gz             100% of  144 kB  130 kBps 00m01s
fetched bpkg 0.2.0
unpacked bpkg 0.2.0
configured bpkg 0.2.0
c++ bpkg-0.2.0/bpkg/cxx{bpkg}
[...]
ld bpkg-0.2.0/bpkg/exe{bpkg}

$ b -v
g++-5 -I/tmp/hello-gcc5-release/libhello-1.0.0+1 -O3 -std=c++11 -o hello.o -c ../hello2/hello.cpp
g++-5 -O3 -std=c++11 -o hello hello.o /tmp/hello-gcc5-release/libhello-1.0.0+1/hello/libhello.so

Pour le moment, le projet est décrit comme une préversion technologique, rien n’est véritablement fixé dans le marbre. Il est porté par la société Code Synthesis, qui ne s’oriente pas complètement autour de ce gestionnaire de dépendances : contrairement au prédécesseur de Conan, Biicode, la viabilité de ce nouvel écosystème ne dépendra pas uniquement du modèle économique développé pour lui, ce qui pourrait en augmenter les chances de survie.

Site de Build2.

Fedora et GCC 6 montrent de mauvaises pratiques de codage

Pour leur version 24, les développeurs de la distribution Linux Fedora ont notamment voulu utiliser la dernière version du compilateur GCC, qui sera la GCC 6.1 quand la nouvelle version de la distribution sera disponible (GCC 6 est prévu pour avril 2016, Fedora 24 vers juin). Ainsi, cette semaine, les développeurs ont lancé une compilation complète de l’entièreté des paquets de Fedora, en préparation de ce changement de compilateur.

Leur processus était le suivant : d’abord tenter une compilation de tous les 17 741 paquets avec GCC 6, puis, pour ceux dont la compilation a échoué, retenter la compilation avec GCC 5. Si la compilation échoue deux fois, le problème vient du paquet, sinon de GCC. Un peu moins de mille paquets ont ainsi échoué deux fois, ce qui en laisse à peu près six cents qui ont échoué avec GCC 6 mais pas GCC 5. En comparaison, pour le passage de GCC 4.9 à GCC 5, moins de la moitié avait posé problème. La nouvelle version de GCC serait-elle si mauvaise ?

Effectivement, une série de ces problèmes était due à des régressions au niveau de GCC, des défauts qui ont rapidement été corrigés en vue de la sortie de la version finale. La majorité des cas, cependant, vient des développeurs des applications concernées. Pour une série d’entre eux, le problème vient du fait que GCC 6 compilera le code C++ par défaut en mode C++14 (au lieu de C++98, une norme bien dépassée) : ces paquets n’étaient pas prêts et ont dû être recompilés avec GCC 5.

Pour d’autres, certains changements dans l’implémentation de la bibliothèque standard C++ et, surtout, de nouveaux messages d’erreur ont montré des pratiques de programmation douteuses dans le chef des développeurs des applications disponibles dans Fedora. Les réponses des développeurs de GCC sont parfois éloquentes quant à ces mauvaises pratiques :

The code is nonsense, what’s it even supposed to do? […] It’s useless, and only compiled by accident.

Allowing it with -fpermissive might make sense though, as that flag allows all kinds of terrible rubbish to compile.

Les mêmes défauts seront probablement remarqués par les autres distributions en passant à GCC 6, comme Gentoo, habituellement prompte à se mettre à jour.

Source : Fedora mass rebuild 2016.

Qu’attendre de GCC 6 en C++ ?

Avec le nouveau cycle de GCC, la prochaine version majeure sera numérotée 6 et sera la première à apporter des nouveautés par rapport à GCC 5, sorti en avril dernier dans sa première version finale (5.1). La principale nouveauté concerne la norme C++ par défaut : adieu C++98, GCC s’ouvre maintenant au vingt et unième siècle en activant C++14 par défaut ! Il est toujours possible de compiler son code en C++98 ou C++11 avec l’option de compilation adéquate (std=c++11, par exemple).

Toujours au niveau des normes, quelques points qui devraient faire partie de la prochaine révision de C++, en 2017, sont déjà implémentés (et accessibles par l’option std=c++1z). Cette révision n’étant pas achevée, toutes ces fonctionnalités sont marquées comme expérimentales. La principale est nommée concepts : il s’agit d’une extension du système de templates du langage pour inclure des garanties formelles ; par exemple, pour imposer que le type paramétré T d’une fonction puisse être comparé, on pourrait écrire ce bout de code :

template<typename T> requires LessThanComparable<T>
const T& min(const T &x, const T &y) {
    return (y < x) ? y : x;
}

auto concept LessThanComparable<typename T> {
    bool operator<(T, T);
}

Code très approximatif suite aux énormes limitations du système de blog.

Grâce à ces concepts, le compilateur pourrait fournir des messages d'erreur beaucoup plus explicites qu'actuellement ; ils pourraient être utilisés par les outils d'analyse statique du code pour des diagnostics plus précis ou encore guider l'optimisation du compilateur. Initialement, ces concepts étaient prévus pour C++11, mais n'étaient pas encore estimés suffisamment prêts alors. Au niveau de la bibliothèque standard C++17, les extensions proposées pour la gestion des fichiers et une série de fonctions estimées de base sont implémentées.

Architectures

Pour ARM64, le compilateur peut maintenant déterminer le processeur utilisé et optimiser au mieux pour ce compilateur, sans nécessiter de paramètre particulier pour march et compagnie. Aussi, l’optimisation lors de l’édition est liens fonctionne sur plusieurs unités de compilation.

Sur les processeurs Intel, les extensions de protection de la mémoire (MPX) sont maintenant activées par défaut : ces instructions des processeurs de génération Skylake sont prévues pour augmenter la sécurité lors de l’exécution avec une vérification matérielle des références des pointeurs. Sur ces mêmes processeurs (gamme Xeon uniquement), pour la performance, les extensions AVX-512 sont maintenant gérées : elles servent à exécuter des opérations mathématiques sur des vecteurs de cinq cent douze bits d’un coup (plutôt que d’effectuer une série d’instructions les unes à la suite des autres), ce qui a un impact principalement sur le code scientifique.

Côté AMD, les processeurs de la famille Zen, qui devraient débarquer à la fin de l’année, sont déjà gérés.

OpenMP est une norme prévue pour aider à paralléliser du code source C, C++ et Fortran. La nouvelle version 4.5 de la norme est complètement implémentée pour les compilateurs C et C++.

Diagnostics du compilateur

Une nouvelle option, -fsanitize=bounds-strict, effectue des vérifications plus poussées et plus strictes au niveau des bornes des tableaux lors des appels.

Cependant, le nouvel avertissement qui a fait le plus de bruit concerne l’indentation : par exemple, si un bout de code marque par son indentation qu’il devrait dépendre d’une condition… mais si la syntaxe utilisée montre le contraire, le compilateur donnera un avertissement. Il a déjà montré ses effets positifs pour une partie des elfutils : Mark Wielard a pu corriger un défaut « gênant » dans le code. Il doit cependant être activé séparément avec le paramètre -Wmisleading-indentation.

Ce type d’erreur a déjà mené à des failles de sécurité, notamment chez Apple, où une fonction retournait sa valeur avant d’avoir effectué tous les tests de sécurité nécessaires. Encore une fois, l’indentation indiquait que le code était correct… alors que le retour se faisait sans la moindre condition.

static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
                                 uint8_t *signature, UInt16 signatureLen)
{
    OSStatus        err;
    …

    if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
        goto fail;
    if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
        goto fail;
        goto fail;
    if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
        goto fail;
    …

fail:
    SSLFreeBuffer(&signedHashes);
    SSLFreeBuffer(&hashCtx);
    return err;
}

Cependant, pas mal d’utilisateurs craignent que ce nouveau type d’avertissements ne crée un grand nombre de faux positifs — c’est d’ailleurs pour cette raison qu’il n’est pas activé par défaut.

GCC 6.1, la première version stable de GCC 6, devrait arriver aux alentours de mars ou avril. Il devrait être le compilateur par défaut pour Fedora 24, dont la sortie devrait être retardée jusque juin pour laisser le temps de recompiler tous les paquets avec cette nouvelle version de GCC.

Sources : GCC 6 Release Series, C++1z Support in GCC, libstdc++ status, Among The Changes/Features Coming For GCC 6, GCC 6 To Enable Intel’s MPX Library By Default, GCC 6 Will Warn You About Misleading Code Indentations, Apple’s SSL/TLS bug, Fedora 24 Will Likely Be Delayed.

Conan, nouvelle tentative de gestionnaire de dépendances en C++

L’environnement des bibliothèques C++ peut être considéré en bonne approximation comme un capharnaüm : à moins de développer exclusivement sous Linux avec des bibliothèques relativement matures (et donc disponibles dans les différents gestionnaires de paquets), l’installation des dépendances (et de leurs dépendances respectives !) devient rapidement difficile pour gérer d’autres plateformes ou pour des bibliothèques moins courantes. Même dans ce cas idyllique, il n’est pas rare que les versions disponibles soient trop anciennes.

Conan vient combler ce déficit : en tant que gestionnaire de dépendances, il prend en entrée une série de bibliothèques requises pour un programme donné, puis il les télécharge et les compile si nécessaire, de telle sorte qu’il devient possible de compiler le programme demandé. De plus, Conan dispose déjà d’une grande base de données en ligne de bibliothèques compilées pour différentes plateformes en versions variées — plusieurs dizaines de bibliothèques, certaines très courantes en C++ comme Boost.

Ce projet ne vient pas de nulle part : il s’agit en réalité de Biicode, dont une très grande partie du code a été repris, suite à la faillite de la société derrière ce projet. Une chose a été améliorée depuis : la simplicité de création d’un paquet. Les contributeurs de Conan sont en grande partie des anciens de Biicode, avec des contributions comme des générateurs de fichiers de projet depuis lors. Actuellement, il n’y a aucune société derrière Conan, il s’agit plus d’un projet personnel. Bien que libre, l’objectif est de développer une société derrière pour faire vivre le projet et un modèle économique viable : les idées pour l’instant se rapprochent du modèle de GitHub, c’est-à-dire des paquets privés payants, même s’il a déjà échoué pour Biicode.

Découvrir Conan

Sources : Conan: One Month After Launch, Thanks!, Nerd Food: Dogen: The Package Management Saga

Juno, un EDI pour Julia

Julia est un langage très récent : sa première version date de 2012 et il est actuellement en version 0.4. Il est de plus en plus utilisé de par le monde, avec un public assez divers : quelques programmeurs dont c’est le métier, mais surtout des scientifiques, des ingénieurs, des analystes financiers, des statisticiens qui développent les bibliothèques de fonctions qui leur sont utiles. Pour que plus de gens utilisent le langage, il est nécessaire de fournir un environnement de développement complet et accessible pour ce public, avec de l’autocomplétion intelligente, l’intégration de graphiques, le débogage, etc. Cet environnement prend le nom de Juno.

Le développement est parti d’un composant proposé par GitHub, Atom, un éditeur de texte prévu pour être facile à personnaliser. Il a la particularité d’être basé exclusivement sur des technologies Web, c’est-à-dire qu’il est codé en HTML, CSS et JS, avec une architecture très modulaire (Juno est implémenté comme une série de modules, relativement indépendants les uns des autres, ce qui devrait faciliter leur réutilisation dans d’autres projets, comme Ink, celui chargé des composants graphiques d’un EDI). Il reste cependant accessible comme une application de bureau traditionnelle, en dehors du navigateur, par Electron (ex-Atom Shell).

La prochaine priorité sera le fignolage de l’utilisation interactive, plus particulièrement tout ce qui a trait au multimédia, aux graphiques interactifs. Ainsi, avec un seul téléchargement, les nouveaux utilisateurs pourront rapidement commencer à écrire quelques lignes de code Julia, à créer des graphiques très rapidement avec les paquets fournis de base. Ensuite, la documentation et le débogueur seront inclus (même si ce dernier doit encore être finalisé du côté Julia : les fonctionnalités sont actuellement au stade de prototype et ne sont pas incluses de base), avec un outil d’analyse de la performance du code.

Pour le téléchargement de la nouvelle version 1.1.0, direction le site de Julia. (La version sur le site de Juno est dépassée et ne fonctionne pas avec les versions actuelles de Julia et des paquets requis.)

Source : Julia IDE work in Atom.