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

Visual Studio en 64 bits ?

Visual Studio est l’environnement de développement intégré proposé par Microsoft. Il gère une pléiade de langages, historiquement des langages natifs comme C++, mais aussi toute la pile .Net avec C#, VB.Net et d’autres, y compris pour le Web. Il est souvent cité en référence comme environnement de développement. À l’origine payant, les éditions Express sont apparues en 2005 ; nettement moins limitées, les éditions Community sont beaucoup plus récentes.

Cependant, l’environnement est aussi connu pour ne pas toujours être extrêmement réactif, notamment dans le cas de projets de grande ampleur ou avec des extensions. Suite à un tweet de Gunnar Skogsholm, le débat sur une version 64 bits de Visual Studio est relancé : ainsi, l’environnement ne serait plus limité à quatre gigaoctets de mémoire vive, il pourrait exploiter toute la mémoire des machines actuelles, ce qui pourrait aider en performance. D’ailleurs, une série d’environnements de développement y sont déjà passés, comme Eclipse ou Android Studio — ces deux-là ayant la particularité d’être écrits en Java et de dépendre d’une JVM, les efforts de portage en 64 bits sont donc très limités.

Des raisons techniques

Rico Mariani, un ancien développeur de Visual Studio, maintenant passé dans l’équipe de Microsoft Edge (le nouveau navigateur de Microsoft), indique cependant que passer au 64 bits n’apportera pas grand-chose à l’EDI, cela risque même plus probablement de lui causer d’autres problèmes de performance. En effet, du côté technique, ces 64 bits correspondent à la taille d’une adresse en mémoire (ce qui permet d’indexer beaucoup plus de mémoire vive qu’en 32 bits) ; cette transition doublerait donc la taille de tous les pointeurs, ce qui a des impacts sur l’alignement en mémoire des structures de données, sur la densité des données stockées et donc la localité.

De manière générale, le code sera plus lourd, l’application occupera plus de place en mémoire en 64 bits plutôt qu’en 32 bits (dans un facteur qui dépend fortement de l’application considérée). Par conséquent, les caches du processeur seront moins bien utilisés, puisqu’il n’ont pas soudainement augmenté en taille ; les registres disponibles seront certes plus grands et plus nombreux, mais ce facteur n’a de réel impact que sur le code effectuant des traitements lourds (comme du calcul scientifique ou de l’analyse de données) plutôt que des interfaces graphiques. Cette migration en 64 bits ne se justifie donc que s’il est nécessaire d’utiliser plus de mémoire que la partie accessible en 32 bits.

Une autre justification est moins attendue : en 64 bits, une fuite de mémoire ne fera pas planter l’application rapidement, puisqu’elle pourra consommer toute la mémoire de l’ordinateur, le ralentir à l’extrême et le rendre complètement inutilisable. Ce genre de justification est encore plus valable pour un navigateur qui autorise des extensions.

Utiliser plus de mémoire est une erreur de conception, pour Rico Mariani

D’ailleurs, même si cette mémoire supplémentaire était utile, il y a d’autres options pour rester en 32 bits, notamment une représentation plus efficace des données en mémoire. Pour gagner en performance, cette solution est bien évidemment meilleure que d’exploiter plus de mémoire. L’idée est donc de prévoir son code pour que seule une partie de la mémoire requise doive résider en mémoire vive, c’est-à-dire stocker la plupart des données dans un fichier et s’assurer d’en avoir besoin très peu souvent.

En effet, pour qu’une application puisse se mettre à grande échelle, ce genre de techniques est souvent requis. Pour un client, l’approche est très positive : il est possible de faire bien plus avec moins de ressources. Par exemple, en 1989, même avec 640 ko de mémoire vive, il restait possible d’exploiter sans problème de performance une base de données de 24 Mo, simplement en gardant un cache de 12 ko de données intéressantes pour accélérer les opérations de recherche et de lecture. À l’heure d’aujourd’hui, les échelles monteraient plutôt à quelques téraoctets de données au moins à traiter, mais le principe reste d’application. Le conseil de Rico Mariani est de considérer les données comme une base de données relationnelle (SQL), puis de la charger par tranches en mémoire, au lieu d’exploiter des représentations plus naturelles pour un programmeur à base de pointeurs.

Cette migration n’est de toute façon pas encore utile

Ces considérations techniques éloignent le débat des points réellement importants pour une application : être agréable à utiliser. Utiliser telle ou telle technologie n’a aucun impact sur ce critère premier ; en réalité, utiliser aussi peu de technologies différentes est probablement une bonne chose. De même, utiliser plus de mémoire n’a pas d’impact direct sur l’utilisabilité : moins de mémoire pour la même expérience devrait être l’objectif. En d’autres termes, une migration en 64 bits n’a pas de sens pour l’utilisateur de Visual Studio.

De plus, elle coûte en temps de développement, qui pourrait être investi dans d’autres fonctionnalités. Notamment, les formats de fichiers binaires utilisent régulièrement des pointeurs, ce qui empêche une migration aisée entre 32 et 64 bits. Cette même évolution a longtemps été retardée par Firefox par manque de compatibilité avec un grand nombre de dépendances externes en 64 bits : il est difficile de ne pas effectuer une migration complète, c’est-à-dire seulement les quelques parties qui en bénéficieraient.

Depuis 2008, Visual Studio permet la création d’extensions chargées en dehors du processus principal de l’EDI, c’est-à-dire dans un processus séparé, avec un espace mémoire séparé. Ces extensions peuvent être réalisées de diverses manières : par exemple, le cÅ“ur de Visual Studio est assez léger, beaucoup de fonctionnalités sont proposées en extensions (comme les solutions), dont un nombre grandissant est écrit en C#, avec une exécution dans une machine virtuelle. Il est aussi possible de compiler ces extensions en 64 bits si nécessaire… et le fait est que très peu d’extensions le font, parce que ce changement ne leur est pas utile. Par contre, Resharper C++, une extension de JetBrains pour Visual Studio, à l’origine du retour de cette controverse, ne profite pas de cette possibilités et a des difficultés à gérer de grandes quantités de code.

Et Rico Mariani de conclure en disant qu’avoir un système d’exploitation 64 bits est extrêmement utile — ce qui n’est pas le cas de la majorité des applications.

Sources : Revisiting 64-bit-ness in Visual Studio and elsewhere, 64-bit Visual Studio — the « pro 64″ argument, Visual Studio: Why is there no 64 bit version? (yet).