La Qt Company en manque de liquidités : les prochaines versions de Qt pourraient n’être disponibles sous licence libre que dans les 12 mois

La Qt Company est la société qui possède actuellement Qt et qui se charge de la majorité du développement de la bibliothèque, mais aussi de la vente de licences commerciales. Ces derniers temps, on dirait que la société ne se porte pas des mieux, avec notamment une restriction pour la maintenance des versions LTS : les mises à jour correctives ne seront plus disponibles que pour les clients commerciaux ; ceux qui utilisent une version libre de Qt sont priés de passer à une version mineure plus récente. Par exemple, pour Qt 5.15, seules les quelques premières mises à jour correctives seront disponibles sous licence libre : on ne verra pas d’équivalent de Qt 5.12.8, qui marque pourtant une très grande stabilité.

Ce n’est pas tout.

Qt et KDE sont très liés : si Qt n’est plus disponible sous licence libre, c’est presque la fin de KDE. C’est la raison d’être de la fondation KDE Free Qt, créée en 1998 : si la dernière version de Qt n’est pas disponible sous licence libre dans les douze mois, tout le code libre de Qt passe sous licence BSD (une licence très peu contraignante), pour garantir la survie du projet KDE. Grâce à cet accord, les développeurs de KDE contribuent très régulièrement au code de Qt.

En permanence, la communauté KDE et la société derrière Qt (Trolltech, Nokia, Digia, Qt Company, selon l’époque) négocient des petits changements dans les accords de la KDE Free Qt, mais toujours de commun accord. Par exemple, quand Qt est racheté, il faut les modifier ; pour ajouter une licence pour certaines parties de Qt, il faut aussi changer cet accord.

Ces derniers temps, les demandes de la Qt Company se font plus pressantes pour améliorer la valorisation économique de Qt. D’un côté, ils cherchent un accord pour gérer les incompatibilités de licence entre les clients qui utilisent une licence commerciale, mais publient du code utilisant Qt sous licence libre (comme KDAB). Ce point doit effectivement être résolu avant que des litiges n’apparaissent. D’un autre côté, avec la crise actuelle liée à la pandémie de COVID-19, les finances de la Qt Company ne sont pas au beau fixe et l’entreprise a besoin de rentrées d’argent à court terme. C’est pour cela qu’elle cherche à exploiter l’accord actuel au maximum, en repoussant les sorties sous licence libre de douze mois : l’objectif est d’inciter les utilisateurs de Qt à ouvrir le porte-feuille et à acheter une licence commerciale.

Cependant, bien qu’autorisée, cette pratique signifierait probablement un grand changement dans la gouvernance de Qt. En effet, depuis des années, toutes les décisions techniques sont prises en public, tout le monde peut y participer. Les mainteneurs de module (qui valident ou non les ajouts de code) ne font pas forcément partie de la Qt Company. Si les versions libres venaient à être restreintes, ce modèle de gouvernance tomberait à l’eau, puisque le pouvoir reviendrait presque exclusivement à la Qt Company. Les contributeurs se demandent aussi comment les choses pourraient évoluer, mais sont d’accord sur le fait qu’il ne leur serait plus possible de contribuer à Qt (qu’il s’agisse d’un projet libre comme KDE ou d’une société comme KDAB — de l’avis de représentants officiels, des deux côtés), chose que la Qt Company semble avoir acceptée.

Pendant ce temps, la décision concernant les versions LTS annoncée en janvier n’avait pas été discutée au sein de la fondation KDE Free Qt. On peut aussi remarquer que, depuis l’annonce officielle, les questions en suspens restent sans réponse. Notamment, qu’adviendra-t-il des correctifs de sécurité ? Seront-ils partagés avec les distributions Linux (qui sont généralement frileuses à faire une grosse mise à jour de Qt) ?

Il semblerait que ces décisions sur les versions LTS ou sur le retard des versions libres serve surtout de moyen de pression pour obtenir des concessions de la part du projet KDE dans le cadre de la fondation KDE Free Qt : selon Olaf Schmidt-Wischhöfer, un des deux représentants de KDE dans la fondation, la Qt Company a explicitement indiqué qu’elle pourrait faire marche arrière sur ces deux dossiers à condition d’obtenir des concessions importantes (faciliter la vente de logiciels en même temps que Qt ou l’intégration avec des logiciels propriétaires tiers). L’objectif est bien d’augmenter les revenus de l’entreprise, sans grande considération pour le projet libre qu’est KDE. Cette situation est entièrement opposée aux habitudes de négociation, où l’objectif était d’augmenter les revenus commerciaux sans porter préjudice au projet libre.

Si la Qt Company ne change pas d’avis sur le dossier, les clauses de l’accord KDE Free Qt ne pourront a priori pas être activées, mais cela incitera très fortement les parties prenantes à créer un fork du projet Qt. Certes, KDE n’estime pas avoir les ressources nécessaires pour y arriver, mais KDAB et probablement d’autres sociétés seraient intéressées par un tel mouvement.

Source : les points de vue de KDE et KDAB.

À quel point l’ordonnanceur de Linux pose-t-il problème aux jeux ?

Un système d’exploitation est constitué d’une myriade de parties, tel un orchestre jouant une symphonie : chaque instrument doit jouer sa partition exactement (et en synchronie), sans quoi l’orchestre ne produit qu’une infâme cacophonie. Certains composants sont essentiels, même s’ils ne sont pas utilisés en permanence : d’un certain point de vue, l’ordonnanceur d’un système d’exploitation peut se comparer à un triangle. Certes, ce n’est pas l’instrument le plus compliqué à jouer (selon Linus Torvalds), mais, sans lui, la symphonie tombe à plat.

Plus précisément, l’ordonnanceur est la partie du noyau du système d’exploitation qui détermine qui aura droit à s’exécuter sur un cœur à un moment donné. Il est exécuté dans deux occasions particulières :

  • lorsqu’un processus fait appel au noyau (un appel système, par exemple pour demander l’exécution d’une opération d’entrée-sortie : celle-ci prend du temps, le processus est mis en pause le temps qu’elle soit terminée) ;
  • au moment d’une interruption lancée au niveau matériel (en particulier, des interruptions sont lancées à intervalle régulier, pour s’assurer qu’aucun processus n’utilise l’intégralité d’un cœur sans laisser de miette aux autres).

Cet ordonnanceur joue donc un rôle fondamental dans la performance d’un système où plusieurs processus doivent fonctionner simultanément. Les jeux, en particulier, peuvent bénéficier d’améliorations à ce niveau : l’ordonnanceur peut en général prendre en compte des priorités pour chaque processus. (Par exemple, c’est ce que fait Windows 10 quand il détecte un jeu, avec son mode jeu.) Justement, pour le moment, la communauté Linux discute d’un nouvel algorithme d’ordonnancement, MuQSS. Celui-ci est prévu pour être relativement léger (un ordonnanceur doit s’exécuter très vite !), mais pour gérer un très grand nombre de cœurs.

Malte Skarupke est développeur de jeux : professionnellement, il développe des intelligences artificielles chez Avalanche (à l’origine de jeux come Rage 2) ; il adore aussi améliorer les temps d’exécution des algorithmes (notamment avec des tables de hachage). Lors du portage de Rage 2 sur Stadia, la plateforme de jeu exécutée dans le nuage, les développeurs ont remarqué une série de blocages qu’ils avaient du mal à régler. Malte Skarupke a fait partie de ceux qui ont analysé la situation (il a participé à l’écriture du code incriminé dans ces blocages) et en a fait un compte-rendu assez exhaustif. Sa conclusion ? L’ordonnanceur actuel de Linux n’est pas génial, MuQSS améliore un peu la situation, mais ils ne sont pas au niveau de l’ordonnanceur de Windows : personne n’a encore trouvé d’algorithme d’ordonnancement parfait.

Le problème vient de certains mécanismes de synchronisation entre tâches (un processeur pouvant contenir une grande série de tâches, mais rien n’oblige une tâche à se synchroniser uniquement avec d’autres du même processus). La théorie fournit une série d’outils pour cette synchronisation : des barrières de synchronisation (souvent utilisées sur GPU), des sémaphores, des verrous tournants, des primitives d’exclusion mutuelle et d’autres encore. Pour les besoins du portage vers Stadia, deux étaient plus appropriées que les autres :

  • les verrous actifs : une variable indique si une tâche est en train de s’exécuter (par exemple, la variable vaut 1) ou non (0). Ensuite, chaque tâche vérifie régulièrement si cette variable change de valeur : dès qu’elle passe à zéro, une tâche peut commencer à s’exécuter. Pour ce faire, elle utilise une instruction spécifique du processeur : souvent nommée test-and-set, elle s’exécute de manière atomique et vérifie si la variable en question a une valeur donnée ; si oui, elle est écrasée ; sinon, rien ne se fait. Le principe de cette instruction atomique est que la lecture et l’écriture pour cette valeur se font sans qu’aucune interférence ne puisse avoir lieu, ce qui évite bon nombre de problèmes de synchronisation. Le problème principal d’un verrou actif est que chaque tâche doit vérifier, en permanence, si le verrou est disponible (dans une boucle infinie tant qu’il est indisponible) : cela pompe énormément de ressources de calcul et n’a de sens que dans certains contextes particuliers (notamment en temps réel, puisqu’on n’a pas forcément le temps de rendre l’exécution au noyau : un changement de contexte prend beaucoup de temps) ;
  • les verrous std::mutex de C++, qui se comportent comme des sémaphores ou d’autres primitives : la principale différence avec les verrous actifs est que, si le verrou n’est pas disponible, la tâche est mise en pause par le noyau. L’exécution reprend donc sur une autre tâche, celle qui vient d’être bloquée reviendra sur le devant de la scène plus tard. S’il n’y a aucune raison d’avoir des contraintes fortes en temps (comme une exécution en temps réel), ce système permet une très bonne utilisation des ressources de la machine.

Le problème de Stadia, c’est que Google vise à proposer, pour ceux qui ont une connexion à Internet suffisamment véloce (bande passante élevée et latence faible — la précision est importante, ici), des jeux à soixante images par seconde, pour une fluidité impeccable. Or, cela ne laisse que seize millisecondes et des poussières pour effectuer tous les calculs pour une image. Si le jeu est bloqué une fraction de milliseconde à chaque image pour des problèmes d’acquisition de verrou, ça passe encore. S’il l’est plus d’une milliseconde, des problèmes commencent à apparaître. C’est pour ça que les développeurs de Rage 2 ont commencé à utiliser des verrous actifs pour Stadia : ils n’ont pas vraiment de temps à perdre pour acquérir un verrou. Sauf que cette implémentation a fait en sorte que toutes les tâches étaient bloquées !

Le problème principal est que les primitives de synchronisation voient leur performance testée en nombre total d’acquisition par secondes. Ce qui importe aux développeurs, ici, c’est le temps nécessaire à l’acquisition d’un verrou, c’est-à-dire la latence. Une implémentation équitable des verrous actifs (ticket_spinlock dans le tableau ci-dessous, qui rend l’exécution au système d’exploitation s’il n’arrive pas à prendre le contrôle du verrou assez — rendre l’exécution prend à peine cent cinquante nanosecondes) prend bien plus longtemps que d’autres implémentations (même plus qu’un std::mutex), mais force la tâche à attendre un temps relativement constant. La milliseconde et demie s’explique par le fait que l’ordonnanceur de base de Linux donne le droit d’utiliser un cœur par tranche d’une milliseconde et demie.

Sauf que ces attentes ne sont pas vraiment le problème pour Rage 2 sur Stadia ! Leur situation est plus difficile à mesurer : dans les cas où le verrou est disponible, il s’agit de compter le temps où ce verrou reste disponible, mais où personne ne s’en saisit. Les mesures changent assez drastiquement : ce temps est assez limité… avec des valeurs très élevées qui apparaissent. Les écarts sont bien plus limités pour std::mutex : c’est pour cela que l’utiliser a pu améliorer les résultats du jeu, contre-intuitivement.

Le même test sous Windows donne des résultats bien meilleurs, jusque deux ordres de grandeur pour le même code :

En lançant le même test sur Linux, mais avec d’autres algorithmes d’ordonnancement, les résultats ne varient pas énormément : Linux garde son retard sur Windows sur ce point. Ce phénomène se remarque en pratique : quand tous les cœurs sont utilisés et que l’utilisateur joue un fichier audio, Windows n’aura aucune coupure, contrairement à Linux, dans bien des cas. Bien évidemment, ces résultats n’ont pas de valeur scientifique à proprement parler, vu qu’ils n’ont pas été reproduits sur d’autres machines que celle du développeur, mais donnent déjà de la matière à digérer pour le développement d’une nouvelle génération d’algorithmes d’ordonnancement.

Bien plus de détails (dont les détails des mesures et les résultats complets) : Measuring Mutexes, Spinlocks and how Bad the Linux Scheduler Really is. Code des mesures.

Sortie de Qt Design Studio 1.4

Qt Design Studio est un éditeur d’interfaces graphiques prévu pour Qt Quick et cherchant à rassembler développeurs et designers. Cette version est la première à s’ouvrir à la 3D, avec une première intégration avec Qt Quick 3D. Le logiciel peut maintenant créer des scènes 3D, y déposer des éléments, importer des éléments 3D et les transformer en des formats chargeables facilement par le moteur de rendu. Lors de l’édition d’un projet 3D, Qt Design Studio ouvre une nouvelle fenêtre affichant un rendu en 3

Qt Design Studio est un éditeur d’interfaces graphiques prévu pour Qt Quick et cherchant à rassembler développeurs et designers. Cette version est la première à s’ouvrir à la 3D, avec une première intégration avec Qt Quick 3D. Le logiciel peut maintenant créer des scènes 3D, y déposer des éléments, importer des éléments 3D et les transformer en des formats chargeables facilement par le moteur de rendu. Lors de l’édition d’un projet 3D, Qt Design Studio ouvre une nouvelle fenêtre affichant un rendu en 3D de la scène sur laquelle on travaille, avec des possibilités d’édition : déplacer, tourner et mettre à l’échelle les objets.

Pour les interfaces 2D, de nouveaux outils font leur apparition, comme les classiques Aligner et Distribuer. Ils devraient faciliter la mise au point de dispositions complexes d’éléments, avec un alignement et un espacement automatique des items.

alignanddistribute

L’édition des liaisons entre propriétés ne doit plus se faire à la main, grâce à un éditeur spécifique qui construit l’expression souhaitée. Il permet notamment de visualiser toutes les propriétés disponibles plutôt que de faire appel à sa mémoire. De plus, la liste proposée est limitée, car seules les propriétés qui ont un type compatible sont proposées.

Télécharger Qt Design Studio 1.4.

Source : blog Qt.

Sortie de Qt Installer Framework 3.2.0

Qt Installer Framework (QIF pour les intimes) est utilisé pour l’installateur en ligne de Qt, mais est prévu pour que toute application puisse s’en servir pour ses besoins d’installation et de mise à jour. La version 3.2 vient de sortir. Elle utilise désormais Qt 5.12.4 par défaut, dans les versions précompilées, afin de profiter d’une version d’OpenSSL à jour.

En outre, cette version change la manière de représenter les métadonnées sur les fichiers à installer pour en réduire la taille, en plus d’une série de modifications cosmétiques.

Télécharger Qt Installer Framework 3.2.0.

Voir l’intégralité des changements de cette version.

Qt 5.14 est disponible

La voie vers Qt 6 est en train d’être tracée, Qt 5.14 est la première version de Qt à intégrer des fonctionnalités importantes de la prochaine itération majeure du cadriciel de développement. Les premières préversions sont apparues en septembre, voici venue la version finale, numérotée Qt 5.14.0. Au programme, on retrouve le début de l’introduction d’une nouvelle couche d’abstraction de l’API de rendu utilisée par Qt Quick (au lieu de forcer l’utilisation d’OpenGL), une nouvelle API pour la 3D de très haut niveau, des améliorations de performance pour Qt 3D, entre autres. La prochaine version, la 5.15, disposera d’un support à long terme et aura un très haut niveau de compatibilité avec Qt 6 (notamment avec l’indication des fonctionnalités qui seront supprimées : le processus a déjà commencé, mais ne sera achevé qu’avec Qt 5.15 et le bon avancement de Qt 6).

Côté Qt Quick, le moteur de rendu du graphe de scène (la base de l’implémentation de Qt Quick 2) s’abstrait d’OpenGL grâce à Qt RHI (rendering hardware interface), à la demande. Pour ceux qui font le choix d’utiliser cette nouvelle implémentation, le rendu pourra se faire tant avec OpenGL qu’une autre API comme Vuklan, Metal ou Direct3D 11. À terme, ce mécanisme deviendra celui par défaut et devrait améliorer la performance des applications Qt Quick.

Dans les nouveaux modules pour Qt Quick, on compte d’abord Qt Quick Timeline pour faciliter l’implémentation d’animations (à travers, comme son nom l’indique, une ligne du temps et des images clés). Ce module provient de Qt Design Studio. Aussi, Qt Quick 3D fait son apparition, en tant que préversion technologique (un statut qu’il devrait perdre avec Qt 5.15). Son objectif est de faciliter l’intégration de contenu 3D dans des applications Qt Quick, tout en se synchronisant parfaitement avec le reste de l’application grâce à l’utilisation d’un même moteur de rendu. Qt 3D reste d’actualité, mais est plus orienté vers les applications gourmandes en 3D avec plus de fonctionnalités (mais un moteur de rendu qui tourne en parallèle à Qt Quick).

D’autres fonctionnalités sont apportées, comme l’implémentation des facteurs de mise à l’échelle non entiers (très courants sur les ordinateurs portables non Apple, depuis une demi-dizaine d’années). Toujours au niveau du rendu, Qt gère enfin les espaces colorimétriques, pour un rendu parfait sur les moniteurs calibrés (évidemment, on ne peut rien faire pour les autres…). L’espace de noms QColorConstants définit une série de couleurs précompilées.

Les éditeurs de texte de Qt Widgets et Qt Quick peuvent afficher du texte au format Markdown. Pour l’internationalisation, la version 12.1 d’Unicode est incluse ; QCalendar implémente d’autres systèmes que le grégorien (habituel en Europe et en Amérique du Nord, mais pas forcément en Afghanistan ou en Iran).

Le module Qt Network peaufine son implémentation de HTTP/2 avec une API de configuration et un suivi de la connectivité réseau. Qt WebEngine a été mis à jour vers Chromium 77 ; le module reçoit aussi une API pour contrôler le cycle de vie des pages Web affichées.

Télécharger Qt 5.14.0.

Source : blog Qt.

Simulation de tissus dérivable

Les réseaux neuronaux et leur outillage apportent de nouvelles techniques pour la résolution de problèmes inverses, comme l’obtention d’un modèle 3D à partir d’une image. De manière générale, un problème inverse cherche à reconstruire les causes à partir des conséquences, au contraire d’un solveur habituel. Dans le cas particulier de la simulation physique, un solveur traditionnel part d’une situation initiale et de forces appliquées et détermine le mouvement des objets. Au contraire, le problème inverse associé part d’une position finale (comme la position d’un bras robotique) et détermine la manière de l’atteindre.

La simulation de tissus n’est pas forcément une chose aisée, même dans le cas relativement simple des jeux vidéo (on souhaite un rendu réaliste, pas forcément très précis). Parmi les difficultés, on compte la gestion des contacts avec d’autres objets et les collisions, notamment avec le tissu lui-même. Pour la simulation, on tend souvent à découper le bout de tissu à simuler en carrés (par exemple, une grille de seize carrés de côté), entre lesquels on écrit des contraintes pour que cette grille continue de représenter un bout de tissu. Cependant, même dans cet exemple assez simple, on arrive déjà à 289 sommets (seize carrés côte à côte dans chaque dimension, c’est-à-dire dix-sept points de chaque côté), 512 triangles à afficher, 867 variables (une position 3D pour chaque sommet), 140 000 contraintes juste pour prendre en compte les collisions des sommets. Il est impossible de calculer ensuite un gradient en un temps raisonnable pour résoudre un problème inverse…

Des chercheurs de l’université du Maryland et d’Intel se sont penchés sur le problème. Ils proposent une nouvelle technique pour déterminer les collisions à l’aide de matrices bien plus creuses (en réalité, un problème d’optimisation quadratique), tout en facilitant le calcul de dérivées (à l’aide d’une décomposition QR). Leur solveur est entièrement dérivable par des algorithmes automatiques (comme Autograd de PyTorch), à l’exception de cette opération de détection des collisions et de calcul des réponses ; ils peuvent en exprimer la dérivée à l’aide du résultat des forces appliquées suite à la collision et de la résolution d’un système linéaire triangulaire. Ils indiquent que, de par leurs expériences, ils peuvent réduire fortement la taille des systèmes linéaires en jeu (d’un facteur dix à vingt) et les temps de calcul (entre soixante et cent trente, bien qu’il peut en théorie monter à cent quatre-vingt-trois).

Parmi leurs expériences numériques, les chercheurs ont testé une estimation des paramètres d’un tissu qui flotte dans le vent. Le modèle physique utilisé nécessite l’estimation de trois paramètres : la densité du tissu, la raideur à l’étirement (à quel point le tissu réagit-il lorsqu’il est étiré ?) et la raideur en torsion (à quel point le tissu peut-il être courbé ou plié ?). Ils prennent en entrée une vidéo du tissu sous l’action du vent et de la gravité et en estiment les paramètres : sur septante-cinq images, les vingt-cinq premières servent à l’estimation et les cinquante dernières à la vérification de la précision d’estimation. En particulier, les chercheurs ne considèrent aucun préentraînement (contrairement à NVIDIA et à sa transformation d’objets 2D en modèles 3D) : ils n’utilisent aucun réseau neuronal, uniquement l’outillage développé pour eux. Le résultat est une meilleure estimation des paramètres du tissu par rapport aux méthodes existantes, sauf dans le cas du pliage.

Source : article. Voir aussi : le code source.

NVIDIA transforme des images 2D en modèles 3D en moins de 100 ms

Dans le développement de jeux vidéo, la création de modèles 3D prend une bonne partie du temps total, mais aussi des gens compétents. Certains ont donc eu l’idée d’automatiser cette partie, par exemple à l’aide de réseaux neuronaux, la tarte à la crème actuelle. L’idée très générale est simple : on entre une image, on ressort un modèle 3D. Un peu plus en détail, on voit déjà apparaître des zones d’ombre : on peut traiter assez facilement des images avec des réseaux neuronaux, mais quid des modèles 3D ? Quelle architecture pourrait-on utiliser pour relier les deux ? Comment effectuer l’entraînement du système ? Commençons par la troisième partie.

Quand il s’agit de transformations du genre, il est courant d’utiliser des GAN, des réseaux antagonistes génératifs : dans ce cas-ci, par exemple, un réseau neuronal crée un modèle 3D à partir d’une image 2D, un autre réseau décide si le modèle 3D correspond bien à l’image 2D. Ainsi, on dispose d’un mécanisme qui peut améliorer le générateur de modèles 3D. Cependant, comment décider si le modèle 3D correspond à l’image ? Pour ce faire, il faut utiliser un moteur de rendu, auquel on fournit le modèle 3D produit : il ressort une image, que l’on compare avec l’image d’entrée.

Et si on simplifiait la chose, en intégrant le moteur de rendu dans le réseau neuronal (au moins pendant son entraînement) ? Ainsi, on a un réseau qui prend une image en entrée, ressort un modèle 3D, qui passe dans un moteur de rendu, on calcule l’erreur du modèle en comparant l’image obtenue à la sortie du moteur de rendu avec l’image d’entrée. Il faut “juste” arriver à dériver le moteur de rendu, ce que l’on arrive à faire depuis quelques années, de manière approchée (mais de manière exacte avec du lancer de rayons). Les chercheurs introduisent cependant une nouvelle approche, DIB-R (differentiable interpolation-based renderer), avec un mélange d’interpolation locale (comme le fait tout moteur de rendu, par exemple à l’échelle d’un triangle éclairé) et d’agrégation globale.

birb.

Ensuite, quelle architecture de réseau faut-il utiliser ? Le choix des chercheurs de NVIDIA s’est porté sur un encodeur-décodeur, pour (en principe) réduire l’image à quelques paramètres très significatifs (par exemple : oiseau, petit, jaune). L’encodeur peut donc notamment effectuer une tâche de reconnaissance d’objet.

model2a-2.png

Comment représenter un modèle 3D ? Les chercheurs ont pris la manière la plus classique de faire : ils présupposent que le modèle peut être formé à partir d’une sphère avec une discrétisation donnée ; la sortie du modèle est la position et la couleur de chaque arête de cette sphère. D’une certaine manière, cette sortie est une “image” à six dimensions (trois pour la position, trois pour la couleur), d’où l’utilisation d’une architecture avec des convolutions pour gérer la sortie. (Pour obtenir de meilleurs résultats, les chercheurs ont aussi étendu leur travail pour générer une texture en sortie, ainsi que des informations d’éclairage.) L’arrière-plan de l’image est extrait et affiché derrière le modèle 3D.

allsup.png

Le résultat est une application qui peut générer des modèles 3D en moins de cent millisecondes. Ces derniers ne sont pas encore parfaits (bien que meilleurs que les approches précédentes), mais sont une bonne base pour un artiste : il peut gagner beaucoup de temps en partant de ces modèles plutôt que de zéro. Son entraînement a néanmoins pris deux jours.

Source : l’article et son supplément.

Voir aussi : le code source de DIB-R.

Un apprentissage profond rapide pour un grand nombre de classes

L’apprentissage automatique est une discipline issue des statistiques qui cherche à effectuer des prédictions. Par exemple, dans un problème de classification : sachant la valeur d’une série de variables, eu égard aux données disponibles, quelle classe attribuer à l’échantillon que l’on vient de recevoir ? Pour ce faire, l’algorithme d’apprentissage extrait des corrélations dans les données qu’il reçoit. L’une des techniques pour y arriver est d’entraîner un réseau de neurones, une idée très vaguement inspirée par la biologie et l’organisation des cerveaux.

Cependant, l’apprentissage risque de patiner dans le cas où le nombre de classes possibles est grand. Par exemple, la classification de spam ne fait appel qu’à deux classes : spam ou pas (“ham”). Au contraire, prédire un produit qu’un consommateur pourrait acheter nécessite de retourner un produit dans le catalogue d’un magasin (de par le monde, Amazon vendrait pas loin de trois milliards de produits différents…). Si on prend un réseau neuronal assez basique, on se retrouve avec au moins deux mille paramètres à apprendre pour chaque produit : dans le cas d’un magasin en ligne de grande taille (cent millions de produits à l’étalage), cela fait deux cent milliards de valeurs numériques à déterminer, presque un téraoctet et demi — sans compter les données sur lesquelles entraîner ce modèle. C’est beaucoup trop ! Surtout que l’entraînement est surtout effectué sur des cartes graphiques, qui ont du mal à dépasser les trente-deux gigaoctets de mémoire pour le moment…

Des chercheurs de l’université Rice, aux États-Unis, ont eu l’idée d’appliquer une série de transformations aléatoires sur les données pour que cet entraînement soit plus facilement réalisable. Ils ont décidé d’appeler leur algorithme MACH (merged average classifiers via hashing). Aléatoirement, les cent millions de classes sont divisées en un certain nombre de seaux (peu nombreux : par exemple, dix ou cinquante), on entraîne alors le réseau neuronal (ou n’importe quel autre algorithme d’apprentissage) à prédire dans quel seau doit aller chaque échantillon (on parle donc de métaclasse). Formellement, il ne s’agit pas d’une répartition aléatoire, mais selon une fonction de hachage (idéalement, une fonction de hachage a une sortie qui ressemble furieusement à un nombre aléatoire). Ce modèle n’est pas encore utile : il faut répéter ce processus un grand nombre de fois, toujours en créant les métaclasses plus ou moins au hasard. Ensuite, quand on dispose d’un nombre suffisant de modèles, on peut leur demander leur prédiction : la valeur que cet empilement de modèles doit produire est l’élément qui est présent dans toutes les prédictions. (Plus précisément, les chercheurs travaillent avec des probabilités que l’échantillon appartienne à une métaclasse, ce qui permet de gérer le cas où les différents modèles ne prédisent pas des métaclasses qui se superposent : c’est le théorème 1 de leur article.)

Tout l’intérêt de cette approche se situe dans la création arbitraire de métaclasses : les classificateurs à entraîner sont beaucoup plus petits, ce qui règle le problème de la taille du modèle complet. Cependant, il existait déjà des techniques pour gérer des nombres extrêmement importants de classes, comme Parabel, FastXML ou la création de plongements denses (représenter une classe avec quelques variables). Néanmoins, elles souffrent de problèmes de performance : leur découpage n’est jamais assez indépendant (l’entraînement des modèles ne peut pas se faire en parallèle sur des machines ne communiquant pas) ; de plus, MACH effectue de meilleures prédictions (du moins en regardant le rappel et en maintenant la précision à 100 %).

Source : article publié à NeurIPS 2019.

Broadcom lance son premier commutateur à 25,6 Tb/s

La compétition dans les centres informatiques pour le matériel réseau n’est pas neuve. Autrefois, elle portait sur les capacités techniques du matériel ; désormais, avec l’avènement de l’informatique en nuage, le coût devient plus plus important. Broadcom vient de lancer sa première puce pour la commutation dans les réseaux Ethernet avec une capacité de commutation maximale de plus de vingt-cinq térabits par seconde, baptisée Tomahawk 4.

Celle-ci peut monter à soixante-quatre ports Ethernet à quatre cents gigabits chacun (en faisant la somme, on tombe sur vingt-cinq térabits et six cent gigabits par seconde) ou deux cent cinquante-six ports à seulement cent gigabits par seconde chacun. Elle est constituée de trente et un milliards de transistors et est fabriquée par TSMC sur un processus en sept nanomètres. La puce intègre cinq cent douze composants de sérialisation-désérialisation (SerDes) en PAM4.

L’intérêt d’un tel matériel est multiple, mais se résume à une diminution du nombre de commutateurs nécessaires pour desservir une même quantité de matériel informatique : pour connecter deux cent cinquante-six machines, pour un débit de cent gigabits par seconde chacune maximum, il suffit d’un seul Tomahawk 4. Le matériel actuel ne peut monter, pour de tels débits, qu’à cent vingt-huit ports : il faut donc mettre deux étages de commutateurs pour garantir une connectivité entre toutes ces machines. Le temps de commutation restant équivalent par paquet, la latence d’une communication est divisée par trois. De plus, la consommation énergétique de l’installation est diminuée de septante-cinq pour cent, tout comme le coût d’achat.

Cette puce dispose de fonctionnalités de gestion de la charge, de mise en tampon et de contrôle de la congestion améliorées. La mémoire tampon pour les paquets qui ne peuvent pas être immédiatement transmis sur le lien souhaité est globale à la puce, pour éviter de mettre à disposition de la capacité sur les ports qui n’en ont pas besoin. De plus, cette puce intègre quatre cœurs de calcul ARM à un gigahertz, pour effectuer de l’instrumentation avancée dans le réseau (ces cœurs étant entièrement programmables), au centre des opérations de traitement des paquets Ethernet. La puce intègre aussi nativement une application de suivi statistique, avec une télémétrie intrabande IFA 2.0, une mesure de la qualité des liens et un suivi des pertes de paquet et de la congestion. Pour aller plus loin, Broadcom libère l’API pour ses commutateurs, sous le nom de Broadcom Open Network Switch APIs (OpenNSA, une généralisation de OpenNSL).

Sources : EETimes, WccfTech, The Next Platform.

LLVM réfléchit à l’implémentation native des matrices

Un compilateur est souvent divisé en deux parties distinctes : une partie avant, qui lit un code source ; une partie arrière, qui écrit un code binaire pour une architecture donnée. Ces deux parties communiquent à l’aide d’une représentation intermédiaire (IR), sur laquelle bon nombre d’optimisations peuvent être effectuées. Un tel découpage facilite l’implémentation de nouveaux langages ou le ciblage de nouvelles plateformes. Pour qu’il soit efficace, le compilateur doit reposer sur une représentation intermédiaire de qualité, à un niveau d’abstraction bien choisi : ni trop élevée (l’IR correspondrait à un langage de programmation complet), ni trop basse (l’IR serait alors un assembleur pour une architecture précise). L’IR doit être un compromis entre une grande possibilité d’optimisation du code, une simplicité d’écriture du générateur de code binaire (qui a déjà beaucoup de choses à gérer avec les détails de l’architecture cible, comme l’utilisation des registres du processeur ou les optimisations spécifiques) et une indépendance de la plateforme ciblée (pour que les optimisations effectuées sur l’IR soient utiles à plusieurs plateformes).

La représentation intermédiaire LLVM IR est utilisée par la famille de compilateurs de l’écosystème LLVM, comme Clang ou Flang. Ses développeurs la décrivent comme un langage d’assemblange pour une plateforme virtuelle (d’où l’acronyme LLVM : low-level virtual machine), avec toutefois la notion de fonction (ce qui permet notamment d’écrire une valeur de retour : la gestion des conventions d’appel peut alors dépendre de la plateforme cible, ce qui est par exemple le cas en C et C++). Elle dispose d’une série de types de base : des scalaires, évidemment, des agrégats, c’est-à-dire des ensembles de valeurs stockées ensemble (comme une structure ou un objet), mais aussi des vecteurs à longueur fixe ou variable, qui servent à donner aux passes d’optimisation autant d’informations que possible sur le code (et donc à générer un code binaire aussi efficace que possible, in fine, avec l’utilisation des instructions SIMD disponibles sur le processeur visé).

Dès 2018, certains développeurs d’Apple travaillant sur LLVM proposent d’ajouter un nouveau type de structure de données dans cette représentation intermédiaire : les matrices. L’IR disposerait aussi d’une série de fonctions intrinsèques pour travailler sur ces matrices, comme l’extraction et la mise à jour d’une valeur, un produit entre matrices ou entre une matrice et un vecteur. L’avantage serait d’apporter plus d’informations au générateur de code binaire : le compilateur pourrait alors fournir de bien meilleures garanties de vectorisation du code (voire d’utilisation des instructions matricielles, par exemple celles présentes dans l’architecture ARM v8.6) ; de même, il pourrait s’arranger pour éviter des écritures en mémoire trop fréquentes, laissant les valeurs nécessaires dans les registres du processeur. Il pourrait aussi décider de la meilleure manière de représenter la matrice en mémoire : soit ligne par ligne, soit colonne par colonne, soit comme une matrice creuse, selon le type d’accès le plus fréquent. Le générateur de code pourrait aussi décider de remplir la mémoire entre les lignes ou les colonnes, de telle sorte que chaque ligne ou chaque colonne soit bien alignée.

En détail, la proposition porte sur quatre types de fonction intrinsèques : transposition, multiplication, lecture et écriture (y compris avec décalages). L’idée d’un type entièrement spécifique aux matrices n’a pas été très appréciée de tous les développeurs (à cause de la complexité de l’opération), c’est pourquoi elles sont plutôt implémentées comme des vecteurs : les dimensions de la matrice sont passées en argument aux fonctions intrinsèques.

Clang suit assez rapidement la tendance et prévoit déjà d’utiliser ces nouvelles fonctionnalités. Les matrices seraient indiquées par un attribut, matrix_type(lignes, colonnes), où les dimensions de la matrice seraient constantes ; cet attribut porterait sur une variable de type tableau, contenant au moins autant d’éléments que la matrice déclarée. Le compilateur fournirait une série d’opérations intégrées correspondant aux fonctions intrinsèques.

Source : liste de diffusion LLVM (proposition d’origine, proposition reformulée, détails d’implémentation pour Clang), premier commit.