Quelques raccourcis :
Grâce à l'Université de Sherbrooke (en particulier, grâce à Jean Goulet et à Claude Cardinal du CeFTI) et au Collège Lionel-Groulx, j'ai eu le privilège de participer à cppcon 2014. Merci aussi à Gabriel Aubut-Lussier, Samuel Denis D'Ortun et Pierre Prud'homme pour leurs suggestions d'enrichissements et de corrections.
Ce qui suit est une sorte de journal de voyage, avec éléments humains et éléments techniques.
Pour un peu de bagage supplémentaire sur l'événement, voir :
Pour les journaux de voyage d'autres participants, voir :
Début du contenu technique
Je vais mettre le contenu technique dans des blocs les mettant comme celui-ci. Si vous ça ne tombe pas dans vos cordes, passez par-dessus.
Fin du contenu technique
Débarqué à Dorval peu après 2 h 30, pour mon vol à 5 h 30. L'aéroport est tranquille, ce qui me permet de gérer la paperasse d'usage rapidement.
J'ai besoin de monnaie américaine, mais tous les bureaux de change sont fermés jusqu'à 4 h, ce qui me coince un peu (j'embarque à 4 h 55 et je n'aurai pas vraiment le temps de chercher un autre bureau lors de mon transit à Toronto, pour lequel j'ai moins d'une heure incluant le temps de manger quelques chose et de passer les douanes américaines). Je me renseigne et il y a un autre bureau de change près de mon point d'embarquement, alors je pourrai procéder à la dernière minute (cela dit : cher! 40 $ de frais pour 200 $ de retrait... Ouch!)
Le vol Toronto-Montréal est un express (45 minutes total, deux sièges de chaque côté plutôt que trois). Je suis aux abords du hublot, mais entre deux hublots alors je ne vois pas très bien le paysage. Triste, le lever du soleil est charmant. Petit bonheur: les hôtesses apportent du café (chaud mais pas bouillant, mais bon...) et il est convenable.
Coup d'oeil charmant en descendant sur Toronto aux aurores, avec toutes les lumières de la ville alors qu'il fait encore sombre dehors.
Une personne devant moi regarde un documentaire sur les ours qui chassent le saumon et se chamaillent entre eux pendant le vol. C'est amusant au début, mais plus de 30 minutes de ces grosses bêtes qui tuent des poissons et s'arrachent mutuellement la chair pour se les piquer, c'est un peu « heavy ».
Moi, en vol, j'aime bien les cartes interactives qui montrent où on en est.
En approchant de Toronto, je pense à mon épouse que j'aime. Si le colloque était à distance de route, j'aurais essayé de faire le trajet en sa compagnie. Za, tu me manques déjà.
À Toronto, l'aéroport est gros mais bien organisé. Je ne sais pas pourquoi, mais lors de mon dernier trajet, on m'avait permis de passer outre l'étape du « on se déshabille et on vide nos sacs » lors du transit vers Denver, alors que là j'ai fait tout le processus à nouveau. Mystérieuses règles...
Deux agents nous guident et sont adorables: une dame très entraînante qui vérifie nos cartes d'embarquement en riant fort, et un monsieur qui fait tout en chantant et en faisant taper les gens des mains.
J'ai le temps de passer brièvement aux toilettes (plus simple à l'aéroport que dans l'avion) et de prendre un bagel + omelette + fromage. Pas transcendant mais correct. 6,50 CAD. Cher, mais moins que dans l'avion. Pas mal chaud, alors ça m'a pris quelques bouchées (je pense à mes filles et à mon épouse en disant ça, car elles me font une réputation TOTALEMENT injustifiée de quelqu'un qui prend de grandes bouchées ). Quelques secondes ensuite pour me rendre à la porte pour embarquement, et on saute dans le vol vers Seattle...
Arrivé à Seattle vers 10 h 20 (13 h 20 à l'heure de Montréal), mais j'ai dû me promener un peu pour trouver un point d'information me permettant de comprendre comment me rendre à mon hôtel (je cherche les navettes, moins chères que les taxis; ça reste 41 USD mais tout de même). J'écris ceci en attendant que ma navette arrive, d'ailleurs. J'ai été extrêmement chanceux pour ce qui est du vol. Placé aux abords d'un hublot, juste à côté de l'aile (à droite de l'avion), j'avais une vue pratiquement idéale... Et il n'y avait pas de nuages! J'ai donc traversé les États-Unis (pas exactement, étant parti de Toronto, mais tout de même) d'Est en Ouest en ayant en tout temps le panorama sous les yeux. Les changements de paysage, les rocheuses, le (superbe, faut le dire) Mont Rainier qui surplombe Seattle et sa région, etc. Wow! Difficile de demander un meilleur coup d'oeil. Seattle, vu de haut, semble plutôt vert. Beaucoup d'arbres, beaucoup d'eau (j'ai de la difficulté à dire si ce sont des lacs un peu partout ou si les bâtiments sont construits sur des îles). Je dirais que ça semble un peu plus petit que Montréal, mais ce n'est peut-être qu'une impression dû aux grandes zones d'eau un peu partout.
L'aéroport m'a semblé moins moderne et moins bien organisé que ceux que Charles-de-Gaulle, celui de Toronto ou celui de Denver (où je suis allé en juin pour présenter un article dans le cadre de mon doctorat). Plus gros que Dorval mais c'est quand même à celui-là que, dans ma petite expérience des aéroports, il ressemble le plus.
J'ai croisé Howard Hinnant en cherchant un point d'information (il a un visage reconnaissable). Je l'ai croisé à nouveau quand je suis arrivé pour m'inscrire à la navette; même les sommités voyagent de manière raisonnée, c'est rassurant.
La navette est venue me chercher après moins de quinze minutes d'attente. Nous sommes manifestement nombreux à nous rendre à Bellevue, lieu du colloque. Une charmante dame à côté de moi, noire d'environ 50 ans je dirais, semblait triste que les gens passent devant elle, mais sa destination semblait moins populaire.
J'ai atteint l'hôtel vers 11 h 45 heure de Seattle (14 h 45 heure de Montréal). La dame à l'accueil a un anglais approximatif, mais est gentille. Je ne logerai pas dans un truc luxueux, évidemment, mais je suis supposé avoir accès à une petite buanderette, et avoir un petit frigo et des instruments pour me faire à manger. Il y a des lignes d'autobus pas loin et je pense pouvoir faire le trajet décemment en transport en commun (Google Maps parle de 29 minutes aller, 31 minutes retour; c'est un trajet direct si je marche un peu entre l'hôtel et l'arrêt d'autobus, ce qui me convient). Malheureusement, je ne peux prendre possession de ma chambre avant 15 h, ce qui m'agace (j'étais dans le mariage de mes grands amis Martine Drapeau et David Viens hier soir, et je n'ai dormi que deux heures environ alors, ajouté au fait que j'ai été dans des aéroports et dans des avions pendant plus de dix heures depuis, j'aurais bien aimé de rafraîchir et faire une sieste avant d'aller m'inscrire au colloque, vers 16 h).
Le chauffeur de la navette m'a dit que le bâtiment à côté de l'hôtel brille mauve la nuit, et que c'est très étrange. On verra bien.
Je me suis donc installé au Starbucks non loin de l'hôtel pour prendre quelques cafés en attendant, et échanger avec mon amoureuse (qui me manque) sur Skype. Une partie de ce qui suit est adapté de notre conversation à elle et à moi.
Il y a une petite épicerie entre l'hôtel et le café d'où j'écris. Je vais essayer de réduire mes frais en me faisant des lunchs. Faut juste que je voies de quoi la chambre a l'air avant.
Ici, la végétation fait très Twin Peaks (on est dans le même coin, évidemment), mais c'est un bel endroit. Il y a beaucoup de côtes, d'arbres, de gens qui marchent... Ça rappelle un peu Sherbrooke, mais avec une végétation différente. La Starbucks où j'écris se situe à distance de marche de l'hôtel; dans le même petit centre d'achats, il y a en fait deux Starbucks (je vois l'autre de la fenêtre); c'est un peu comme nos Tim Horton's, mais en plus chic. Faut dire que ça a été inventé ici, alors...
Seattle, même sa banlieue immédiate où je suis, c'est très différent de Denver. Très « vert conifère »; même les feuillus sont foncés. Le climat est aussi très différent. Météomédia parlait d'une journée très chaude, mais c'est relativement confortable pour le moment (addendum: non, fait vraiment chaud finalement, et très humide, un peu comme au Québec J'étais juste chanceux d'être à l'ombre). J'ai fini par accéder à ma chambre vers 13 h 48 plutôt que 15 h. C'est une bonne nouvelle, car je « sentais le voyageur ». Il faisait très chaud à mon arrivée mais j'ai trouvé la climatisation alors ça va s'améliorer pendant que je vais prendre ma douche (tiens : ils fournissent le savon mais pas le shampooing et le revitalisant).
Pour le colloque, si vous êtes curieuses/ curieux : mon horaire est http://cppcon2014.sched.org/patrice_roy et les gens sur place (dont moi) sont http://cppcon2014.sched.org/directory/attendees (ça ne vous dira peut-être rien, mais c'est du gratin intellectuel dans mon petit créneau scientifique personnel). L'inventeur du langage C++ est là, à titre d'exemple. Les auteurs de plusieurs livres que j'utilise dans mes cours. Les architectes des trois principaux compilateurs. Les gens du comité de standardisation du langage... C'est pour ça qu'on m'a envoyé ici.
On m'a plusieurs fois posé la question, mais sachez que je ne présente pas d'article cette fois. En fait, je vais peut-être faire une petite présentation mardi matin, mais ça va dépendre si j'ai le temps de préparer un truc décent; on m'a proposé de faire un « lightning talk », soit une présentation de 15 minutes un peu punchée. J'ai une idée, mais je ne le ferai pas si je ne peux pas la préparer raisonnablement. Jeudi soir, je dois aller souper avec un petit groupe d'entre eux (les co-organisateurs de l'événement, avec moi qui ai fait de petites contributions – passer en revue certaines propositions de présentations, trouver quelques conférenciers) pour préparer le colloque de l'année prochaine. Ce sera aussi une nouvelle expérience pour moi. Ça ne veut pas dire que je vais participer en personne chaque année ($$$) mais c'est très intéressant pour moi.
Je suis arrivé au Meydenbauer Center vers 16 h 50. L'autobus qui passe relativement près de mon hôtel m'a déposé à deux coins de rue du centre, qui se trouve tout près du terminus d'autobus de la ville.
Bellevue (la banlieue de Seattle où a lieu le colloque) est un peu plus grosse que Longueuil, je dirais (je suis peut-être dans les patates, mais c'est l'impression que j'en tire). Beaucoup d'espaces verts, un centre-ville propre et plutôt tranquille (ça me frappe à chaque fois; c'est fou à quel point Montréal bouge en comparaison avec la plupart des autres grands centres que j'ai visités en Amérique du Nord). La plupart des gens que j'ai croisés ici sont en relativement bonne santé; ça ne semble pas être un haut lieu de la malbouffe et du mal-être. J'ai vu plusieurs vélos, plusieurs joggeurs, beaucoup d'étudiant(e)s.
En marchant de l'autobus au centre de congrès, j'ai croisé quelques visages connus (Lawrence Crowl, entre autres, qui a conçu plusieurs éléments clés du standard). C'est impressionnant de voir tous ces gens en un même lieu, mais je vais m'y faire. En entrant dans le centre, il faut monter un étage pour se rendre à notre colloque. C'était l'accueil. Comme je m'en doutais, ils ont mêlé mon prénom et mon nom, un classique chez les anglophones (Roy Patrice au lieu de Patrice Roy). Je me suis bien amusé; l'organisateur (Jon Kalb, très gentil, un spécialiste du code sécuritaire) s'était déjà trompé en m'écrivant et il avait fait corriger les listes d'invités, mais mon badge reste à l'envers. Je sens que je vais passer la semaine à l'expliquer aux gens, celle-là :)
D'ailleurs, en confirmant notre présence, on nous a remis un badge (à l'envers dans mon cas) et une enveloppe, qui contenait plusieurs cadeaux d'informaticiens (en particulier donnés par Google): un concours pour gagner un téléphone haute-gamme, un 2 Go d'espace disque supplémentaire sur Dropbox (ceux qui ne connaissent pas, c'est pas grave, mais sachez que c'est chouette) et un puzzle à assembler, placé dans un petit sac de velours identifié à Google (j'ai bien ri en voyant ça :) ). Il y avait une mention à côté de mon nom d'aller voir Jon (l'organisateur susmentionné). J'y suis donc allé, pour tomber sur Herb Sutter (moins grand que moi mais plus grand que je ne le pensais), toujours aussi dynamique, de même que James McNellis et quelques autres. Pour ceux qui ne le savent pas, James McNellis est l'un des visages publics les plus connus de Visual Studio (le compilateur C++ de Microsoft) et Herb Sutter est l'architecte principal de C++ chez Microsoft, de même qu'un auteur très réputé, un conférencier important (il donnera quelques présentations sur les atomiques, auxquelles je vais assister parce que c'est un truc que j'enseigne mais qui est difficile à bien enseigner; je vais piger des idées ici), et l'ancien Chairman du standard ISO de C++. Une vedette dans notre créneau, disons.
Jon nous a remis (à nous comme aux autres membres du comité de programme) une petite boîte de chocolats noirs fins d'origine, en remerciement de notre aide. Je veux avertir mon épouse et mes flos que je ne le rapporterai pas à la maison (douanes), mais que je vous ferai un rapport détaillé sur le petit délice que cela risque de constituer. C'est une toute petite boîte à six morceaux, mais ça semble succulent.
Il y a une réception vers 20 h, à laquelle je vais participer (ça termine à 22 h; je verrai si je quitte avant, ça dépendra de mon état). Mon autobus passe aux 30 minutes environ et le dernier est vers 23 h alors je devrais pouvoir passer une nuit normale tantôt.
Vers 18 h 15 (heure de Seattle), je suis sorti manger une bouchée. Notez que je suis en plein centre-ville de Bellevue. J'ai fait plusieurs pâtés de « maison » (des petits gratte-ciels, surtout), j'ai croisé plusieurs restos (dont cinq Starbucks, incluant trois sur le même coin de rue :) ), mais presque tous fermés. Outre un Subways' (pas très exotique) et quelques restos hors de prix, j'ai fini par trouver un resto thaï encore ouvert vers 19h où la salade de papaye coûtait moins de dix dollars. C'est de là que je vous écris.
Bière locale (Mac n' Jacks), très correcte. Pour la salade, j'avais demandé très épicé et j'ai été servi: j'ai eu de la difficulté à la finir (j'ai pris mon temps, ma bière et un verre d'eau glacée). Mioum! Vous allez comprendre, me connaissant: papaye râpée, carotte, laitue, sauce nguoc nam, piment, haricots, arachides, lime. Pas mal du tout. Fait très chaud, alors c'est le genre de bouffe qui fait du bien. Cela dit, une « mi-épicée » m'aurait probablement suffi (et c'est rare que vous allez « entendre » ça de moi, qui aime bien manger épicé). Excellent service.
Quelques remarques sur la route, toujours instructive en voyage:
C'est une région du monde où il ne faut pas avoir peur de monter et de descendre des côtes. Tout est en pente ici. C'est amusant; je ne connaissais rien de la géographie locale (je ne suis jamais venu dans le coin) alors j'ai beaucoup à apprendre.
La cérémonie était chouette. A priori, il y avait de la bouffe (format goûter, pas format repas, mais tout de même: hummus, du bon fromage, de bons fruits et légumes, quelques satays de poulet, de petits pains farcis pas banals, des bouchées de saumon fumé, des bouchées de thon cru mariné, des oeufs de caille farcis... Très bon), du vin et de la bière (détail: le premier verre était aux frais du colloque, mais quand j'ai pris un verre à mes frais... 8,50 USD! Et ce fut la fin du vin pour moi ce soir).
J'ai reconnu plusieurs personnes, mais je n'ai pas pu m'empêcher d'aller bavarder avec l'un des québecois là-bas, Louis Dionne, un matheux de l'université Laval dont j'avais été appelé à évaluer (positivement, d'ailleurs) la présentation. Un gars passionné et intéressant, qui contribue à Boost, un projet de programmation important. J'aurais aimé voir les gens d'Ubisoft avec lesquels j'étais en contact mais je ne les ai pas croisés ce soir (ça ne veut pas dire qu'ils n'y étaient pas; on était pas mal nombreux).
Y étaient aussi pour bavarder un chic français de de Paris, dont le nom m'échappe malheureusement (note : c'est Edouard Alligan, qui a donné une présentation sur C++ multiplateforme : https://www.youtube.com/watch?v=K4c8QJvueas), et Michael Caisse (ils prononcent Michael Caisse, à l'anglaise; c'est Américain de Californie malgré ce que laisserait croire son nom; il a des grands-parents qui parlaient français, mais lui et sa famille sont plus dans le mélange anglais-espagnol en raison de leur situation géographique). Tous les trois donnent dans la métaprogrammation, un truc que j'utilise beaucoup et que j'enseigne chaque session comme pratique de programmation, mais eux en font leur pain et leur beurre. Michel et Louis ont échangé sur le processus pour faire accepter les idées de Louis (fort intéressantes, d'ailleurs) par « le grand public » (lire ici: des programmeurs moins au fait du type de pratique qu'il met de l'avant). Le processus de Boost ressemble beaucoup, sur le plan politique, au processus pour faire accepter sa thèse de doctorat par un jury, disons-le comme ça.
Brefs échanges avec quelques autres personnes ici et là. Jon Kalb qui manque de badges vierges pour les inscrits de dernière minute, par exemple. Chandler Carruth (Clang, Google) se promène et prend des photos. C'est amusant.
Je suis revenu à l'arrêt d'autobus parce que je suis trop fatigué, faut que je dorme un peu, mais malheureusement, à cette heure, mon autobus passe aux heures plutôt qu'aux trente minutes (ma faute de ne pas m'être préparé correctement) alors je vais devoir attendre au peu...
Demain matin, le tout commence pour vrai.
Pour commencer, quelques compléments d'information pour le jour 0 :
Le décalage horaire m'a eu ce matin; je me suis réveillé à 5 h comme il se doit, mais je me suis endormi à nouveau jusqu'à 6 h 40 alors maintenant, c'est la course. N'habitant pas à distance de marche du centre ce congrès, je dois être prévoyant (autobus aux 30 minutes et ≈30 minutes de trajet, c'est serré). Heureusement pour moi, ce matin, ça commence vraiment à 8 h 45 alors je vais pouvoir me faire un lunch.
J'ai eu des crampes fortes à une cuisse et à un mollet cette nuit. La combinaison de grande fatigue et de beaucoup de marche, sans doute.
L'hôtel offre un Breakfast to Go, c'est-à-dire un café et un muffin commercial + barre tendre . Mais c'est Ok, je ne m'attendais pas à autre chose et ça me suffit. Au colloque, il y a des fruits et du café, alors tout est sous contrôle.
Ça va me prendre beaucoup de monnaie cette semaine. L'autobus (pas cher, 2,25 USD hors-pointe et 2,50 USD en période de pointe) requiert la monnaie exacte, et je vais devoir laver un peu de linge ce soir mais j'entre trop tard pour en prendre au lobby alors ça va me prendre pour 6 USD de pièces de 25 sous.
Trucs amusants :
Ce matin, c'est « la vraie affaire ». J'ai croisé Nicolai Josuttis (The C++ Standard Library, C++ Template: The Complete Guide) et Bjarne Stroustrup en entrant ici. L'une des premières présentations sera faite par Scott Meyers tantôt. Il se prépare sur la scène présentement. Disons à tout le moins que c'est une autorité en la matière.
La salle de conférence est vaste (on doit être plus de 300 ici) et il y a un band rock live qui joue sur place.
Âges très variés. Un certain nombre d'étudiants de la région sont venus se joindre à nous, mais il y a beaucoup de têtes grises et de têtes blanches (comme moi, faut l'avouer maintenant). La plupart des gens sont en santé; d'ailleurs, je n'ai pas vu le moindre individu en surpoids dans l'autobus ce matin. Vive les villes universitaires, je suppose.
Les gens du comité de standardisation (qui sont dans bien des cas aussi des organisateurs ici) sont très courtois dans leur ton et dans leur choix de mots. À date, tous ceux que j'ai croisés ont été très gentils, avec moi comme avec les autres ici.
Nous sommes officiellement 583 ici, finalement. Wow! C'est la première incarnation de cppcon. Un gros succès déjà. Leur budget de publicité était zéro; tout s'est fait par contacts via les médias sociaux. C'est impressionnant.
Jon Kalb vient de lire l'énoncé de mission du colloque, qui met l'accent sur l'exploration et l'encouragement des meilleures pratiques de programmation en C++ contemporain. Ça cadre bien.
On ajoute des sessions plus tôt le matin (trop de contenu). Je pensais peut-être offrir un Lightning Talk de 15 minutes mais il y en a 18 déjà alors on verra
Le contenu sera enregistré, mais il y a plus de 100 présentation sur cinq jours alors je doute pouvoir faire le tour a posteriori.
Pour voir la salle un peu avant le début : https://twitter.com/herbsutter/status/509003397522280448/photo/1 (je suis à droite alors on ne me voit pas dans la photo).
Pour le voir en-ligne : https://www.youtube.com/watch?v=y7WSmCpWAuo
Note culturelle
Pour comprendre certains passages dans ce qui suit (et ailleurs dans cette page), il faut savoir que, bien que C++ 14 soit ratifié, nous n'avons pas encore fini de bien cerner les meilleures pratiques et les idiomes de C++ 11. Des gens comme Scott Meyers et Herb Sutter, qui écrivent des livres à ce sujet, se questionnent sur certains points clés.
En 2014, Scott Meyers a soulevé une question sur le passage de certains types de paramètres à des fonctions (voir http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html pour des détails), et il se trouve que la communauté est divisée entre ce qu'il propose et ce que Herb Sutter propose (voir les commentaires dans http://www.blogger.com/comment.g?blogID=7101933101966798446&postID=7122649870026005997 pour en savoir plus). Il y a plusieurs références indirectes à cette réflexion publique sur cette page.
Scott Meyers est franchement drôle, tout en étant intimidant (c'est un expert d'entre les experts). Je connais le contenu qu'il présente, mais c'est un grand présentateur. Le sujet est la déduction de types, mais (a) je dois l'expliquer aux étudiant(e)s et (b) j'ai lu ses textes sur le sujet alors je connais bien son vocabulaire. C'est aride (je plains ceux pour qui c'est un premier contact!) mais il est très amusant.
Sa technique pédagogique :
Début du contenu technique
Petite retouche à faire à mon matériel pour :
int x = 3;
const int &rcx = x;
auto && r = rcx; // ici, r est une const lvalue ref car les règles des templates s'appliquent
Intéressant : auto p = q; sur un const int* donne un const int*, mais aussi sur un const int * const; le pointeur est copié, donc la copie n'est pas const, mais les pointés demeurent const. C'est différent si on prend une référence sur le pointeur const (le const demeure dans ce cas).
Pour l'étrange auto x{1,2,3};, un initializer_list<int>, qui diffère de template<class T> void f(T); avec f({1,2,3}) (illégal, car {1,2,3} n'a pas de type), voir n3922 où auto x1{1,2,3}; serait illégal alors que auto x2={1,2,3}; serait initializer_list<int>) pour C++ 17. Weiiird!
Dans C++ 17, selon n3922, auto x{17}; ferait de x un int alors que auto y={17}; ferait de y un i initializer_list<int>. C'est pas encore accepté, disons-le comme ça. Cela dit, il semble que MSVC implémente déjà n3922, alors faut être prudents pour la portabilité du code source.
Oh, surprise (pour moi) : dans une capture de λ, les qualifications const et volatile sont préservées pour les copies, mais pas si on fait une copie manuelle. Ainsi, si cx est const int, alors [cx](){...} fait du cx de la λ un const int, même si la λ est mutable, alors que [cx = cx](){...} crée une copie explicitement et élimine les qualifications pour cette copieé
Pour identifier les types effectivement déduits, Scott Meyers propose :
template <class T> class TD; // ne pas définir; TD signifie « Type Displayer »
Ainsi, si on a auto v = f();, alors TD<decltype(v)> var; ne compilera pas et le message d'erreur décrira le type en tant que raison. Par contre, typeid(T).name() est mauvais pour en arriver au même résultat, ne donnant pas le type souhaité (c'est une exigence du standard qu'il le soit; Scott Meyers disserte à ce sujet).
Truc pas gentil : si int x; alors decltype(x) est int mais decltype((x)) est... int&. Celle-là a surpris bien des gens, dont moi. Prudence dans des cas comme celui où une fonction retournerait decltype(auto) et où la valeur retournée serait une variable locale (boum!). Dans ses mots, decltype(auto) is sensitive to function implementation.
Recommandation : n'utiliser la déduction automatique de types de fonctions qu'avec prudence. Cela suppose que l'appelant a une compréhension fine de règles du jeu.
Pour une fonction dont le type de retour est déduit, tous les return doivent exprimer le même type. Les règles de std::common_type<T>::type ne sont pas appliquées; il faut vraiment le même type partout.
Fin du contenu technique
Petite pause. Déplacement d'une salle à l'autre. La salle principale où ont lieu les Keynotes (les grandes conférences pour tous les gens présents) se nomme Fermat (pour Pierre de Fermat, le mathématicien juriste bien connu, en particulier pour son théorème impossible pour , sur lequel les gens se sont cassés les dents pendant 500 ans, un contemporain de Descartes). Pour la prochaine, pour moi, ce sera la salle Newton. En après-midi, je serai principalement dans la salle Pascal.
Truc amusant : ici, je suis un buveur de café dans la moyenne.
J'ai trouvé une chaise pas trop loin d'une source de courant, bonne chose étant donné l'autonomie pour le moins limitée de mon ordinateur portatif.
Rendons hommage à la qualité de l'offre de contenu: au moins trois des présentations offertes à 11 h m'intéressent directement. J'ai dû faire un choix difficile.
Pour le voir en ligne : https://www.youtube.com/watch?v=JhMlWj4tCDo
Pour la présentation de 11 h, pour moi, ce sera un truc d'Alon Zakai (de Mozilla) sur le projet Emscripten et la norme asm.js, qui permet de transformer du code C++ en code JavaScript pour utiliser JavaScript en tant que «machine abstraite» pour disséminer les programmes C++ à travers le Web.
Début du contenu technique
Je connais le principe, soit compiler de C++ (avec le compilateur Emscripten) vers un sous-ensemble restreint de JavaScript (asm.js) qui ressemble beaucoup à une sorte de code machine, puis pousser le texte résultant vers les machines distantes où il sera exécuté en tant que JavaScript, ou même (potentiellement) « retraduit » en code natif pour être exécuté à pleine vitesse. Je n'ai par contre pas joué avec encore alors je suis curieux.
Évidemment, tous les fureteurs importants commercialement sont écrits en C++ (c'est des bêtes et faut que ça roule). L'idée ici est de permettre de « populer » le Web de code C++.
Alon Zakai fait remarquer que le Web est hautement standardisé, ce qui explique que JavaScript soit un choix de qualité à titre de format intermédiaire (pour fins de portabilité).
Il montre une diapo mettant de l'avant à quel point le système de types de JavaScript est loin de celui de C++ (« addition » de texte et d'un nombre, opération eval(), liste de types disjoints, etc.)
Ceci :
em++ x.cpp -o a.html
donne une exécution « console » dans un fureteur. Chouette!
Emscripten est basé LLVM (un superbe projet de compilateur s'il en est un), comme l'est entre autres Clang. Ça leur permet d'avoir « gratuitement » l'optimisation de LLVM et tout le tralala
Les parties de SDL et de OpenGL d'Emscripten sont distinctes de ce qu'on trouve habituellement, étant construites sur des API Web (p. ex. : OpenGL devient WebGL).
Intéressant : ceci, en C++...
int f(int *p)
{
int r = *p;
return calc(r, r << 16);
}
...devient, en JavaScript respectant asm.js, cela...
function f(p) {
var r = HEAP32[p >> 2];
return calc(r, r << 16);
}
...ce qui n'est pas si mal, mais faut que les pointeurs soient correctement alignés en mémoire pour que ça fonctionne.
Ils génèrent des « mémoires » allouées dynamiquement pour remplacer des tableaux locaux, sur lesquelles ils appliquent des « vues » pour traiter le sous-ensemble approprié comme une entité de la bonne taille et du bon type. Certaines manoeuvres sont faites pour assurer la correspondance (JavaScript utilise des nombres 64 bits mais certaines expressions sont 32 bits, alors il y a des manipulations de bits ici et là pour fins de conformité). Ceci « type » les opérations plutôt que les données, un concept intéressant.
Les manoeuvres particulières d'Emscripten sont encadrées par asm.js, un formalisme restrictif qui devient un style de codage mécanique.
Passer par asm.js élimine tout Undefined BeUndefined Behaviorhavior, car JavaScript n'a pas cette particularité de C et de C++.
Évidemment, les modèles de sécurité de JavaScript et de C++ diffèrent, dû au Sandbox des programmes JavaScript qui leur cache la machine. Entre autres, une version asm.js de libc++ doit être posté avec le code, et les accès au système de fichier doivent être simulés.
C++ a plusieurs types numériques, JavaScript n'a que double. Conséquence par contre, on doit s'en tenir à des programmes 32 bits.
Le code JavaScript généré a des caractéristiques de performance variables (collecte d'ordures, découverte dynamique des « types », profilage intégré, etc.)
Le code JavaScript est toutefois beaucoup plus rapide à l'exécution qu'auparavant. Vive la compétition!
Exemple amusant :
function add(x,y) {
x = x | 0; // x « devient » int32
y = y | 0; // idem
return (x + y) | 0; // re-idem... Le moteur JavaScript peut faire des additions d'entiers!
}
asm.js utilise ces techniques pour encoder statiquement les types dans les opérations! Ils forcent le langage dans environ un quart de ce qu'il peut faire, soit le quart qui est essentiellement statiquement typé et fortement « optimisable ».
Quelques benchmarks sont présentés. Ils s'en tirent pas si mal, étant typiquement entre 1,5 et 2 fois plus lents que le code natif (donc 50%-67% de la vitesse du code natif). Dans le contexte, c'est fameux.
Évidemment, le créneau clé de ce projet est le monde du jeu! Les deux plus gros utilisateurs sont Unreal et Unity
Alon Zakai a roulé devant nous deux démos 3D qui ont essentiellement l'air de code natif... dans son fureteur (sérieusement, fallait le savoir car c'était «seamless»), incluant le son et les shaders!
Intéressant : ils utilisent CSmith pour faire de l'analyse statique sur leur code.
Alon Zakai discute brièvement de l'intérêt d'avoir un seul Build général plutôt qu'un Build par machine, un avantage de pouvoir compiler une seule fois pour tous les ordinateurs possibles.
Mention des exceptions. Emscripten les supporte, mais pas gratuitement car les blocs catch de C++ sont typés, alors que JavaScript n'a pas de types. Pour cette raison, ils doivent enrichir les types levés par des identifiants (entiers) et créer manuellement une cascade de blocs catch selon le « type ». Ouch!
Ils distinguent invoke(ptr) pour l'appel, can_handle(ptr,type) pour un catch possible, et do_throw(ptr) pour le throw lui-même (un rappel, je présume). Ça génère une cascade de fonctions pour un try avec plusieurs catch, donc faut y penser quand on écrit le code étant donné l'impact sur la vitesse d'exécution. Faut aussi réfléchir pour RAII, question de s'assurer que les finalisations se fassent dans le bon ordre. C'est le même principe.
Alon Zakai mentionne que c'est amusant d'écrire un compilateur qui utilise JavaScript comme destination
Question : pour quel type d'application Emscripten optimise-t-il?
Réponse : le cas d'utilisation le plus clair est le monde du jeu, mais ils essaient de viser plus large.
(un membre de l'audience mentionne que ce qui pénalise le plus côté vitesse est la copie de données entre C++ et JavaScript, car les modèles mémoire sont bien différents; c'est ce qui fait le plus mal)
Question : et les threads de C++, ça donne quoi avec Emscripten?
Réponse : il y a des Webworkers, mais typiquement on compilera du code monoprogrammé pour le moment. Il existe une version multiprogrammée mais c'est un projet de recherche
Question : interopérer de C++ avec d'autre code JavaScript?
Réponse : ils en parleront dans une autre présentation.
Question : et qu'en est-il du délai entre la réception par un fureteur du code JavaScript à son lancement?
Réponse : en général, les benchmarks présentés incluent ce temps! En pratique, ça varie selon les plateformes, donc selon les moteurs, et plusieurs fureteurs parviennent à faire de la compilation JIT en parallèle.
Question : et le débogage? (la salle rit, moi aussi)
Réponse : réponse en deux temps. On offre plusieurs options de compilation pour aider, incluant des vérificateurs de l'alignement convenable en mémoire; les fureteurs ont aussi des débogueurs JavaScript, et nous offrons une option SourceMap pour que le code C++ d'origine soit visible dans les commentaires du code asm.js généré.
Question : y a-t-il des optimisations en JavaScript pour éliminer des tests de frontières dans les tableaux quand le code généré est certifié correct?
Réponse : on a des trucs pour tricher sur des machines 64 bits, en faisant sembler d'avoir alloué la mémoire entière, mais JavaScript est safe et teste les accès en général. Cependant, les optimiseurs JavaScript sont bons.
Question : et qu'en est-il du inlining?
Réponse : l'optimiseur de LLVM fait du inlining, alors les fonctions générées sont parfois grandes, mais on a les bénéfices qui viennent avec, même en JavaScript)
Question : pourquoi allouer 32768 bytes en JavaScript pour parcourir un tableau de 20000 bytes en C++?
Réponse : c'est pas nécessaire mais le code généré est un peu plus rapide sur les tailles qui sont une puissance de deux)
Question : avez-vous pensé prendre les AST de LLVM pour générer des entités plus idiomatiques de JavaScript plutôt que passer par une « machine » proche des idiomes C++?
Réponse : deux trucs. De un, on a essayé, mais les tableaux typés sont plus rapides et on réduit la collecte d'ordures. Aussi, les implémentations JavaScript ont des comportements qui peuvent varier d'un compilateur à l'autre. Il existe un autre compilateur, http://leaningtech.com/cheerp/, qui suit l'approche que vous suggérez, si vous êtes curieux)
Question : merci. On utilise Emscripten dans mon entreprise et nous sommes très impressionnés (quelques échanges sur les options de compilation s'ensuivent).
Question : l'optimisation d'asm.js n'est présentement fonctionnelle que si le tas est de taille fixe. Pense-t-on changer ceci?
Réponse : oui, mais c'est pas encore rodé.
Question : quelles sont les meilleures pratiques pour que la gestion d'exceptions en JavaScript soit rapide?
Réponse : ne pas les utiliser! Plus sérieusement, c'est un truc sur lequel on travaille, et sur lequel les fureteurs travaillent aussi. On parle d'un horizon de mois, pas d'années.
Question : y a-t-il des recommandations pour le traitement d'entiers signés ou non-signés avec asm.js?
Réponse : on peut le faire, ce n'est pas un problème.
Fin du contenu technique
Il faut arrêter à midi, mais il y avait encore une file pour les questions. Très intéressant. Ses diapos sont déjà en ligne sur http://kripken.github.io/mloc_emscripten_talk/cppcon.html#/
J'ai dîné sur la terrasse, solo ce midi, pour lire mes courriels et mettre en ligne le récit de la première journée de mon voyage. Des gens à côté discutaient de la présentation de Pablo Halpern sur le parallélisme et la concurrence (j'aurais aimé celle-là aussi). Analogie intéressante avec le sport: la course à pied est du parallélisme, le basketball de la concurrence. J'aime!
Les gens sont encore en état de choc suite à la présentation de Scott Meyers en début de journée. Je les comprends, même si c'est un choc qui est derrière moi depuis longtemps. Ça dépend, je pense, du profil des gens. Les plus applicatifs ne « frappent » pas nécessairement ces détails en pratique, alors que les gens plus près des fondamentales, qui doivent les expliquer, les ont en plein visage tout le temps.
Ça réagit aussi beaucoup aux variations d'opinion sur des questions de fond. Par exemple, Herb Sutter recommande « auto partout » alors que Scott Meyers a mis de l'avant des bémols importants ce matin. Pour ce type de conflit philosophique, ma présence ici est particulièrement utile.
Pour le voir en ligne : https://www.youtube.com/watch?v=c1gO9aB9nbs et https://www.youtube.com/watch?v=CmxkPChOcvw
J'ai pris quelques minutes pour recharger la pile de mon ordinateur portatif et classer quelques signets, puis me voilà dans la salle Pascal pour la première partie d'une présentation en deux temps par Herb Sutter sur la programmation à l'aide d'atomiques (sous-titrée « jongler avec des lames de rasoir », ce qui convient tout à fait).
Je manipule les atomiques, j'explique aux étudiant(e)s comment faire de même, mais on parle ici d'un truc pour lequel il est difficile de trouver les bons mots, d'un sujet très ardu à comprendre pour les étudiants. Herb Sutter est un bon pédagogue alors je compte porter attention à la méthode. Je vais aussi essayer d'aller Voir Jeff Preshing, plus tard cette semaine, car ses articles sur ce sujet sont excellents.
Petite jasette avec un sympathique gars du nom d'Ernie Ewert, autre spécialiste de la question. On blague sur le fait que sa belle-soeur était une cadre pour la compagnie Target, qui a eu des ennuis au Canada. It's you French people's fault (en riant, ne vous en faites pas). Chic type. On a pris un peu de temps pour échanger sur le fait qu'on sait tous deux pas mal tout sur le sujet, mais qu'on cherche des idées pour expliquer, et des surprises (ce qui serait génial). C'est sans doute le meilleur endroit où être aujourd'hui pour ça.
Herb Sutter joue du piano en attendant que ça commence. La salle est pas mal pleine (grande salle). Je suis ici pour plusieurs heures (Herb Sutter deux fois, puis Chandler Carruth qui est toujours excellent dans ces occasions) alors je suis bien content d'être près d'une prise de courant.
Michael Wong s'est assis par terre, faute de place. Je lui ai offert un « spot » pas loin, mais il est pris comme moi et a besoin d'une prise de courant.
Début du contenu technique
Herb Sutter fait un parallèle entre le stress de jouer du piano en public et manipuler des atomiques.
Semble qu'il travaille sur des pointeurs intelligents atomiques, mais ils ne sont pas prêts alors on n'en aura qu'un survol (a posteriori, c'est un des éléments clés de la présentation)
Petite diapo sur « pourquoi écrire du code sans verrous » (éviter les courses, les deadlocks, les problèmes de composition de verrous, etc.). Il parle de (b)locking, une belle tournure, avec un bel exemple utilisant deux lock_guard<mutex> d'une manière risquant un deadlock
Équilibre entre plus de verrous/plus fine granularité/plus de risques et moins de verrous/de risques/d'échelonnabilité.
On suppose que les gens mesurent avant, et après le recours au code sans verrous.
Il suggère qu'on pense aux verrous en tant que feux de circulation, et aux intersections comme une ressource partagée. Le code sans verrous est plus comme une intersection ressemblant à un trèfle à quatre feuilles (plusieurs croisements à hauteurs différentes, fusions en ralentissant sans bloquer).
Outils intellectuels clés : penser « transactionnel », au sens de ACID (atomicity, consistency, isolation, durability), et atomic<T>
Pour ACID, il insiste sur ce qui suit :
Ses exemples pour la présentation :
Il fait ensuite un survol d'atomic<T> et des opérations Compare-and-Swap (CAS) que je connais bien.
Herb Sutter note qu'un atomic<T> et un T peuvent être structurés différemment en mémoire (surtout si T n'est pas primitif, je suppose, du fait que dans un tel cas il est possible que l'implémentation du type atomic<T> doive reposer sur un mutex).
Il faut toujours initialiser une atomique à la déclaration pour éviter les surprises, sinon, pour une atomique primitive, on a un comportement analogue à celui qu'on a pour un primitif non-initialisé.
(J'escamote les trucs évident pour moi, p. ex. : entre deux accès à une atomique, ses états peuvent changer dû à d'autres threads).
Herb Sutter met de l'avant les trois grands catégories d'algos sans verrous :
Avec C++ 11, le Double-Checked Locking n'est plus « brisé ». On peut par exemple instancier dynamiquement un singleton X à travers un pointeur si on utilise un atomic<X*> plutôt qu'un X* (les raisons pour lesquelles ça fonctionne avec un pointeur atomique n'ont pas encore convaincu tout le monde dans la salle, c'est amusant).
Stephan T. Lavavej passe une remarque qu'en utilisant deux lectures sur le même pointeur atomique dans son exemple, le code n'est pas optimal; Herb Sutter lui dit que cette optimisation devrait être faite par son compilateur, et le mandate de s'en occuper. On rit. Plus sérieusement, il se trouve que sa diapositive suivante couvre ce cas.
S'ensuit un bref échange sur memory_order_consume... Herb Sutter indique qu'il faut éviter consume pour le moment, qui n'est pas correctement spécifié dans le standard, et se limiter à memory_order_acquire. Jeff Preshing écrit, à ma connaissance, une proposition formelle pour régler ce problème.
Herb Sutter fait remarquer que call_once() (idée de Google, apparemment) est meilleur encore que le Double-Checked Locking. Plus simple, plus rapide.
Il ajoute qu'il y a mieux encore : une variable statique locale à la fonction génératrice du singleton, plutôt qu'un appel à new (l'une des approches que j'enseigne, idée de Scott Meyers d'ailleurs).
Il y a plusieurs questions, mais Herb Sutter suggère qu'on évite les détails pour aller plus loin. Juste après que certains aient posé des questions sur des petits détails mineurs.
Il est essentiel que la synchronisation soit cohérente dans l'ensemble d'un programme. Elle peut ne pas être la même partout, cela dit : on peut passer de mutex à atomique à mutex, mais faut que ce soit une approche holiste.
Il présente son exemple producteur / consommateur avec verrous, variables conditionnelles et une sentinelle de fin d'alimentation dans le code du producteur. Sur le plan pédagogique, Herb Sutter trace des lignes sur les frontières de non-réordonnancement. Il y en a partout dans cet exemple!
Dans le cas du consommateur, on itère tant que le conteneur est vide avec suspension sur la variable conditionnelle, puis on consomme jusqu'à atteinte de la sentinelle. La position du lock_guard est importante ici : sa portée couvre le processus complet d'obtention; le traitement se trouve hors de cette section. Les lignes de non-réordonnancement sont visibles aussi, mais il y en a moins. Les lignes de non-réordonnancement correspondent aux points où les invariants doivent tenir (intéressant).
Illustration du même code avec une combinaison de verrous et d'atomiques. Le mutex se limite au point où on insère un élément dans la liste de trucs à traiter (pour synchroniser un groupe d'opération). La tête est un pointeur atomique, donc la modifier publie de l'information au système. Il y a une relation acquire-release (if (head != null) pour acquire, et head = ... pour release).
Pour la solution sans verrous, Herb Sutter suggère de voir les accès comme un automate déterministe à états fini (start→[boucle: empty→task→empty]→done, avec métaphore de boîtes postales). Il utilise des sémaphores à titre de signal (you got mail!). Les lignes de non-réordonnancement sont alors les points de lecture et d'écriture des variables atomiques.
La première étape est Lock-Free, parce que le producteur se trouve parfois à attendre (en fait, Wait-Free si t'as moins de workers qu'un certain seuil). La deuxième n'est qu'Obstruction-Free.
Le code du consommateur va de soi, selon le même modèle. Le consommateur copie la donnée dans la boîte et écrit atomiquement un nullptr à la place pour que le producteur s'en aperçoive. Le travail doit être fait après l'écriture de nullptr pour permettre au producteur de continuer à alimenter le consommateur.
Question : ceci risque-t-il de provoquer un échange entre débit et latence, en alimentant le consommateur pendant qu'il ne peut pas consommer?
Réponse : c'est l'un des points subtils de la présentation! Si le producteur est plus lent que le consommateur, il peut être préférable d'intervertir le signal de consommation et le code de traitement. Écrire nullptr signifie « je peux en prendre encore », après tout.
Stephan T. Lavavej : un état supplémentaire, genre « j'ai mon courrier mais je suis occupé », serait-il utile?
Herb Sutter : si on fait ça, notre atomique devient plus grosse (un pointeur + un enum), ou se transforme en une file d'attente.
Pour la liste simplement chaînée, il suggère d'essayer d'écrire une slist<T> qui offre construction, destruction, find(T) et push_front(T) sans utiliser de verrous (un truc qu'on fait dans mon cours de parallélisme l'été... C'est dur!).
Brève pause café, il est 15h, heure de Seattle; Herb Sutter continue à jouer du piano entre-temps... Les toilettes sont pleines au quatrième étage, où je suis, alors je suis allé au sous-sol où c'était plus humain. J'ai croisé Chandler Carruth sur le chemin du retour, qui peaufinait sa présentation, complètement dans sa bulle, mais souriant.
Premier jet : la tête est un atomic<Node*>, les noeuds n'ont rien d'atomique, find(T) retourne un T*. slist<T> est Incopiable, évidemment.
Il utilise l'initialisation à nullptr de la tête pour déclarer slist<T>::slist()=default. Intéressant. Faut que j'y réfléchisse.
Dans le destructeur, il accède la tête une seule fois (méthode load() de la tête) pour fins de vitesse, car accéder une atomique fait mal. Pas de synchro (pas de course à la destruction). Recommandation : ne pas s'occuper de courses dans le constructeur par défaut ou dans le destructeur. S'il y a course, c'est un bogue dans le code appelant.
La méthode find(T val) est correcte si on commence par auto p=tete.load(); suivi d'une boucle, et si on retourne nullptr si on ne trouve pas.
La méthode push_front(T) est illustré par un schéma. Ça met en relief le moment où on fait un CAS sur la tête. Sa version initiale met en relief que sans compare-exhange, il y a une course. Des schémas montrent les risques.
Herb Sutter a fait une blague sur les canadiens... dont il fait partie, tout comme Michael Wong et moi-même. Bon, bon.
La version avec CAS est correcte jusqu'ici. On n'a par contre pas de service pour retirer un élément. Herb Sutter mentionne au passage que le tete.compare_exchange_weak(p->next,p), qui met à jour p->next, est dû à Lawrence Crowl.
En ajoutant la suppression (méthode pop_front()), sabs surprises, ça devient compliqué. Il montre la situation avec un schéma encore une fois.
Dans le code, la première version contient une course entre deux accès à la tete. Peter Sommerlad signale une course entre push_front() et pop_front(). Enfin, find() retourne un T* qui peut être détruit de manière asynchrone par le pop_front()!
Même avec un simple CAS, on a un risque de course (et de ABA).
Herb Sutter présente une chic illustration où un thread fait p=tete;temp=p->next; puis où deux pop_front() ont lieu, suivis d'un push_front() provoquant un ABA sur tete->val qui laisse temp incorrect à l'insu de p.
Il existe quelques solutions pour le problème d'ABA :
Il existe aussi quelques solutions pour le problème du Delete-while-Traversing :
Solution chouette avec C++ 11 : combiner comptage de références et passage par référence :
On n'a pas atomic<shared_ptr<Node>> avec la sémantique appropriée pour le moment, mais il y a moyen de l'écrire manuellement (un atomic_shared_ptr<T>). Herb Sutter compter proposer atomic<unique_ptr<T>> et atomic<weak_ptr<T>> pour novembre en vue de C++ 17)
Herb Sutter parle de « linéarisabilité », la propriété selon laquelle des fonctions peuvent se chevaucher mais on obtient malgré tout la même sémantique que dans un programme séquentiel. C'est beau : ajout de concurrence, droit au chevauchement, sémantique de code séquentiel! Ce qui est concurrent sans être « linéarisable » est difficile à prouver correct.
Pour illustrer le bon fonctionnement, Herb Sutter présente deux appels à pop_front() côte à côte. C'est intéressant pour fins de visualisation. Pas de delete, pas d'ABA.
Idem pour push_front() et pop_front() côte à côte. La seule compétition est sur compare_exchange_weak(), donc Ok.
Pour find() et pop_front(), le recours à des shared_ptr en fin de compte règle le problème. L'illustration visuelle montre que même si un find() pointe sur un noeud et qu'un pop_front() est appelé pour l'enlever de la liste, le noeud est encore vivant tant qu'il a un client.
Aujourd'hui, il faut écrire :
shared_ptr<T> a;
auto p = atomic_load(&a);
atomic_compare_echange_weak(&a,&e,d);
On veut en arriver à écrire :
atomic<shared_ptr<T>> a;
auto p = a.load();
a.compare_echange_weak(e,d);
...qui est plus simple, moins susceptible d'être cassé accidentellement et peut être plus rapide. Herb Sutter suggère d'écrire une classe maison en attendant.
On ne sait pas si on peut écrire atomic<shared_ptr<T>> sans verrous en pratique; c'est une question ouverte, mais Herb Sutter pense que même avec un mutex, ça vaut la peine. On espère y arriver.
Il y a des débats d'experts et de la compétition sur la qualité des implémentations dans la salle... Les gens sont tendus!
On a réussi à se rendre aux diapos bonus pour 15 h 58! Ainsi, Herb Sutter :
Sa technique de base :
Il suggère qu'un std::mutex est souvent un Spin Lock avant de basculer vers un outil système (truc moche : une gars de gcc dans la salle essaie de le couper sans arrêt).
Il offre un service consommateur, bool consume(T&) qui cherche while(consLock.exchange(true)) et finit par consLock=false; pour s'assurer l'exclusivité. En étant le seul consommateur, il est seul de son côté du divider
Le producteur fait quelque chose de semblable. Il est responsable de nettoyer de qui traîne de son côté.
Il y a un seul responsable pour « bouger » divider : le consommateur
On veut un fort débit, une forte échelonnabilité (multi producteur + multi consommateur)
Les risques sont la contention et la sur-souscription (plus de threads que de coeurs)
Il illustre ses mesures par une matrice nombre de consommateurs nombre producteurs, avec des bulles à chaque indice . La grosseur de la bulle est proportionnelle au débit. Son type T pour les tests est un objet petit (tapon d'une dizaine de string) ou gros (tapon d'une centaine de string) selon le cas. L'échelonnabilité n'est pas terrible (le meilleur score est vers trois threads).
Question : combien de coeurs pour les tests?
Réponse : 24 (son schéma va de 1 à 25 dans chaque axe). On voit une belle diagonale qui montre que quand on a plus de threads que de coeurs au total (quand , on devient moins efficaces).
Il obtient de meilleurs résultats... avec plus d'allocation dynamique! Réactions dans la salle (je comprends!); ça coûte quelque chose localement, mais ça accroît le débit systémique parce que ça permet de sortir la copie des données de la section critique. L'impact plus grand sur le gros T que sur le petit.
Le consume() doit détruire le noeud une fois la valeur collectée. Il n'y a pas de changement dans la signature, qui demeure bool consume(T&).
On voit de gros gains quand on accroît le nombre de threads : les grosses bulles se tassent vers la droite.
J'ai l'impression que la copie hors-section critique provoque une course dans son exemple, mais c'est à vérifier; faut que je révise la gestion des pointeurs dans son exemple quand j'aurai eu accès aux diapos.
Amélioration: en laissant chaque consommateur retirer les noeuds qu'il a consumés, on peut supprimer le recours à divider et alléger encore la longueur/ le poids de la section critique. Herb Sutter rappelle que si on optimise pour 24 threads et plus, ça ne donne pas grand-chose si on a quatre coeurs et si ça nous ralentit dans un tel cas. Faut choisir ses combats.
Ça donne encore de bons résultats. On gagne encore un peu.
Enfin, il suggère de garder les données qui ne sont pas utilisées ensemble distantes l'une de l'autre, avec alignas sur le noeud pour qu'il y en ait un seul par Cache Line. Juste ça, ça rapporte beaucoup, même sur de petits noeuds et même en situation de « sursouscription ».
D'autres graphes montrent qu'on a le maximum avec à peu près deux producteurs pour un consommateur. Il y a une discontinuité frappante quand on passe à plus de threads que de coeurs : le code devient moins prévisible.
Beaucoup de contenu pour mon cours de parallélisme dans cette présentation!
L'alignement réduit le faux-partage. L'allocation dynamique (ici, pas toujours!) allège la section critique et permet de gagner sur le plan systémique.
Fin du contenu technique
Petite pause vers 16 h 30, heure de Seattle. Les gens autour de moi admettent avoir trouvé ça intéressant mais ne pas avoir compris grand-chose. Je sympathise; comprendre ces trucs, c'est un boulot à temps complet. Je me suis tapé le boulot à cause de mes cours, mais c'est vraiment un territoire très spécialisé.
Pour le voir en ligne : https://www.youtube.com/watch?v=RrAgaQCfr5M
Chandler Carruth va parler de « comment rendre le code rapide ». Ce sera une introduction, et il demande aux gens sur place d'être gentils.
Début du contenu technique
Chandler Carruth recommande de se demander pourquoi on se préoccupe de vitesse avec C++. Il cite Niklaus Wirth : Software is getting slower more rapidly than hardware is getting faster (A Plea for Lean Software).
Chandler Carruth recommande qu'on se demande pourquoi on veut que notre code roule vite. Ça mérite qu'on y pense. Il recommande de lire le livre de Wirth sur les structures de données, toujours d'actualité.
Il mentionne l'avènement du iPhone, et le fait que la mobilité est maintenant un citoyen de première classe du monde de l'informatique.
Il mentionne aussi l'avènement du problème de la pile de ces appareils (it's not a feature, it's a bug!).
La recherche examine les « instructions peu coûteuses ». Chandler Carruth dit que c'est bidon. La meilleure solution : finir au plus vite d'exécuter le programme. Le plus vite c'est fini, le plus vite on dort, le moins de pile on consomme. Le off est la meilleure solution.
Autre aspect : l'électricité est une ressource finie, et doit être économisée aussi dans les très gros centres. C'est important dans le très petit et dans le très grand, surtout.
Le rapport Compute/ Watt est ce qui domine.
La vitesse brute n'est pas tout.
Il mentionne que Java est parfois plus rapide que C++ (James Gosling lui a déjà remis au visage), par exemple quand on a une application spécifique avec un moteur de collecte d'ordures très spécialisé précisément pour ce cas.
C++ ne donne pas de vitesse « magiquement ». Ce langage donne du contrôle sur la vitesse.
On va donc parler de :
Efficiency : la quantité de travail que doit accomplir une tâche. En gros, on veut faire le moins possible pour les mêmes résultats (efficience)
Performance : à quel point le programme fait son travail (faire plus dans le même intervalle de temps... ça évoque le débit dans un réseau)
On peut être efficient, si on a le meilleur algo possible pour une tâche donnée; on ne peut pas être performant, car on peut toujours aller plus vite. Chandler Carruth fait remarquer que Performant n'est pas un mot du dictionnaire anglais!
On peut par contre rendre un programme plus « performant ». Évidemment :
Image : on veut solliciter la totalité des transistors de la machine à notre bénéfice. Chandler Carruth montre une puce à plusieurs coeurs, et indique qu'avec une Heat Map on peut voir qu'on la sous-utilise la plupart du temps
Ne pas utiliser un circuit gaspille de l'énergie! Il faut évidemment l'utiliser à bon escient.
Algorithmes, les trucs :
Exemple : chercher une sous-chaîne dans une chaîne :
Autre exemple : une fonction qui ajoute n instances de X à la fin d'un vector<X> avec push_back() (ajout de reserve()(), remplacement de push_back() par emplace_back() – il n'a pas fait ça, mais ça va de soi)
Autre exemple : plusieurs accès à une unordered_map par une clé, chaque fois avec []. Ouf. Évidemment, une temporaire permet de faire mieux en évitant le recalcul des hash codes.
(c'est pas de la micro-optimisation de faire moins de travail; c'est de l'hygiène!)
Suit : une critique frontale de Chandler Carruth sur l'interface de unordered_map.
Note : le return value optimization (RVO) et le copy elision sont possibles quand il y a un seul return dans une fonction et que la variable retournée est la première déclarée dans la fonction... Retournez par valeur, et faites des fonctions courtes! (Ouf, c'est ce que je dis à mes ouailles!)
Assurez-vous que le design de vos API mène le code client à faire moins de travail!
Structures de données : évitez les structures discontinues. Dites non aux listes chaînées. Le processeur vous dira merci.
La mémoire est trop lente pour nos processeurs, alors on crée une hiérarchie de la mémoire (on peut faire 36 opérations par cycle si on a coeurs + sockets, et ça prend 100 cycles pour aller chercher un byte en mémoire vive... Ugh!).
Il explique l'horreur des listes chaînées de noeuds alloués séparément sur un ordinateur contemporain)
Une std::list est Ok si on la traverse TRÈS rarement et si on la met à jour TRÈS souvent. C'est son cas d'utilisation.
Et std::stack? std::queue? std::map? Bof. Utilisez std::vector, sauf si vous mettez à jour au début sans arrêt (une std::queue se simule bien avec un std::vector en n'enlevant pas les éléments au début!)
Suit : une critique sentie de std::deque; Chandler Carruth dit ne pas avoir rencontré une implémentation satisfaisante.
Utiliser std::map ralentit le programme. C'est pire que std::list. Ouch. Radical. La salle est un peu déstabilisée. Pourquoi? C'est une « liste » (un arbre), avec tous les désavantages que cela comporte. Faut la traverser, pour ajouter ou fouiller. Faut la traverser pour balancer l'arbre. Non, ne faites pas ça!
Et std::unordered_map? Pas mieux : les buckets sont... des listes chaînées! Et c'est _très_ difficile de l'adapter pour qu'elle se comporte autrement, dû à son interface. Fouiller dans un unordered_map va pratiquement toujours générer des Cache Miss.
Une bonne hash table serait une table de paires nom-valeur avec Local Probing (pas de buckets) lors de collision pour optimiser l'accès à la Cache. Et faut que la clé et la valeur soient petites.
On est à la fin : this is all a lie. Cher Chandler Carruth
On ne peut pas vraiment traiter algorithmes et structures de données séparément pour tirer le maximum. Il faut que les deux oeuvrent de concert (il réfère à Niklaus Wirth encore une fois).
Penser algorithmes d'un côté et structures de données de l'autre est un point de départ.
Il rappelle que le tri à bulles est le tri le plus rapide... pour un petit nombre d'éléments! Il parle aussi du Cuckoo Hashing : quand t'as une collision, utilise un autre algo de hashage (un backup hashing!) et essaie à nouveau... Le pire, c'est que ça a de bonnes propriétés algorithmiques! Mais c'est Locality-Hostile, et t'as toujours des Cache-Miss (note personnelle : c'est un Cache-Mess!)
À la fin, un bref recap. C'était très bon, comme d'habitude avec lui.
Question : peut-on avoir vector avec entreposage In-Place dans le standard?
Chandler Carruth : dans C++ 98, c'était conforme de travailler ainsi, un peu comme SSO pour les string, mais ce fut brisé avec C++ 11 dû au constructeur de mouvement, qui doit ne pas invalider des itérateurs lors d'un mouvement... Stephan T. Lavavej indique que le vrai problème est swap()
Question : y a-t-il une version Google de std::map?
Chandler Carruth : on a une sorte de hash map, elle est très rapide mais l'API n'est pas optimale
Question : et un allocateur maison pour std::map?
Chandler Carruth : c'est très difficile à faire, et il n'est pas clair que ce serait mieux
Question : donc on utilisera tous std::vector... Que penses-tu de la version proposée par Facebook?
Chandler Carruth : oui, j'ai regardé. Il y a des trucs qui ne me conviennent pas, en particulier qu'elle entraîne plusieurs changements sémantiques pour une amélioration pointue
Question de Scott Meyers : as-tu des suggestions sur ce que la STL devrait avoir comme nouvelles structures de données?
Chandler Carruth : d'abord, une bien meilleure hash table. C'est vraiment essentiel. Et avec un bien meilleur nom que unordered_map, qui est difficile à écrire. Je veux aussi une interface « de type map » mais sur un vector. Je veux aussi une liste avec pointeurs étiquetés pour grouper les allocations de manière à mieux gérer la mémoire. Et de l'allocation par Pool!
Chandler Carruth parle du problème des allocateurs standards, qui agacent le système de types, et des allocateurs polymorphiques, chouettes mais avec indirections
Question : et des structures de données persistantes? Ceci n'implique-t-il pas des listes chaînées? (il parle de structures immuables)
Chandler Carruth : elles tendent à avoir de meilleures qualités de localité, dû au fait qu'il n'y a pas de libération de noeuds, mais ça reste moins rapide
Chandler Carruth se questionne sur l'intérêt réel de structures toujours triées... Dans mon doctorat, mon type contexte l'est, mais je trie sur construction et je garde immuable par la suite, donc j'utilise vector.
Je viens de réaliser que je suis assis à côté de Bruce Dawson depuis près de 90 minutes... Amusant! Je cite ses textes dans certains de mes cours; c'est un spécialiste du débogage et de la mesure de la vitesse des programmes, et il blogue sur plusieurs sujets corsés, incluant les nombres à virgule flottante et leurs particularités. Ce sont des sujets plus complexes qu'il n'y paraît, alors je réfère à son matériel comme complément aux cours. Sympathique, bien qu'un peu timide
Échange amusant sur le point où on doit passer de recherche linéraire à recherche dichotomique. Chandler Carruth répond qu'à son avis, c'est à l'implémentation de faire ça tout seul, mais Stephan T. Lavavej signale que le standard impose des contraintes de complexité. Je pense que Chandler Carruth souhaite que les implémenteurs de la bibliothèque standard poussent pour que le standard leur donne plus de latitude dans de tels cas.
Fin du contenu technique
C'est la fin de cette (excellente) présentation. J'ai été gâté aujourd'hui. Il reste un panel vers 20 h 30 ce soir, mais j'ai un problème pressant, soit trouver... de la monnaie, car il me faut le compte exact pour prendre l'autobus, et je dois absolument faire de la lessive à mon retour ce soir, pour laquelle il me faut au moins 6 USD en pièces de 25 sous.
Mon problème: la plupart des commerces (incluant les cafés) ferment leur porte à 18 h. Il restait un Subway's d'ouvert, mais l'employé en place était du type « désolé monsieur, je ne peux pas monsieur »... L'idéal aurait été les banques, mais elles fermaient tôt aussi. J'ai fini par m'en sortir avec un Starbucks encore ouvert, près du terminal d'autobus, où j'ai acheté un café en demandant à la gentille dame de me donner en échange le maximum de 25 sous... J'ai eu un rouleau :) Je vais en avoir besoin encore vers la fin de la semaine, mais je pense être Ok jusqu'à demain soir.
Je n'ai pas le temps de retourner à l'hôtel; j'aurais bien aimé me laver et commencer ma lessive, mais ça me ramènerait trop tard pour le panel. Je vais travailler un peu sur mes propres trucs en attendant.
Je viens de voir passer cette photo de Stefanus Du Toit, qui est aussi l'auteur actuel du document officiel du standard C++ 14... Il était à côté de moi et m'a pris en gros plan, et je ne m'en suis même pas aperçu, c'est bien pour dire: https://twitter.com/stefanusdutoit/status/509130923153952768/photo/1
Pour les voir en ligne : https://www.youtube.com/watch?v=4cCeUfzH-No
La séance de 20 h 30 est un panel avec quelques-uns des auteurs les plus connus dans le monde de C++, soit John Lakos, Herb Sutter, Ade Miller, Alex Allain, Kate Gregory, Michael Wong, Peter Sommerlad et Scott Meyers.
Sutter joue encore du piano quand nous entrons dans la salle. C'est chouette.
Le panel est modéré par Chandler Carruth. Il insiste pour que les gens utilisent le micro, pour éviter que tous les auteurs répètent les questions :)
Les auteurs se présentent un par un. Quelques passages choisis:
Herb Sutter demande quel est le livre le plus important sur C++. Il propose lui-même A Tour of C++, le plus récent (et bref!) volume sur C++ par Bjarne Stroustrup. Il dit que c'est brillant d'avoir produit ceci en 180 pages à peine. C'est intéressant comme point de vue.
Question : comment vous-y prenez-vous pour débloquer quand vous avez de la difficulté à écrire?
Kate Gregory : je commence toujours une nouvelle section avant d'arrêter. C'est plus difficile de repartir quand on n'a pas commencé encore
Alex Allain : mon truc est aussi de ne jamais m'arrêter à un endroit «raisonnable»
Ade Miller : je n'aime pas écrire mais j'aime expliquer mon code. Ça me permet de me distraire quand je bloque quelque part
Michael Wong : quand je bloque, je parle à des gens et ça finit par me redémarrer
John Lakos : je suis très lent, mais je travaille chaque jour, jusqu'à très tard. Malheureusement, après un certain nombre d'années, le matériel vieillit et il faut recommencer, donc on ne finit jamais
Peter Sommerlad : j'écris avec des co-auteurs, ce qui aide. Il ne faut pas avoir peur de réécrire si vous êtes préoccupé par la qualité... Et si vous ne ressentez pas le besoin de devenir riches!
Bjarne Stroustrup : quand je lis un livre et j'entends la voix de l'auteur, incluant son accent, j'ai le sentiment que c'est réussi. Donner des présentations publiques sur le sujet aide. Aussi, il me faut toujours deux jours pour écrire quelque chose: un mauvais jour et un bon jour
Question : va-t-on un jour atteindre le point où on n'aurait plus de séparation entre .h et .cpp?
Chandler Carruth : Header Files must Die
Herb Sutter : Chandler Carruth et Gabriel Dos Reis travaillent sur les modules, et on en a besoin pour deux trucs: améliorer le temps de compilation, et isoler les macros
Peter Sommerlad ajoute Macros should die!
Herb Sutter dit que ça ne se fera que quand on pourra les remplacer
Bjarne Stroustrup indique qu'il essaie depuis vingt ans de les tuer, sans succès.
Bjarne Stroustrup : séparer l'interface de l'implémentation est une bonne chose, mais traiter l'interface plusieurs fois ne l'est pas
Question : j'utilise un livre pour enseigner la programmation aux enfants, mais c'est avec Python. J'aimerais que mon fils apprenne C++. Y arrivera-t-on?
Peter Sommerlad : je pense que C++ est un outil professionnel. Je ne donnerais pas une chainsaw à un enfant
Herb Sutter : cela se fait, plusieurs l'ont fait, même à dix ans. Le langage n'est pas la clé, mais l'important est de garder les enfants intéressés et les aider à interagir avec l'ordinateur pour faire naître la magie
Question : il y a un problème avec les paramètres «sink» (par copie, par mouvement), où on peut risquer une copie supplémentaire si on n'a pas de constructeur acceptant une référence sur un rvalue, mais si on écrit toutes les combinaisons possibles on a une explosion combinatoire de cas
Herb Sutter : ma réponse sera donnée vendredi matin
Scott Meyers : pour ton cas, la réponse est le Perfect Forwarding et les références universelles
Question : c'est beaucoup d'effort d'écrire un volume. Comment faites-vous?
Bjarne Stroustrup : relate avoir connu Aho, qui écrivait le Red Dragon Book (j'ai manqué un passage mais les gens ont ri et applaudi)
Scott Meyers : il est important d'avoir une histoire à raconter. Il faut avoir la pulsion de l'exprimer. C'est la valeur qu'a l'expression de cette idée qui rend le tout possible.
(les autres réponses sont du même ton)
Herb Sutter : le plus important est d'écrire. Souvent, souvent. Écrire. C'est la clé. Pour Scott Meyers et pour moi, c'est écrire de petits items.
Alex Allain : j'ai commencé par écrire un site Web, puis écrire de petits trucs. Écrire, écrire. Je n'aimais pas mon style, j'ai travaillé dessus.
Bjarne Stroustrup : le dernier 10% d'un projet prend 90% du temps... et c'est récursif. Ça s'applique aussi à l'écriture d'un livre (ajout personnel : idem pour une thèse de doctorat)
(j'ai manqué quelques interventions et une question)
Bjarne Stroustrup fait remarquer que tous les auteurs du panel peuvent écrire des programmes dans plusieurs langages. Il parle aussi des livres qui montrent xyz dans dix langages différents... sans jamais parler de ce qui est une bonne pratique dans ces langages!
Peter Sommerlad parle du manuel de Niklaus Wirth sur Pascal, qui a servi à titre de manuel pendant longtemps, faute de véritables livres sur le sujet.
Herb Sutter : il importe d'apprendre des autres langages que C++, mais qu'il faut les penser pour C++ de manière à ce que le design lui convienne. S'inspirer plutôt qu'emprunter.
Question : question de meilleures pratiques. Mon équipe utilise g++ 4.3 qui ne supporte pas vraiment C++ 11. Devrions-nous passer des paramètres par ref-vers-const ou par valeur?
Bjarne Stroustrup : allez à la présentation de Herb Sutter vendredi
Scott Meyers : rappelle les conseils traditionnels
Bjarne Stroustrup : aussi, envisagez une mise à jour!
Scott Meyers : considérez ce que vous ferez avec la variable avant de choisir. Si vous ne faites que la lire, ref-vers-const est Ok
Peter Sommerlad : faites confiance à votre optimiseur
Question : C++ 11 est si complexe, il y a tellement d'endroits où on risque de faire des erreurs... Qu'est-ce qui vous a fait passer d'étudiant à enseignant?
Peter Sommerlad : la meilleure manière d'apprendre et d'enseigner à autrui
Bjarne Stroustrup : il y a toujours un étudiant qui sait des trucs obscurs et recommande aux autres étudiants d'écrire « à la C ». La plupart du temps, ils ne savent pas comment indiquer les bonnes options d'optimisation à leur compilateur. Mieux vaut écrire propre et simple et bien alimenter l'optimiseur
Ade Miller : je pense que c'est la technologie qui est complexe, pas C++. Il ne faut pas avoir peur de ne pas savoir quelque chose
Kate Gregory : quand on écrit, il y a des lecteurs qui n'aiment pas ce qu'on a écrit. Faut passer par-dessus ça
Peter Sommerlad : je ne fais plus écrire de boucles manuellement aux débutants. J'enseigne avec des algorithmes maintenant.
Scott Meyers : après des années, j'écris encore souvent des imbécilités.
(quelques échanges sur les vertus des éditeurs de documents, comme FrameMaker et Word... Scott Meyers dit ne jamais avoir été satisfait; Bjarne Stroustrup souligne que l'outil est rarement le problème; Peter Sommerlad indique que FrameMaker était grandiose en comparaison avec... etc.)
Herb Sutter : un outil est bon pour quelque chose. On tend à leur demander ce qu'ils ne font pas bien. La prolifération des formats complique la situation.
Question : tout le monde se bat avec le code. Avez-vous vécu une bataille mémorable?
John Lakos : à l'époque, les gens écrivaient des fonctions de 10000 lignes. Ça existe encore... C'est le principal combat de ma vie!
Kate Gregory : en écrivant le livre sur AMP, nous faisions des tests... Parfois, ça ne fonctionnait pas et je me sentais bête... et c'était occasionnellement le Build de AMP qui posait problème!
(quelques questions sur les autres langages connus par les panelistes)
Question : de quoi parlent les auteurs quand ils se rencontrent socialement?
Chandler Carruth : c'est un panel, pas une cabale secrète!
Herb Sutter : je suis allé dîner avec Scott Meyers et Stephan T. Lavavej... et on a parlé de passage de paramètres :)
Bjarne Stroustrup : je hais le schéma de conception Visiteur. Il y a beaucoup de travail à faire pour s'en débarrasser. C'est un de mes objectifs. Je travaille là-dessus depuis le début des années '90.
(les questions continuent et c'est intéressant, mais je dois quitter vers 21 h 45, heure de Seattle, pour prendre l'autobus)
Il me reste à retourner à l'hôtel et à me lancer dans ma lessive pour être opérationnel demain matin, en espérant ne pas me lever en retard cette fois.
Début du jour 2. Je ne me ferai pas prendre ce matin, il est 6 h et je sors de la douche. Ça fait une nuit de 3,5 heures, alors je suis cerné, mais je serai aussi moins cerné.
Quelques trucs généraux :
Arrivé au centre de congrès un peu avant le début des présentations ce matin. Je croise quelques présentateurs, en train de faire ce que je présume être des retouches de dernière minute. En particulier, Walter E. Brown, un mathématicien à la fois très intéressant à lire et plutôt drôle dans ses écrits (même formels), avec qui je compte passer deux séances cet après-midi pour une présentation sur la métaprogrammation.
Pour le voir en-ligne : https://www.youtube.com/watch?v=HlsxKVwp9J8
Ma première présentation est dans la salle Liebniz, et porte sur l'écriture d'algorithmes parallèles destinés à être exécutés par un GPU. Le présentateur, Ade Miller, est le co-auteur d'un livre sur AMP (Advanced Multiprocessor Parallelism), une technologie franchement surprenante.
Début du contenu technique
Ade Miller avoue d'office qu'il aime C++ mais ne fait pas de programmation C++ au quotidien (il fait du C# et du PHP dans le cadre de son travail).
Conséquemment, nous couvrirons la programmation parallèle sur GPU mais pas les côtés obscurs du langage, des GPU et de l'optimisation. Ade Miller insistera sur les algorithmes.
Il montre une hiérarchie d'algos, et dit qu'il va parler trois d'entre eux surtout (les autres leur ressemblant).
Cet algorithme se parallélise bien. Il utilise l'exemple de doubler les valeurs des éléments d'un tableau. La correspondance 1:1 entre entrées et sorties est triviale sur un GPU, et les opérations sont indépendantes.
Pour le faire avec AMP, il faut deux :
Le array_view contrôle les copies de données. Il est possible de le faire manuellement pour plus de contrôle.
Question : comment peut-on utiliser restrict?
Ade Miller : restrict(amp) pour le GPU. restrict(cpu) pour le CPU
Son exemple est une accumulation, mais techniquement accumulate() n'est pas un reduce : accumulate() présume que les opérations sont ordonnancées, mais un reduce ne garantit pas l'ordre des opérations.
Une réduction simple implique de séparer un tableau en deux (additionner les éléments 0 et 8, 1 et 9, ..., 7 et 15), puis le résultat en deux (0 et 4, 1 et 5, ...), puis...
Son reduce parallèle passe par concurrency::parallel_for_each(), mais est inefficace. L'utilisation de la mémoire n'est pas terrible, et les threads sont dormants pour la plupart.
Ade Miller présente la mémoire par tuiles (Tile Memory) et la séparation de tuiles en sections (Warps). Un Warp est « large comme 32 opérations », et ces opérations s'exécutent en Lockstep.
Chic schéma de la mémoire et des accès mémoires (globale, registres du GPU, tuiles...)
Ade Miller parle de ce qu'il nomme le occupancy pour les threads, qu'il faut optimiser. Il ne faut pas utiliser plus de threads que le nombre offert pas un Symmetric Multiprocessor, car alors on risque d'en avoir moins que prévu. Il y a un jeu d'équilibre à faire ici.
Utiliser les tuiles, c'est mieux qu'utiliser la mémoire globale. Pour utiliser une tuile, il faut tenir compte de trois indices; charger les tuiles, c'est un peu comme contrôler manuellement sa Cache.
Il montre comment réaliser le reduce, comme avant, mais dans des tuiles.
Le modèle global est :
Ade Miller fait remarquer que la majorité de ses threads ne font que bouger des données, sans faire de réel traitement. C'est triste. Il suggère qu'on peut remplacer les simples chargements par des combinaisons « deux chargements et une addition » pour mieux occuper les threads.
Le code résultant n'accède pas efficacement la mémoire et contient un if. Ça fait mal.
Il faut tenir compte du fait que chaque Warp exécute tout le code, qu'il soit pertinent ou non. Ainsi, les threads qui ne font rien ne terminent pas plus vite que ceux qui font du vrai travail. En modifiant les indices utilisés, pour profiter de ceux qui « dorment », on peut faire plus de travail avec les mêmes threads!
On veut aussi réduire les conflits de Banks, pour améliorer le pattern d'accès à la mémoire. On veut lire des données sur des Banks distincts pour réduire les conflits.
Ade Miller recommande de ne pas écrire ce genre de code manuellement, puisque l'algorithme amp_algorithms::reduce() le fait.
Un scan est une somme de valeurs à des indices consécutifs (c'est une sorte de fold mais avec dépendances entre les éléments) :
Une technique pour l'implémenter est le up-sweep (une sorte de reduce). Une autre est le down-sweep. Ade Miller recommande de consulter la littérature pour l'algorithme, car il est subtil.
Évidemment, AMP supporte scan et offre un algorithme pour ce faire, de même qu'un « wrapper » pour la version de DirectX
En résumé :
Il propose son site, http://www.gregcons.com/cppamp
Question : y a-t-il une différence entre utiliser le thread zéro et utiliser un autre thread lors d'une réduction?
Ade Miller : sous la couverture, le GPU divise le travail en threads, et les threads s'exécutent Lockstep. Ainsi, tous les threads paient le prix de la pire exécution, qu'ils en aient besoin ou non
Question : pourquoi ne pas faire faire les calculs à tout le monde, puis ne conserver que les résultats pertinents/ corrects?
Ade Miller : ça se fait, et c'est parfois mieux. C'est du cas par cas. Si une condition est vraie (ou fausse) pour tous les threads d'un même Warp, alors on n'a pas besoin du if dans ce Warp.
Question : il y a une proposition sur la table pour une STL parallèle standard, et il y a une politique d'exécution à spécifier. Est-ce que tu sais si on pense éviter aux usagers de se préoccuper des array_view?
Ade Miller : non, je n'en sais pas beaucoup à ce sujet, mais je veux apprendre. J'espère que le standard de C++ et AMP s'influenceront mutuellement. Je sais qu'il y a aussi une proposition pour ajouter array_view à la bibliothèque standard.
Question : pour obtenir les performances idéales, il semble nécessaire de connaître en détail le GPU et ses caractéristiques. Est-il possible de faire abstraction de ces détails?
Ade Miller : la version par défaut de AMP fait un bon boulot. Toutefois, en fin de compte, pour tirer le maximum du matériel, mieux vaut le connaître et le comprendre. AMP permet d'écrire du code portable pour le GPU, ce qui est avamtageux, mais cette abstraction n'est pas gratuite.
Question de Michael Wong : la spécification technique pour le parallélisme parle d'algorithmes mais pas de modèle mémoire, ce qui est plus près des préoccupations de AMP et de OpenMP.
Ade Miller : en effet. Quiconque se préoccupe de vitesse doit aussi se préoccuper de la manière dont il accède à la mémoire.
Fin du contenu technique
En route vers le Keynote du matin. Sur une table, je trouve un Cheat Sheet pour les (nombreuses!) directives OpenMP, sans doute laissées là par Michael Wong. Chouette!
Pablo Halpern semble perdu dans ses pensées, mais je pense qu'il présente en après-midi alors je le laisse tranquille.
Les muffins du colloque sont nettement meilleurs que ceux de l'hôtel. Par contre, je pense qu'ils ont manqué de café ce matin car la plupart des cruches étaient déjà vides à mon arrivée dans la grande salle.
Ce matin encore, un band joue du rock et du blues joue à notre arrivée. C'est bien pour l'ambiance ici.
Pour le voir en-ligne : https://www.youtube.com/watch?v=ULqNSvR8PZo
Le keynote du matin est par Bjarne Stroustrup lui-même, et s'intitule Make Simple Tasks Simple.
Début du contenu technique
Jon Kalb prend la parole d'abord. On nous présente une vidéo d'Alexander Stepanov qui présente Bjarne Stroustrup, et qui dit pourquoi il aime C++ :
Alexander Stepanov dit que Bjarne Stroustrup lui a dit d'office, quand ils se sont rencontrés, que son objectif était d'offrir aux programmeurs la capacité d'exprimer toutes les formes d'abstraction qui leur conviendrait.
Alexander Stepanov relate qu'à un certain moment, les membres du comité de standardisation ont essayé de rendre toutes les fonctions virtuelles, et relate que Bjarne Stroustrup a résisté, préservant l'intégrité du langage. C'est pour cette raison, grâce à cette intégrité, que C++ continue d'évoluer.
Bjarne Stroustrup prend la parole. Il semble un peu ému.
Il dit souhaiter parler de simplification, par opposition à une forme d'ingéniosité (Cleverness) mal placée. On valorise la pensée latérale, Out of the Box, mais on sous-estime la vertu de la simplification du code et de la pensée qu'il y a derrière. Ainsi :
Il cite Machiavel sur la difficulté de réaliser des réformes, puis Voltaire avec : « Le mieux est l'ennemi du bien ». Il n'est pas astucieux d'être trop astucieux.
Design for Clarity. Il existe de la complexité essentielle et accidentelle. Cachez la complexité mais sans donner dans l'abus d'abstraction (mieux vaut abstraire pour des cas concrets).
Que veut-on dire par « simple »? (Bjarne Stroustrup cite Albert Einstein : as simple as possible, no more). Simple est souvent compréhensible, beau, rapide (et quand on ne peut faire simple, faut penser à une interface simple). Il faut que le code exprime clairement l'intention.
Sur le plan historique, Bjarne Stroustrup présente le passage de fonctions init(x) et cleanup(x) à la dualité constructeur/ destructeur, en 1979, une grosse amélioration!
L'avènement des constructeurs a mené aux exceptions, pour rapporter les erreurs d'initialisation, et les pointeurs intelligents, pour la saine gestion de la mémoire allouée dynamiquement.
Bjarne Stroustrup présente la simplification graduelle des répétitives, et le retour de auto après des années de non-utilisation, puis le recours à un algorithme standard, puis un glissement vers ce qui sera un intervalle pour éliminer les begin et les end dans les cas simples.
L'allègement syntaxique poursuit avec les alias de types (vive using!).
Bjarne Stroustrup parle ensuite de la gestion des ressources, en évolution depuis l'époque où on utilisait des pointeurs sur des conteneurs pour économiser des copies jusqu'à aujourd'hui, avec la sémantique de mouvement, en passant pour fins d'illustration naïve par des pointeurs intelligents.
Remerciements publics à Howard Hinnant pour la sémantique de mouvement. Tout le monde applaudit!
Insertion de généricité. Le code fond et gagne en utilité.
Insertion de λ. Encore moins de code.
Ajout de traits, pour alléger l'écriture. Bjarne Stroustrup critique les traits, trop compliqués à ses yeux, même si tout le monde présent est à l'aise avec eux.
Cas intéressant où un objet peut-être initialisé avec accolades même sans constructeurs, comme un struct du langage C.
Arrivent maintenant les concepts, dans une optique d'évolution de la programmation générique :macros, puis templates, puis templates et concepts de concert. Il avoue avoir passé 20 ans sur les concepts; il existe des solutions (p. ex. : passer par des infos dans une vtbl), mais il refusait de payer un prix à l'exécution.
Les templates ont des problèmes : trop de code Boilerplate, syntaxe particulière, messages d'erreurs spectaculaires, etc. Ça donne en fait deux langages en un, ce qui n'est pas bon selon lui. Il parle d'une fonction de quatre lignes pour laquelle les messages d'erreurs dépassaient la capacité de la fenêtre console où il avait essayé de la compiler.
Pourtant, on les utilise : flexibles, rapides, extrêmement puissants!
Il y a des remèdes à cette complexité :
Enfin, le compilateur sait de quoi on parle et peut nous expliquer ce qui se passe convenablement!
Alexander Stepanov a mis de l'avant que les contraintes sur les paramètres génériques sont la clé ici, alors Bjarne Stroustrup le remercie. Restait à voir comment y arriver. On l'a fait: des prédicats sur des types! Bjarne Stroustrup mentionne aussi Andrew Sutton, Andrew Lumsdaine et Gabriel Dos Reis qui ont travaillé avec lui pour actualiser cette vision.
La première tentative a échoué. Trop complexe. La version simplifiée, elle, est presque finalisée.
Insertion de requires suivi d'un prédicat statique entre template<...> et l'entité pour laquelle les contraintes s'appliquent (classe ou fonction). Par exemple :
template <class T>
requires Sortable<T>
void sort(T &);
Simplification possible et équivalente :
template <Sortable T>
void sort(T &);
... quand il n'y a qu'une seule contrainte. Simplification additionnelle :
void sort(Sortable&);
Wow-e!
Ajout génial d'Andrew Sutton :
requires(C x) { {f(x)}->int; }
...(j'espère avoir bien noté la syntaxe) qui vérifie si l'expression compilerait... Enfin!!!!!
Dans la présentation de Walter E. Brown cet après-midi, une révélation a été faite qui nous montre même comment on pourrait écrire requires... Quelle journée!
Il y aura une bibliothèque standard de de concepts. Les concepts ne sont pas destinés à être des classes de base.
On permettra la surcharge sur la base de concepts. Par exemple :
template <ForwardIterator T>
int distance(T,T);
template <RandomAccessIterator T>
int distance(T,T);
Yesss!!!
Les concepts s'appliquent aux λ :
auto add = [](Number x) {
return [](Number y) {
return x + y;
};
}; // fermeture! concept Number!
D'ailleurs, auto est le concept le moins contraint :
auto add = [](auto x) {
return [](auto y) {
return x + y;
};
}; // fermeture! généricité!
Je suis vraiment ému. Là, on touche au bonheur!
Quelques défis :
Le reste de la présentation était un survol des travaux en cours.
Question : je suis inconfortable avec les risques de conflits de noms entre concepts et classes
Bjarne Stroustrup : ça va fonctionner de la même manière que maintenant pour les conflits de nom. Ça va vivre dans un espace nommé
Question : il me semble qu'on aurait pu aller plus loin en remplaçant find_if(v,pred)!=end(v) par any_of(v,pred)
Bjarne Stroustrup : tu as raison. C'était un exemple qui visait à mettre l'accent sur la démarche incrémentale
Question : en créant la spécification pour les concepts, combien de concepts aviez-vous pour couvrir la bibliothèque standard?
Bjarne Stroustrup : on en avait 120 initialement, c'était fou, et je visais 12. J'ai fini avec 16. C'est clairement moins de 24, et certains (Mergeable<T>, par exemple) sont en fait des « concepts synthétiques » (NDLR : c'est ma terminologie, pas la sienne) construits sur des concepts plus simples
Question : pour ce qui est de la facilité d'entretien du code, quelle est votre position sur le mouvement et le Forwarding implicite ou explicite?
Bjarne Stroustrup : j'ai choisi mes exemples parce que spécifier l'intention clarifiait le propos. Je pense que plusieurs débats d'experts sur le passage de paramètres, par exemple, sont mal avisés. Je vous invite à aller à la présentation de Herb Sutter vendredi à ce sujet.
Question : progresse-t-on vers un langage où nous faisons effectivement du Duck Typing?
Bjarne Stroustrup : on a approché la question de la programmation générique car il s'agit du créneau qui a le plus grandi au fil des années. Des trucs comme la réflexivité dynamique nous conviennent moins que la réflexivité statique, où il y a plus d'information sur les types. Dans le futur, on verra beaucoup d'avancées dans la multiprogrammation et la concurrence; les threads et les verrous sont trop « bas niveau » pour le code de tous les jours. C'est là que les prochaines grandes évolutions du langage se trouvent, à mon avis.
Bjarne Stroustrup remercie publiquement Herb Sutter pour avoir convaincu le comité de standardisation d'adopter un rythme accéléré de mises à jour régulières.
Question : RAII est merveilleux, mais le nom est terrible...
Bjarne Stroustrup : la solution est une machine à voyager dans le temps
Question : pouvons-nous en arriver à un équivalent de scope_exit() dans D?
Bjarne Stroustrup : j'en donne un exemple dans The C++ Programming Language, 4th Edition. J'ai écrit une classe finally parce que j'étais tanné qu'on m'en parle.
Pour les intéressé(e)s, finally est facile à exprimer :
template <class F>
class finally_
{
F f;
public:
finally_(F f)
: f{f}
{
}
finally(const finally&) = delete;
finally& operator=(const finally&) = delete;
~finally()
{ f(); }
};
template <class F>
auto finally(F f)
{ return finally_<F>{f}; }
Ne reste plus qu'à insérer le code de fin de portée dans une λ comme ceci :
void f()
{
auto _ = finally([]() { cout << "On sort de la fonction!" << endl; });
// ...
}
Fin du contenu technique
C'était riche en contenu!
Petite pause dîner, le temps de mettre de l'ordre dans mes notes. J'ai mis la version en ligne de mon périple à jour en même temps. Il y avait beaucoup de matériel pour mes cours alors je voulais le nettoyer avant d'oublier des détails.
Pour le voir en ligne : https://www.youtube.com/watch?v=Am2is2QCvxY et https://www.youtube.com/watch?v=a0FliKwcwXE
Je passe mon après-midi avec Walter E. Brown, que j'ai mentionné un peu plus haut. Sa présentation débute par une vidéo de cinq minutes d'un « robot orchestre », assez sympathique d'ailleurs.
Je suis là mais au fond de la pièce, étant donné l'angle de la photo.
Début du contenu technique
On va faire de la métaprogrammation, et ce n'est pas une présentation pour débutants.
On va s'attarder sur <type_traits> et sur void_t. Chic!
Walter E. Brown explique qu'il programme depuis 50 ans. Il est un mathématicien (Ph. D en 1982) et son directeur prétendait que les maths sont une petite branche de l'informatique. Il a pris sa retraite de Fermilab où il côtoyait des détenteurs de prix Nobel, mais il n'est pas mort et demeure actif Il est l'auteur de plus de 85 propositions pour le standard ISO de C++ sur une période de 15 ans.
À son crédit : cbegin/ cend, common_type, <random>, <ratio>... Il a aussi contribué à using pour les templates, aux conversions contextuelles, et aux variable templates. Il contribue aussi aux fonctions mathématiques de C++, qui est un projet connexe au standard ISO usuel.
Il nous informe qu'il a des opinions prononcées, que nous pouvons ne pas partager... mais que nous devrions partager
Il travaille sur les versions constexpr de gcd, abs, type_is, bool_constant, is_one_of, void_t, etc.
Walter E. Brown définit la métaprogrammation :
En C++, on en fait entre autres avec des templates, mais aussi avec constexpr. Walter E. Brown disserte sur les templates comme machine de génération de code (programmation générative, en quelque sorte). Il indique qu'on s'en sert pour accroître la flexibilité des sources et la vitesse d'exécution. C'est pas gratuit (en temps de compilation) mais ça rapporte.
Walter E. Brown présente un remplacement pow<> pour std::pow() et montre un temps d'exécution qui n'est que 25% de celui de la version standard... à l'époque (2001), et sans optimisation. Avec optimisation, on passe de 12 secondes pour std::pow() à 0,3 seconde pour pow<>... environ 90% de gains!
En métaprogrammation, il faut penser immuabilité, absence de variables, absence de services virtuels, absence de RTTI (ou de Run-Time whatever). C'est un état d'esprit.
Montre une métafonction :
template <int N>
struct abs
{
static_assert(N != numeric_limits<int>::min()); // sans messages, saveur C++ 17
static constexpr int value = N < 0? -N : N;
};
Le test pour N!=numeric_limits<int>::min() est là car cette valeur n'a pas de correspondance positive.
Walter E. Brown discute de la nuance entre une fonction constexpr et une métafonction. La principale qualité de la fonction constexpr est la familiarité; les métafonctions ouvrent par contre plus de portes (public, privé, types, templates imbriqués, etc.). Il présente la récursivité statique, avec gcd<M,N>::value, et la spécialisation gcd<M,0>. Il décrit la spécialisation comme du Pattern Matching spécialisé, et valide M!=0 par un static_assert.
Ça me fait réaliser que Incompilable<Raison> est moins utile depuis static_assert.
On peut raisonner sur des types (écrire notre propre sizeof, par exemple).
Walter E. Brown décrit le trait std::rank (0 pour les scalaires, 1+rank<U>::value pour U[N]). Il montre ensuite remove_const, et suggère remove_const_t<T> en remplacement de typename remove_const<T>::type.
S'ensuit une brève discussion du formalisme qui a évolué. Il parle du type iterator_traits, qui expose plusieurs types, jusqu'à la pratique formalisée par David Abrahams et d'autres de ::value pour les valeurs et ::type pour les types.
Une métafonction pour l'identité :
template <class T>
struct type_is { using type = T; }; // il y a de la chicane autour du nom identity...
Il utilise cela par héritage, une pratique simple mais discutable :
template <class T>
struct remove_const : type_is<T> {};
template <class T>
struct remove_const<const T> : type_is<T> {};
Il présente IF (static_if_else, std::conditional, maintenant std::conditional_t) et son utilisation pour divers cas, incluant choisir une classe parent.
Variation amusante : le bien connu enable_if :
template <bool, class T = void>
struct enable_if : type_is<T> {};
template <class T>
struct enable_if<false, T> {};
Avec enable_if<false, ...>::type, on a SFINAE (nom trouvé par Daveed Vandevoorde) qui retire le cas de l'ensemble des surcharges considérées :
Walter E. Brown montre des cas d'utilisation pour enable_if. La salle réagit. C'est fou : ça se passe comme dans ma classe quand je fais des trucs semblables.
Les concepts vont alléger ou modifier de beaucoup les usages avec enable_if. Walter E. Brown vante les bases mathématiques de ce beau projet, et fait un lien avec les travaux d'Alexander Stepanov et d'Emmy Noether.
Pour comparer :
template <class T>
enable_if_t<is_integral_t<T>, maxint_t> f(T) { ... }
...deviendra :
template <Integral T>
maxint_t f(T) {...}
Cool!
Walter E. Brown montre integral_constant<class T, T v> qui a :
template <class T, T val>
struct integral_constant
{
using type = T;
static constexpr type value = val;
constexpr type operator()() const { return value; }
constexpr operator type() const { return value; }
};
Il recommande de dériver d'integral_constant pour une valeur et de type_is pour les types.
Il propose ceci :
template <bool val>
using bool_constant = integral_constant<bool, val>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
Il parle ensuite de l'évolution des pratiques :
is_void<T>::value → bool(is_void<T>{}) → is_void<T>{}() → is_void_v<T>
Nos méthodes se raffinent! Le _v est pour C++ 17, le _t est pour C++ 14.
Petite pause. Les gens apprécient, mais c'est un bon public pour ça. J'ai vu Louis Dionne, qui connaît évidemment tout cela lui aussi, et il est un peu déçu, remarquant que le code utilisé peut générer plus d'instanciations de templates que requis avec type_is et estimant que le gcd<M,N> présenté est naïf en comparaison avec une version qui calculerait la valeur sous la couverture par une fonction constexpr.
Walter E. Brown mentionne que void est l'une des quinze catégories de types standards de C++ (ces catrégories ne se chevauchent pas).
L'idée de base de is_void<T>, sur le même modèle que is_const<T> :
template <class> struct is_void : false_type {};
template <> struct is_void<void> : true_type {};
template <> struct is_void<const void> : true_type {};
template <> struct is_void<volatile void> : true_type {};
template <> struct is_void<const volatile void> : true_type {};
Une autre implémentation passerait par is_same, en prenant soin d'enlever les qualifications cv, avec un using :
template <class T> struct is_void : is_same<void, remove_cv<T>> {};
Il indique que remove_cv<T> est remove_volatile<remove_const<T>>.
Avec des templates variadiques, on peut écrire
template <class T, class ... P> struct is_one_of;
template <class T> struct is_one_of<T> : false_type {};
template <class T, class ... P> struct is_one_of<T, T, P> : true_type {};
template <class T, class U, class ... P> struct is_one_of<T,U,P> : is_one_of<T, P> {};
...puis
template <class T> using is_void = is_one_of<
void, const void, volatile void, const volatile void
>;
Walter E. Brown discute des opérateurs statiques (sizeof, decltype, typeid, noexcept) et du fait que, ne générant pas de code, ils peuvent être utilisés dans des déclarations sans définitions. Il montre declval<T>() au passage (declval<T>() retourne une rvalue, alors que declval<T&>() donne une lvalue).
Il montre un truc amusant :
template <class T>
class is_copy_assignable
{
template <class U, class = decltype(declval<U&>() = declval<U const&>())>
static true_type try_assignment(U&&); // susceptible de SFINAE
static false_type try_assignment(...);
public:
using type = decltype(try_assignment(declval<T>()));
};
Faudrait aussi s'assurer que l'affectation retourne T& pour être rigoureux. Stephan T. Lavavej signale que SFINAE sur une expression mal formée n'est valide que depuis C++ 11, et pas encore dans MSVC
L'arrivée de decltype permet de réduire les abus de sizeof; Walter E. Brown explique les « techniques du passé ».
Maintenant, Walter E. Brown relate sa première écriture de void_t, une métafonction multiparamètres, mais on pourrait la faire à un seul paramètre pour bien des cas :
template <class ...> using void_t = void;
Ça a pincé car il y avait un trou dans le standard. Ça compilait avec Clang mais pas avec gcc, et les deux avaient raison. Au besoin, on contourne :
template <class...> struct voider { using type = void; };
template <class ... P> using void_t = typename voider<P...>::type;
Ça sert à...? Voici :
Walter E. Brown remercie Stephan T. Lavavej et Howard Hinnant pour leur aide.
Quelques applications :
template <class, class = void>
struct has_type_member : false_type {};
template <class T>
struct has_type_member<T, void_t<typename T::type>> : true_type {}; // susceptible à SFINAE
Oh, ça, j'aime! Stephan T. Lavavej vient dire à quel point il trouve ça cool, bien que lui-même fasse de la métaprogrammation tous les jours.
La salle est confuse. Walter E. Brown doit expliquer à plusieurs reprises que la clé n'est pas void, c'est que le type est le même dans les deux cas (son choix est void, mais c'est un choix esthétique).
Il revient :
template <class T>
using copy_assignment_t = decltype(declval<T&>() = declval<T const &>());
template <class T, class = void>
struct is_copy_assignable : false_type {};
template <class T>
struct is_copy_assignable<T, void_t<copy_assignment_t<T>>>
: is_same<copy_assignment_t<T>, T&>
{
};
La salle applaudit! Marshall Clowe dit Thank you Sir!
Si on veut is_move_assignable, on change T const& pour T&& et on change le nom. Bingo! Par la même approche, on peut attaquer n'importe quelle expression. C'est une solution générale au problème. Eh ben!
Tout ça exige Expression SFINAE alors MSVC ne le supporte pas encore, mais ça s'en vient (d'ici la fin de 2014).
Peter Sommerlad demande si ça signifie qu'on peut implémenter Concepts Lite aujourd'hui. Walter E. Brown dit « Oui, mais pas moi, je suis à la retraite ».
On vise <type_traits> pour C++ 17 avec ça... Reste à s'entendre sur le nom! Un membre de l'assemblée suggère qu'on lui donne le nom de son auteur; Walter blague sur le fait que quand il enseignait, il donnait des brownie points.
Il fait enfin une brève récapitulation, et il finit sur une citation du Daily WTF sur le fait que l'on se demande parfois si ce qu'on voit de nouveau est de la sorcellerie ou... un WTF.
Fin du contenu technique
Ouf. Après le Wow! des concepts ce matin, cette deuxième partie absolument brillante vient couronner une journée payante!
Ouais, je suis content de la dernière demie-heure.
Quelques bouchées rapides (pita, biscottes, trempettes, sucreries, café... Dans une petite assiette, parce que c'est pas léger alors vive la modération), puis je vais vers la prochaine présentation.
Pour le voir en ligne : https://www.youtube.com/watch?v=YKa1doFcs8E
Pour terminer l'après-midi, à la salle Gauss, j'assiste à la présentation d'Alisdair Meredith, l'homme en charge de la bibliothèque standard de C++ et de son évolution, qui parle de la vie dans le comité de standardisation de C++. Je suis surpris: nous sommes environ quinze participants. Eh ben...
Début du contenu technique
Il est chairman du LWG (Library Working Group), et ce sont des termes de cinq ans.
Il nous indique que quand on aime quelque chose, il est souvent préférable de ne pas voir comment c'est fait :)
ISO/IEC JTCI SC22 WG21
Quand une décision est prise par WG21, ça remonte chaque strate jusqu'en haut pour être officiel. C'est Herb Sutter qui fait le pont entre WG21 et SC22
Un WG permet à plusieurs compagnies de collaborer pour définir le langage
Au niveau ISO, ce sont des pays qui discutent, pas des compagnies, et les pays ont le même poids en principe (en pratique, tout bouge plutôt par consensus). Ça fonctionne, malgré la complexité de la structure
Pour le standard 14882 (C++), on examine le processus :
Le comité a beaucoup gagné en nombre de participants au fil des années, surtout depuis C++ 11
Projets supplémentaires :
Plusieurs projets en cours :
Il semble manquer un study group pour les nombres, selon un membre de l'assemblée (Lawrence Crowl)
Le comité est organisé en groupes de travail. Pour C++ 14 :
Alisdair Meredith explique les responsabilités de ces groupes. En gros, ils ne laissent «sortir» une proposition que si ça semble tenir la route: les idiomes sont cohérents, les approches ne se contredisent pas, le tout s'intègre de manière harmonieuse, le choix des mots est adéquat pour un standard, etc.
Son premier meeting remonte à 2003, celui où le révision du standard de 98 a été complété et où Bjarne Stroustrup a lancé les travaux de C++ 0x, qui est devenu C++ 11. Alisdair Meredith y était allé parce que c'était près de chez lui. La révision de 2003 visait à nettoyer le standard de 1998; C++ 0x visait à faire évoluer le langage
Les propositions soumises au comité sont envoyées trois semaines avant la rencontre. Elles sont discutées au meeting; on y parle aussi des irritants à régler.
Sa première proposition était std::array. Il s'est basé sur un article existant pour le format, a fait des recherches, s'est inspiré de Boost, d'un exemple dans un livre de Bjarne Stroustrup et d'un autre dans un livre de Matt Austern. Il ne savait pas comment soumettre sa proposition!
Exemple de rencontre de 2003 :
Question : doit-on représenter une entreprise?
Alisdair Meredith : non, mais c'est bien vu. L'essentiel est d'être un expert technique
Alisdair Meredith décrit le contenu du comité de 2003. En particulier, EDG avait « shippé » export pour les templates, d'autres voulaient qu'on s'en débarrasse (As long as nobody implements it, nobody will care... Oops!). C'était un travail de fou, alors export est resté dans le standard (l'enlever à ce stade aurait été injuste).
Lors d'une rencontre en sous-groupes, le travail qualifié « prêt » est ramené sur la table à la fin de la semaine
Une revue de proposition procède comme suit :
Alisdair Meredith explique par exemple que pour les concepts, ont avait pensé utiliser where pour les clauses, mais ce mot était trop populaire donc on y est allé pour requires
Ajouter un mot clé coûte un rein. Non, ce n'est pas une faute d'orthographe
Le standard n'est pas un tutoriel. C'est une spécification, pas une intention
On vote par consensus (strong/weak, for/against, neutral). On peut voter souvent pour voir si une direction se dessine. Alisdair Meredith montre le vote des concepts, avec plusieurs votes sur plusieurs questions...
On vote sur chaque option. C'est pas un choix de réponse entre plusieurs options
Alisdair Meredith relate un vote qu'il a géré sur la liberté qu'auront les implémenteurs de la bibliothèque standard pouvaient appliquer constexpr à leur convenance (c'est un truc qui a une impact observable, après tout!). Les cinq options, chaque vote donnait 6-6-6-6-6... Cauchemar!
On parle bien de consensus, pas de démocratie. C'est pas des votes à 50%+1, c'est plus de l'ordre de .
Les formats acceptables pour soumettre une proposition sont :
C'est tout.
Doivent y apparaître :
Soumettre au secrétaire à temps!
Contenu :
Quand il y a des irritants:
Pour traiter un irritant :
Le « vrai travail » se fait le soir, plus socialement. Les gens sont plus créatifs et échangent sur ce qui s'est passé dans les autres comités.
Le vote officiel (autrefois) commençait par des votes officieux le jeudi. Ainsi, le vendredi, il y avait peu de surprises. Vote américain (20-30 voix), puis ISO
Alisdair Meredith relate une de ses premières discussions pour une question de langage : dans un constructeur de délégation, si une exception est levée, le destructeur devrait-il être appelé? (il s'est obstiné pour oui... avec le gars qui a écrit tout ce qu'il savait sur les exceptions! Il a éventuellement dû plier, mais a beaucoup appris)
En relatant des trucs de dernière minute, Alisdair Meredith nous explique que noexcept est arrivé à la dernière minute pour nous empêcher de tout casser dans la bibliothèque standard, et on a ajouté async même si on n'était pas supposés ajouter de nouveaux features... Hé la la
On est passé à des meetings de six jours plutôt que cinq. C'était trop serré, et le jour de plus fait une grosse différence. Les votes officiels se tiennent le samedi PM (le samedi AM sert à planifier la prochaine rencontre).
On travaille plus entre les meetings aujourd'hui. Les Study Groups continuent de progresser entre temps. Il y en a 12 d'actifs (celui sur le SGBD semble fermé)
Le rapport du lundi matin est devenu trop lourd (trop de groupes). Il faut travailler sur l'efficacité du processus.
Ils ont une liaison avec le comité du standard de C et une autre avec le comité sur les vulnérabilités logicielles
Alisdair Meredith finit par un effort enthousiaste de recrutement. Je ferais bien ça, une fois le doctorat terminé. À voir.
Fin du contenu technique
On a dépassé l'échéance d'une dizaine de minutes, mais c'était intéressant.
J'ai pu parler à mon amoureuse pendant la pause. Elle me manque.
Les Lightning Talks commencent à 20 h 30.
Au retour à l'hôtel, je me demandais quoi manger. Il se trouve que l'épicerie bio locale non-loin de mon hôtel est ouverte 24/24. Je me suis pris des pâtes et du pesto... C'est la faute de mon épouse que j'aime, qui m'a mis cette idée dans la tête un peu plus tôt (merci mon amour!).
Début du jour 3. Mon projet aujourd'hui est :
À mon arrivée, un ajout : un tableau blanc sur lequel il est indiqué Graffiti Wall: insert code here. Sais pas ce que ça va donner, mais c'est une idée sympathique
J'ai croisé Walter E. Brown en allant vers la salle où se donnent les Lightning Talks. J'en ai profité pour lui serrer la main et lui dire qu'avec son implémentation de has_member_type<T> sur la base de void_t hier, il a fait ma journée. Il était content; c'est un sympathique prof d'université retraité mais éveillé, qui fait « grand-papa souriant ». Il m'a souhaité au revoir en français.
Sur la route en autobus ce matin, je contemplais la ville (verte, relativement propre, un bel endroit), mais j'ai eu une vague de nostalgie pour mon chez-moi. Ça m'a fait réaliser que quand je voyage, je travaille fort et j'essaie de ne pas trop penser à la maison (sauf quand je parle à mon épouse ou aux enfants), et je fais bien car quand ça frappe, ça fait mal. Za, les filles, je vous aime. Même les chiens me manquent (un peu), c'est bien pour dire...
Les Lightning Talks de ce matin vont comme suit.
Andy Webber : pourquoi on devrait être terrifié du Strict Aliasing (voir https://www.youtube.com/watch?v=Jd8MI4b_AUc). Ne brisez pas la règle du §3.10.10 (ne pas traiter un entier 32 bits comme deux entiers 16 bits, disons). Il montre des exemples avec des union (un int32_t et un int16_t[2]). Il y a un lien avec TBAI (Type-Based Aliasing Information). Le comité dit que memcpy() va fonctionner car t'as le droit de voir la mémoire comme des bytes. Il suggère un possible alias_cast...
Brett Hall : Software Transactional Memory, for Reals (voir https://www.youtube.com/watch?v=plQOu_LsKHE). Il explique que dans son entreprise, ils utilisent ça depuis trois ans. En ses mots : tl;dr: great, but ymvv (Your Mileage May Vary). Les écritures aux variables partagées deviennent visibles en bloc seulement. Leur système est unbounded, weakly atomic, indirected (pas « in place »). On l'utilise pour fins de composabilité (éviter les verrous, les deadlocks). Leur logiciel traite beaucoup de données, et leurs données se prêtent à du traitement Embarrassingly Parallel, leurs calculs sont privés (intrants, traitements, extrants) et les traitements sont lourds. Ça s'apprend bien, ça facilite le raisonnement. Ils ont un risque de livelock/ famine si les transactions sont longues. Faut localiser les effets de bord. Risque de Inconsistent Read (ça va de soi). Risque de course (immutability & internal transacting). Support d'un retry (mais ça peut causer une variante du deadlock).
Il se trouve que plus tard en 2014, Jonathan Caves a été co-auteur, avec Gabriel Dos Reis et Herb Sutter d'un texte proposant de corriger cette aberration (et plusieurs autres) : http://isocpp.org/files/papers/n4228.pdf
Jonathan Caves : The New Old Thing (or fun with lambdas). Voir https://www.youtube.com/watch?v=Pr9g1nqTz_A. Il mentionne l'article de Sumant Tambe comme base de réflexion, et rappelle la sortie distincte selon le compilateur. Évidemment, ça génère une expression avec plusieurs appels de fonctions, donc on tombe dans le compiler-dependent. À titre d'analogie, il mentionne v[i]=++i;, qui va de soi, mais aussi f1()->mf(f2()); où l'ordre d'évaluation de f1() et de mf() dépend du compilateur (ouch! Pas fin!) La solution? Sans doute imposer un ordre d'évaluation, mais ça peut ralentir le tout et ça peut changer le comportement (imprudent) de programmes existants.
Michael VanLoon : Anatomy of a Smart Pointer (voir https://www.youtube.com/watch?v=bxaj_0o4XAI). Il classe bien des trucs là-dedans, dont des handles comme std::string. Il trace la distinction entre intrusif (collaboratif, toutefois, style COM) et extrusif (container-based, dans ses mots, comme shared_ptr). Il détaille les deux (il a de l'expérience avec COM). Des trucs que j'enseigne, en gros. Sauf peut-être enable_shared_from_this, à examiner avec les amis du DDJV.
On termine à 8 h 55, juste le temps de changer de salle.
Pour le voir en-ligne : https://www.youtube.com/watch?v=ECoLo17nG5c
Michael Caisse est un très gentil monsieur de Californie avec qui j'ai eu la chance de bavarder lors de la réception du jour 0. C'est un spécialiste de métaprogrammation, de même qu'un contributeur connu à Boost, et sa présentation de ce matin porte sur The Canonical Class, le sous-titre laissant entendre que son propos sera de comprendre ce qui va vraiment dans une classe avec C++ 11. Son propos tend à être très clair, alors je compte porter une attention particulière à sa pédagogie.
Début du contenu technique
Michael Caisse blague sur le fait que la salle est pleine, alors que la salle Pascal non-loin discute de la philosophie de Google pour le code C++. On rit un peu.
Il ne compte pas parler de métaprogrammation ce matin, mais bien de l'évolution des règles pour l'écriture de classes avec du C++ contemporain. Il ne donnera pas une checklist de règles, par contre. C'est plus une réflexion sur les fondamentales de la rédaction d'une classe avec C++ 11. La « forme canonique », en quelque sorte.
Il mentionne la règle de 3, de 4 (règle de 3 plus constructeur par défaut), de 5, de 6 (règle de 5 + constructeur par défaut), ajouter swap, initializer_list, noexcept, constexpr, règle de zéro... Il ne les aime pas. Les règles nous empêchent de penser. Analogie avec la dame qui filme la présentation: expérimentée, sait quoi faire, mais demeure très prudente malgré tout.
Mention de l'item 7 de Scott Meyers dans Effective C++ (destructeur virtuel si méthodes virtuelles). Faut continuer de penser malgré tout.
Les règles sont utiles pour l'équipe typique. Les revues de code et le mentorat, c'est mieux.
Quel est le sens de « construire un objet par défaut »? Il parle d'appeler front() sur un vector par défaut... On peut le faire, mais quel sens doit-on donner à cette expression? On parle donc d'invariants.
Qu'est-ce qu'un thread par défaut? Rien, vraiment. Ça attend de devenir quelque chose.
Qu'est-ce qu'un unique_ptr par défaut? C'est un constructeur constexpr, en passant.
Peut-on créer un reference_wrapper par défaut? Manifestement pas.
Est-ce que votre type devait être Default-Constructible? C'est la vraie question. Sinon, on y va pour = delete, tout simplement. Ça clarifie l'intention. Mieux vaut être explicite.
Il montre une classe avec un int et un bar. Le int ne serait pas initialisé implicitement, mieux vaut être explicite.
Il montre une autre classe circular avec un invariant caché (un tampon, et deux pointeurs head et tail qui y pointent, donc des invariants à maintenir)
Faut donc s'occuper de la copie et, si on le souhaite, du mouvement. Michael Caisse demande aux gens si supprimer la copie est une bonne idée. Les réponses viennent, variées, mais il leur indique qu'il ne leur a pas expliqué la sémantique attendue de sa classe... On ne peut pas répondre à sa question, faute d'un savoir suffisant. À retenir!
Michael Caisse copie ses données avec std::copy(), qui fera le choix optimal sous la couverture à partir des types.
Il code son affectation « à bras ». Discute du choix d'avoir une méthode assign(), commune à plusieurs services.
Remarque : si on ne fait rien de particulier dans un destructeur, on n'a pas besoin de s'occuper de la copie. La clé est que le constructeur devient important quand on tient compte d'un état situé hors de l'objet.
Remarque : on peut avoir besoin de la copie sans destructeur si la classe a des invariants cachés, même si on a un tampon alloué automatiquement plutôt que dynamiquement.
Michael Caisse met un tableau à l'écran, pour montrer ce que le compilateur génère en fonction de ce que le programmeur génère, et tenant compte de =delete et =default.
Un paramètre par référence est (au moins) un sortant. Pas gentil. Chaque fois qu'on voit ça, on est obligé de réfléchir à ce que la fonction fait... Une autre motivation pour le mouvement!
Le mouvement permet des optimisations, et permet de profiter de la péremption de l'objet. Certains types sont Move-Only; c'est une sémantique raisonnable.
Michael Caisse pose des questions sur les moments où un paramètre sera déplacé ou copié... À voir la réaction dans la salle, il est clair que ce n'est pas tout le monde qui a compris encore, donc qui sait utiliser std::move()... Eh ben!
Beau schéma pour expr{glvalue{lvalue xvalue}rvalue{xvalue prvalue}}. Le xvalue est expiring lvalue, comme lors d'un return, ce qui explique qu'il apparaisse aux deux endroits. Le prvalue est un pur rvalue, variable sans nom.
Michael Caisse propose une réflexion sur le sens de « déplaçable ».
Le Moved-From doit être destructible et capable de se retrouver à la gauche d'une affectation. C'est tout. Ses invariants doivent être maintenus. (ma position ici; Michael Caisse vise le Destructive Move un peu comme le suggère Sean Parent et al. mais n'est pas fixé encore; il trace un parallèle entre delete p; p->... qui serait impropre). Le fait que prvalue!=xlvalue aide à réfléchir...
Il discute de l'implémentation de l'affectation par mouvement à partir de swap(). Il me semble se contredire un peu ici (accent sur la vitesse, mais...)
Exemple avec RVO et exemple avec construction puis affectation. Grosse différence! Mais la salle ne semblait pas la voir... RVO est mieux que move(). Conseil : don't break RVO (ou NRVO, le Named RVO). Entre autres, ne pas faire return move(x);.
Je ne savais pas celle-là, mais return cond? x : y; brise aussi RVO.
Le mouvement ne fait pas de magie. Si une donnée n'est pas externe à la classe, le mouvement n'est pas mieux que la copie.
À propos de noexcept, il montre quelques exemples. Surtout important pour le constructeur de mouvement et l'affectation de mouvement.
Le Perfect Forwarding permet d'alléger la tâche d'écrire une (ou deux, ou n) versions d'une même fonction relayant des paramètres vers une autre fonction. Il montre par contre qu'un constructeur prenant en paramètre une référence universelle remplace tout.
Exemples avec initializer_list et des {...}. Prudence : le constructeur acceptant initializer_list va avaler tout ce que l'on initialise avec des accolades.
La règle : « sois un programmeur ». Sois consciencieux, réfléchis avant d'agir, pense aux sémantiques visées, pense aux caractéristiques de qualité (vitesse de compilation, d'exécution, productivité, etc.)
Un swap() est souvent utile. Move et swap() vont typiquement de pair.
Il est préférable de ne pas écrire noexcept que de l'écrire erronément.
Question : que dit le standard sur les types Moved-From?
Michael Caisse : le standard ne parle pas des types, mais parle des attentes de la bibliothèque standard quand à ces types (les conteneurs veulent que les objets Moved-From soient Destructible et Assignable-To).
Question : std::swap() n'utilise-t-il pas le mouvement? Pourquoi l'implémenter en tant que méthode?
Michael Caisse : ce que je suggère, c'est d'y réfléchir.
Une personne argumente que, quand on commence à réfléchir au mouvement, et quand on n'est pas un tenant du Destructive Move, il faut réfléchir à un état par défaut, même si on ne voulait pas de constructeur par défaut. On échange sur le Zombie State post-Moved-From dans une optique de Destructive Move. On est dans une zone de problème ouvert et de débats ici; c'est un problème plus clairement pour les xlvalue que pour les prvalue.
Fin du contenu technique
Je n'ai pas appris grand-chose, mais ça me donne une idée de l'état des débats sur le Destructive Move, ce qui est déjà quelque chose.
Constat humain global : la plupart des gens du comité de standardisation sont de très chics types. D'excellents contacts humains avec Michael Wong, Walter E. Brown, Michael Caisse, etc. Les gens comme Howard Hinnant ou Gor Nishanov sourient tout le temps. Stephan T. Lavavej, Herb Sutter et James McNellis sont des humains franchement chouettes. Et ainsi de suite.
En me déplaçant entre les salles, j'ai continué à réfléchir à l'idée du Destructive Move et à ses implications. Aux gains de vitesse possibles dans un conteneur, et aux impacts sur les variables automatiques. Je pense avoir saisi un truc subtil que les tenants de cette position voient et qui m'avait échappé jusqu'ici. Il y a un gain net à faire avec cela, je pense, si le standard est adapté de manière telle que les conteneurs ne puissent pas supposer qu'on peut affecter à un objet Moved-From. En réutilisant la mémoire avec un new positionnel, par exemple. Ça valait la peine.
Dans la salle Fermat, pour le Keynote, le blues résonne (le band joue, comme à tous les matins jusqu'ici). On a enrichi l'offre de nourriture en ajoutant des bagels chauds et un assortiment de fromages, dont un au saumon, qu'on pouvait accompagner d'oignons rouges. Délicieux.
Jon Kalb fait les remerciements d'usage, comme chaque matin.
Le concours de Scott Meyers à propos de ses cheveux semble montrer que He-Man et Jeff Beck sont les meilleurs candidats.
Ils ont adapté le Bag Check de manière à pouvoir recharger les ordinateurs portatifs des gens qui le souhaitent. Chic!
Pour le voir en-ligne : https://www.youtube.com/watch?v=3SdSKZFoUa8
Ce matin, on parle d'envoyer du code C++ en mission critique dans l'espace. Le présentateur, Mark Maimone, a écrit une partie du logiciel qui contrôle le Mars Rover.
Début du contenu technique
On remonte au début des années 2000, un petit système embarqué, contrôlable à distance. Il nous montre trois modèles de Rovers : un de la taille d'un micro-ondes, le Opportunity Rover, et le Curiosity Rover qui est sur Mars depuis deux ans.
La distance entre la Terre et Mars implique un délai d'environ 22 minutes pour envoyer un signal. Grosse contrainte TR. Côté logistique, l'antenne utilisée pour envoyer des messages sert aussi à d'autres fins. Conséquence : environ un signal aller/ retour par jour.
Le plan d'une journée implique utiliser le bras pour prendre ou déplacer un objet, ou faire un peu de « téléscience »
Ils ont un simulateur qui permet de visualiser ce que l'appareil a fait la veille (animation accélérée).
Ils communiquent surtout à travers des satellites. Ça coûte moins cher en énergie.
L'équipe de conception complète : plus de 1000 personnes.
Faut un logiciel très résilient. Des tests à plusieurs niveaux, matériel comme logiciel, répétés très, très, très souvent.
C'est un système embarqué de l'an 2000, donc avec de petites ressources (disque, mémoire, vitesse d'horloge, etc.). La température sur Mars varie de plus de 100 degrés chaque jour (°F, je présume, mais je ne peux le jurer) alors faut que le tout soit résilient.
Complément d'information (merci à Pierre Prud'homme) : Il semble que la NASA utilise le système métrique depuis 1990 mais qu'il y a un flou artistique dans certains cas. Voir : http://www.space.com/3332-nasa-finally-metric.html
Dans le cas de la variation de température de 100 degrés, il s'agirait bien de degrés Celsius. "A summer day on Mars may get up to 70 °F (20 °C) near the equator, but at night the temperature can plummet to about -100 °F (-73 °C)" (http://www.space.com/16907-what-is-the-temperature-of-mars.html). Ouf!
Pour Rover, tout le code devait entre dans 32 Mo RAM, incluant le système d'exploitation. Sur Curiosity, ils ont eu un peu plus de place, mais pas beaucoup.
Leur système d'exploitation est VxWorks, avec mémoire partagée (prudence!)
Il y a beaucoup de C dans tout ça, mais aussi du C++, et on compile avec gcc.
Communication par passage de messages, avec files d'attente et sémaphores pour fins de synchronisation, en essayant de ne pas trop toucher à la mémoire partagée.
Un peu plus de modules, avec un programmeur responsable de chacun. Le présentateur s'occupait principalement de mobilité. Le logiciel auto-valide ses conditions d'utilisation: un message de mouvement ne sera pas traité si le système ne s'estime pas dans une position pour bouger sans danger
On utilise C++ surtout dans le traitement d'images, l'évaluation du terrain, la conduite, la détection des glissements, la vision stéréoscopique, la détection des zones à ne pas approcher, l'odométrie visuelle (parce qu'avec les roues, sur un terrain sablonneux et glissant, ça ne fonctionne pas bien), la planification des mouvements, etc. C'est rapide, c'est fiable, c'est testable.
Pourquoi a-t-on attendu aux années 2000 pour « faire voler C++ »? Les gens ont peur du changement, et la tradition utilisait autre chose. Cela dit, en soupesant les risques d'utiliser C++ en comparaison avec les bénéfices d'utiliser beaucoup de code testé sur le terrain dans une multitude de contextes, C++ a gagné.
La plus grande partie de la contribution de C++ a trait à la vision et au traitement d'images. Curiosity a 17 caméras! Certaines ont un angle de vue allant de 120 à 180 degrés, et elles sont très utiles pour essayer d'éviter les obstacles. C'est les CPU (il y en a deux) qui font le traitement.
Il y a des caméras à 45 degrés d'angle à peu près « à hauteur d'homme », pour que l'on sache mieux ce que verrait un humain sur Mars. Elles sont à très haute résolution.
Avec ces caméras, on peut mieux conduire sans endommager le Rover. On donne de petits ordres résultat d'une planification: avance de deux pieds, pivote à gauche de 10 degrés, etc. On y va avec prudence car il y a beaucoup d'incertitude. Chaque saisie d'images prend du temps, alors faut en tenir compte. Le Rover a une « vision » 3D du terrain (orange : Ok, jaune : peut-être; rouge : va pas là). Le Rover est généralement autonome dans ses mouvements, dû aux délais de transmission, mais on peut prendre le contrôle manuellement au besoin.
L'odométrie visuelle suit d'une comparaison continuelle d'images avant-après. Une analyse de traversabilité suit, puis une planification, puis l'action. On fait ça en boucle jusqu'à atteinte de l'objectif.
La traversabilité va comme suit: on prend plein de points dans les images, on trace un polyèdre à partir des points qui en font les sommets, et on identifie le meilleur chemin pour nous rapprocher du but tout en assurant la sécurité de l'appareil
Le Rover avance à une vitesse maximale d'environ deux pouces par seconde; une grosse journée, il avant ~140 mètres (en moyenne, c'est plus ~68 mètres par jour). À chaque 0,5-1,5 mètre, le Rover s'arrête, prend plein de photos, choisit un chemin... Pour pratiquer, ils l'ont fait suivre ses propres traces. Puisque les roches ne sont pas sphériques, et puisque l'évaluation est visuelle, il se peut qu'un meilleur chemin A→B ne soit pas le meilleur chemin B→A
Ils font du dynamic A* (D*) pour le Pathfinding (le Optimal Long Drive Planning) et des OctTrees. Le présentateur nous montre un film du robot qui cherche à atteindre un but inatteignable (dans la pièce d'à côté, derrière une porte) pour nous montrer comment il se construit une vision du monde et en vient à déterminer que c'est impossible
Les craintes initiales à propos de C++ étaient :
Ils se sont limités à Embedded C++ :
Ils ont écrit leur allocateur. Ils ont déterminé quoi faire un situation de manque de mémoire (leur choix : reboot). Supporte plusieurs Pools de mémoire. Offre des mécanismes de diagnostic. Se sont basés sur celui de VxWorks. Pas de collecte d'ordures, car (évidemment) pas de fuites! La cartographie d'utilisation de mémoire peut être téléchargée en tout temps
Le recours à cet allocateur, et la démonstration qu'il faisait le travail, a permis d'utiliser new et delete dans le code. Les diagnostics ont beaucoup aidé.
IIl faut faire des tests. Tests unitaires. S'assurer que les tests soient raisonnables. Valider le Code Coverage pour éviter les mauvaises surprises. Analyse statique par-dessus le tout.
Ils ont fait des simulations en plus de tout le reste, de même que des tests sur Terre dans des environnements analogues (autant que possible dans le contexte) à Mars et dans des espaces clos où les paramètres étaient contrôlés, un peu comme on le fait au Collège Lionel-Groulx pour nos projets de sessions 4 et 5 en informatique industrielle.
Quand la luminosité ne convient pas, le Rover ne bouge pas. Ça peut durer des jours, un mois parfois, s'il n'y a plus de lumière solaire ou s'il y en a trop. Ça fait partie de la vie.
À un moment, le Rover a cessé d'avancer. Ils se sont aperçus qu'il voulait « survivre »: il se trouvait dans une dénivellation de 60% et travaillait pour reculer!
Il mentionne que C++ est partout sur Terre aussi. La majorité des outils utilisés sur Terre pour accompagner le Rover, visualiser, planifier à distance, contrôler les caméras, etc. est en C++. C'est aussi C++ qui annote les données alors que le Rover se déplace (indique automatiquement les éléments dignes d'intérêt dans l'image).
Il y a du C++ sur d'autres vaisseaux aussi (il donne une courte liste pour donner un aperçu: détecteurs d'eau, satellites, outil de mesure des vents océaniques, de la salinité des mers, etc.)
Pour le futur, C++ prend beaucoup de place. On sait maintenant que c'est plus facile à valider du code C++ que du code C, règle générale (outre les mythes et les croyances), et on a commencé à aller au-delà du sous-ensemble défini par Embedded C++. Le prochain Rover, prévu pour 2020, est très C++. Le téléscope géant James Webb utilise C++.
Il n'y a toujours pas d'exceptions, mais on a accepté de l'héritage (pas de trucs qui impliquent du RTTI), des espaces nommés, et même la surcharge d'opérateurs.
Un des trucs qui a aidé pour Rover a été le constat que le Rover était au niveau du sol. Faire un erreur d'inexpérience ne l'aurait pas fait chuter pour s'écraser au sol ou terminer la mission.
On a commencé à utiliser UML comme générateur de code C++, et inversement.
Question : votre code source pour l'allocateur est-il disponible?
Mark Maimone : une version initiale l'est. À la NASA, cherche pour Gestalt (nom du projet)
Question : est-ce que C++ était un bon choix?
Mark Maimone : je suis content. J'aimerais que l'intégration avec le simulateur 3D soit plus facile. J'aime l'abstraction possible, mince et efficace
Question : quels sont les outils de validation que vous utilisez?
Mark Maimone : on utilise Purify et Valgrind, entre autres. On peut laisser aller notre mécanisme d'allocation de mémoire et retomber sur malloc() au besoin
Question : comment établissez-vous les frontières et les interactions entre les modules?
Mark Maimone : on a des équipes de test spécialisées, qui utilisent les modules dans des contextes spécifiques.
Question : comment se passent vos reboot pour être sécuritaires?
Mark Maimone : un Rover reboote volontairement plusieurs fois par jour. On définit a priori les passivations et les activations avec soin. Ça fait partie des procédures normales.
Question : comment collectez-vous les données scientifiques avec le Roverm outre les paramètres de navigation?
Mark Maimone : on a un bras (code C++ ici aussi), un spectromètre, une brosse, etc.
Question : des bogues intéressants?
Mark Maimone : y en a toujours On a un accéléromètre et un gyroscope, qui servent sans arrêt. Historiquement, on préfère le visuel (j'ai manqué le reste)
Question : pourquoi tant de visuel quand on a tellement d'autres types de capteurs? Et combien de lignes de code?
Mark Maimone : on utilise ce qu'on a. On devait prendre les images de toute manière, alors c'était une économie. De plus, chaque ajout matériel se fait refuser (poids!) à moins que ce ne soit une absolue nécessité.
Question : que demandez-vous de votre compilateur? Comment l'avez-vous choisi/ évalué?
Mark Maimone : c'est un travail d'équipe. On était à la fin des années '90. Collectivement, on avait confiance en gcc. Quand on remarquait un problème, on pouvait en tracer la source et faire un bug report.
Question : quelles sont les optimisations qui sont permises?
Mark Maimone : on a optimisé le module de vision, mais chaque module a ses propres contraintes. Quand ça fonctionne, on essaie de ne rien changer.
Question : quel sous-ensemble de C++ utilisez-vous sur Terre?
Mark Maimone : on n'a pas de restrictions, même pour les exceptions, quoique les gens demeurent frileux.
Question : utiliseriez-vous des templates pour fins de validation statique dans le futur?
Mark Maimone : moi, oui. Le problème est de convaincre les autres.
Fin du contenu technique
Fort intéressant, tout ça. Surtout pour mes étudiant(e)s en informatique industrielle au Collège Lionel-Groulx.
Petite jasette avec Michael Caisse sur la question du move destructif ou non, par la suite. C'est un problème délicat, pour lequel les gens qui ont des positions sont divisés pas mal 50-50. Je vais écrire un truc là-dessus quand j'aurai quelques minutes, le temps de placer mes idées et de voir les enjeux.
Un peu de temps sur l'heure du dîner pour organiser mes flûtes et retourner mes courriels. J'ai croisé Andrei Alexandrescu, en train de bavarder avec deux autres personnes. Quand même... Je ne pense pas qu'il présente quelque chose cette fois (triste, car il est très bon d'habitude), mais c'est chouette de le voir là.
Pour le voir en ligne : https://www.youtube.com/watch?v=YkiYOP3d64E et https://www.youtube.com/watch?v=Q5kyiFevMJQ
Pour l'après-midi, je vais assister à une présentation en deux temps par Alisdair Meredith sur les allocateurs standards de C++ 11, une bête qui a beaucoup changé entre C++ 03 et C++ 11 et pour laquelle il n'y a vraiment pas beaucoup de documentation.
Début du contenu technique
Alisdair Meredith est surpris de voir combien de gens ont déjà utilisé des allocateurs par le passé. Pour ceux qui s'en sont bien tirés, nous sommes moins nombreux.
On va explorer les ennuis d'allocateurs C++ 03 et la solution avec C++ 11, puis les applications. On va essayer d'y aller en profondeur. Alisdair Meredith est enthousiaste.
Un allocateur est un fournisseur de mémoire sur demande. On l'utilise comme détail d'implémentation pour un objet qui doit gérer des états entreposés dans de la mémoire allouée dynamiquement, typiquement un conteneur.
Le but est de le permettre sur une base par-objet, en fonction des besoins et du contexte. Par défaut, un allocateur doit être fourni pour les cas où il n'y a pas de besoins particuliers.
Quelques cas motivateurs :
Un exemple d'allocation sur la pile (Buffered-Sequential) :
Bénéfices :
Risques :
Étonnamment, plusieurs gens dans la salle cherchent des puces au design qui montrent qu'ils ne l'ont jamais codé... On est pas mal en avance dans nos cours, les ami(e)s!
Il donne quelques cas d'utilisation, p. ex. : construire une string dont on connaît la plus grande taille possible au préalable.
Exemple :
Dans le modèle de Bloomberg, les pointeurs retournés par un allocateur sont toujours « maximalement » alignés en mémoire. Le alignas du buffer est utile ici.
Question : pourquoi un char[N*sizeof(double)] alignas double plutout qu'un double[N]?
Alisdair Meredith : parce que le standard garantit que je peux manipuler la mémoire du bon type OU des char*, mais n'offre pas la même garantie pour les autres types (on l'a vu dans les Lightning Talks ce matin)
Alisdair Meredith relate que Bloomberg est entré dans le comité ISO parce qu'il y avait un problème avec les allocateurs.
Leur problème a trait aux types vocabulaires, ceux qui apparaissent dans les interfaces. On en veut un petit nombre. En ayant les allocateurs dans le type du conteneur, le vocabulaire est plus obscur, et on soulève des questions comme « que signifie copier un conteneur de T avec allocateur A dans un conteneur de T avec allocateur A'? ».
Plusieurs objets en C++ profiteraient d'un bon allocateur, mais ils sont dans le type et il est tard pour corriger cela. Des adaptateurs peuvent aider, mais ça reste complexe.
Une implémentation peut supposer :
Pour résoudre le problème :
template <class T>
using rebind_traits = allocator_traits<rebind_alloc<T>>;
Propagation des allocateurs? Un allocateur Stateful est lié à son objet dès la construction, et spécifique à cet objet. La propagation est contrôlée par allocator_traits, et par défaut, un allocateur ne se propage pas :
Les services sont les mêmes qu'avant, mais dans allocator_traits<A> :
Petite pause. C'est dense mais c'est exactement ce que je voulais. C'est clair, Alisdair Meredith a écrit la spécification (et la comprend du début à la fin), et c'est très bien expliqué (pour qui a les a priori pour suivre le propos, évidemment). Impeccable.
Le Graffiti Wall est pas mal plein. Des trucs ordinaires comme :
10 PRINT "Hello cppcon"
20 GOTO 10
...ou :
#define FIVE 7
...mais aussi un tribut aux présentations de la semaine comme :
using void_t = ???;
...ce qui constitue un hommage à la bombe conceptuelle lancée par Walter E. Brown hier.
C'est plein, plein de monde ici. J'ai croisé Gabriel Dos Reis tantôt; « toute une pièce d'homme, comme qu'y disent ».
Ensuite, exemple de déduction du type size_type à utiliser, utilisant SFINAE :
template <class ALLOC>
auto dispatch_size_type(...) -> typename ::std::make_unsigned<difference_type<ALLOC>>::type>;
template <class ALLOC>
auto dispatch_size_type(int) -> typename ALLOC::size_type;
template <class ALLOC>
using size_type = decltype(dispatch_size_type<ALLOC>::type{0});
Oh, que c'est amusant! SFINAE est l'outil le plus répandu cette semaine. J'ai validé avec Alisdair Meredith, et les fonctions unifiées sont là pour fins de lisibilité, pas pour une raison technique (ça aligne bien les trucs à l'écran).
Alisdair Meredith présente SFINAE comme une facilité nécessaire pour supporter des types dépendants dans des fonctions templates. On s'est aperçus avec le temps qu'on pouvait en profiter/ en abuser pour contrôler les mécanismes de résolution dans du code générique, et on a fini par l'automatiser avec enable_if.
Par exemple :
template<class T>
T::type *make_child(T *parent);
...compilera même si plusieurs types T n'ont pas de types internes ::type, car ces types ne seront simplement pas considérés.
using oui = char;
struct non { char _ [3]; };
template <class T> oui sniff_ptr(typename T::pointer *);
template <class T> non sniff_ptr(...);
template <class T> struct has_pointer { enum { value = sizeof(sniff_ptr<T>(nullptr) == sizeof(oui) }; };
On peut déterminer avec ceci et un peu de « métacolle » si allocator_traits<A> doit faire correspondre pointer à typename A::pointer ou vers typename A::value_type* qui est obligatoire pour allocator_traits.
Alisdair Meredith parle de decltype, de fonctions unifiées, de SFINAE généralisé (gros gain, ma découverte du colloque!) et d'alias templates avec using. Ça coûte moins cher à la compilation d'utiliser decltype et expression SFINAE que de générer des types intermédiaires
Avec C++ 14, on pourra alléger l'écriture encore un peu (make_unsigned_t<ALLOC> et autres).
Même mécanique pour les autres types, faut juste élaborer un peu. En particulier, ça prend pointer_traits pour définir les types internes ...::pointer, ...::element_type et ...::difference_type, de même que rebind<U> et la fonction statique pointer pointer_to(T).
Amusant :
using ptrdiff_t = decltype(declval<char*>() - declval<char*>());
Alisdair Meredith utilise quant à lui ceci (tsk!) :
using ptrdiff_t = decltype((char*)nullptr - (char*)nullptr);
L'appel à construct() pour un allocator_traits<A> est un peu lourd à imnplémenter mais pas compliqué. Il utilise une fonction auxiliaire do_construct() pour alléger l'écriture et fait du Perfect Forwarding, mais c'est assez similaire aux usages. La passe intéressante est un SFINAE de la forme :
...->decltype((void)a.construct(p, forward<Args>(args)...))
...pour choisir entre la version qui reçoit un void* et celle qui reçoit un T*. SFINAE embarque malgré le transtypage en void.
Dans un allocateur, des pièges :
Alisdair Meredith montre std::allocator traditionnel. Ouf!
Avec les allocator_traits, il reste beaucoup moins de trucs à implémenter :
Alisdair Meredith mentionne qu'on code encore « tout le kit » par souci de compatibilité avec C++ 98, mais ce n'est plus nécessaire techniquement
Il présente une implémentation complète de leur base de code à code ouvert. Dans son code, l'allocateur est une interface de délégation vers un pointeur sur une autre classe.
Question : c'est le modèle Bloomberg, mais pour le standard?
Alisdair Meredith : le modèle Bloomberg est la base de ce qui entrera dans le standard avec le Fundamentals TS. C++ 11 supporte allocator_traits, les nouveaux conteneurs s'en servent, et on peut utiliser le code présenté aujourd'hui tel quel. Le Fundamentals TS va faire une mise à jour des types d'allocateurs standards pour les mettre à niveau. Il manque des détails d'alignement en mémoire (Bloomberg présume toujours l'alignement maximal).
Question : quel est le gain de passer par un wrapper autour d'un pointeur?
Alisdair Meredith : effacement de types, indirection, polymorphisme. En plus, le select_on_container_copy_construction() retourne un allocateur par défaut (new / delete) sans qu'il n'y paraisse. Magie! Alisdair Meredith rappelle le souhait qu'a Bloomberg de réduire la surface de l'interface (le vocabulaire, dans leur terminologie).
Comment implémenter un conteneur, disons un tableau dynamique? Comme avant :
template <class T, class A = allocator<T>>
struct ZeTableau
{
using ALLOC = A;
using pointer = typename A::pointer;
pointer data_;
ALLOC alloc_;
// ...
Faut ajouter :
Question : devrais-tu détruire les objets en ordre inverse de construction quand un constructeur de copie d'élément lève une exception?
Alisdair Meredith : ce serait gentil mais c'est pas une obligation
Question : addressof(...) retourne un pointer, peut-on faire ++?
Alisdair Meredith : montre comment écrire la chose proprement. C'est subtil (attention aux effets de bord, par exemple séparer *ptr++ au cas où *ptr lèverait quelque chose)
Question : tu as mentionné que construct() pourrait servir à faire des trucs suspects comme entreposer un Cookie dans l'objet... Comment deallocate() en tient-il compte?
Alisdair Meredith : c'est l'allocateur qui en est responsable. C'est pour ça qu'on passe un nombre d'éléments plutôt qu'un nombre de bytes
Alisdair Meredith discute ensuite du trait uses_allocator<T,A>, qui dérivera de true_type si T utilise A, donc si typename T::allocator_type existe et est A, et dérive de false_type dans le cas contraire
Ce qu'on veut utiliser en pratique est scoped_allocator_adapter :
Concrètement :
namespace shm
{
template <class T> class shm_allocator; // faudra évidemment le coder :)
template <class T> using allocator = ::std::scoped_allocator_adaptot<shm_allocator<T>>;
template <class T> using vector = ::std::vector<T, allocator<T>>;
template <class T> using basic_string = ::std::basic_string<T, char_traits<T>, allocator<T>>;
using string = basic_string<char>;
}
// ...
shm::vector<shm::string> vs {"Yo", "man" }; // hello world des allocateurs :)
Ici, shm::vector<shm::string> est un alias pour (préparez-vous...) :
vector<
basic_string<
char, char_traits<char>, scoped_allocator_adaptor<shared_memory_allocator<char>>
>,
scoped_allocator_adaptor<
shared_memory_allocator<
basic_string<char, char_traits<char>, scoped_allocator_adaptor<shared_memory_allocator<char>>>
>
>
>
Ouf! Six chevrons fermants!
Plusieurs questions du public visent la mémoire partagée plutôt que les allocateurs (un peu hors sujet).
Alisdair Meredith parle de la différence entre écrire un vecteur spécialisé et utiliser un allocateur spécialisé. Le travail des allocateurs est complexe à faire mais simple à utiliser.
Question : et rebind sur un allocateur Stateful? Quel sens devrait-on lui donner?
Alisdair Meredith : rebind est utile quand on veut une même stratégie sur des types d'éléments distincts. N'oubliez pas qu'idéalement, les allocateurs partageront leurs états. Le constructeur de conversion fait partie du contrat d'un allocateur.
Question : devrait-on voir un allocateur comme une source de mémoire brute?
Alisdair Meredith : je dirais non. Notre allocateur est utile en partie parce qu'il connaît le type des éléments à allouer.
Qui doit savoir écrire allocator_traits?
Qui doit savoir utiliser allocator_traits?
Fin du contenu technique
Très solide présentation. Considérant le niveau de difficulté, difficile de faire mieux. Bravo!
Pause courte, parce qu'il y avait plusieurs questions. Les responsables de l'organisation ont raffiné un peu la fluidité du processus pour manger une bouchée; on attendait beaucoup moins cette fois. Le menu était le même que lors des autres jours, mais ça me va (j'aime bien le mexicain, et ça fait le travail en fin d'après-midi comme ça)
Je vais maintenant participer à une présentation portant sur l'abstraction logicielle des architectures parallèles. Sais pas ce que ça va donner, ne connaissant pas le présentateur, mais je vais peut-être trouver des idées pour mes cours de systèmes parallèles et concurrents, qui sait? C'est un beau risque.
Pour le voir en ligne : https://www.youtube.com/watch?v=BApBvh888-U
Joel Falcou a intitulé sa présentation (Priceless) Software Abstractions for Parallel Architectures. Au son, je dirais qu'il est français (il est affilié à l'INRIA, Institut National de Recherche en Informatique et en Automatique). À voir les gens dans la salle, je le suspecte de participer à Boost aussi.
La salle ne sera pas très remplie, puisqu'il ne s'agit pas d'un «gros nom» ici, mais on sera au moins une vingtaine, à l'oeil.
Début du contenu technique
Joel Falcou met de l'avant que les machines se compliquent rapidement pour des programmeurs, alors imaginez des scientifiques d'autres spécialités! Pourtant, profiter de la machine demande de profiter de ce matériel, de plus en plus parallèle.
Il cherche à offrir des métaphores expressives pour le parallélisme, à la lueur de ce que l'on fait pour la programmation séquentielle. On ne veut pas déranger les gens outre-mesure. Ses buts, donc :
Son équipe et lui ont défini des bibliothèques C++ sous la forme de EDSL, et utilisent des squelettes parallèles à titre de composants.
L'idée est de réduire les risques inhérents à la programmation parallèle, et contourner le fait que les modèles de bas niveau sont peu composables. Ils se situent près des acteurs avec leurs squelettes
Les squelettes [COLE 89] représentent une généralisation de patterns, et peuvent être assemblés. Sur le plan fonctionnel, ce sont des fonctions de type high-order
Data parallel : map, fold, scan. Map/Reduce combine map et fold
Task parallel : par (exécution indépendante), pipe (dépendances à travers le temps), farm (maître / esclave)
Il est prouvé qu'on peut raisonner sur la composition de squelettes pour prouver des propriétés d'un programme. La sémantique est fixée, l'implémentation est libre
Il procède par programmation générative, et dérive l'application des spécifications
On peut faire ça de plusieurs manières, incluant de la métaprogrammation. En C++, on fait ça avec des templates. Il fait un EDSL par Expression Templates et provoque une implémentation générique consciente de ses optimisations
Il exploite l'AST du compilateur de deux manières, soit pour générer le code (niveau expression) et pour optimiser de manière intra-procédurale (niveau fonction)
Cet injection de DSL n'ajoute pas d'outils supplémentaires dans la toolchain. Ça se passe dans le langage. La présence de la sémantique à même le code permet de résoudre une partie des problèmes de manière statique.
Leur objectif était de démontrer que ça fonctionne, à bas prix, et que c'est applicable. Ils ont fait quelques contributions à Boost par le fait-même.
Entre autres, NT2 qui offre une interface de type Matlab et utilise Boost.SIMD et des squelettes parallèles sous la couverture. Boost.SIMD peut utiliser TBB, OpenMP ou autre chose en fonction des options de compilation.
Il donne un exemple : tu écris du Matlab (fichier .m), tu le passes dans leur outil, t'as du code C++, tu t'en sers. Pour y arriver, ils extraient un squelette de l'arbre d'expression du code Matlab.
Il y a des limites au fork/join :
Ils sont passés à des acteurs :
Ils obtiennent une sorte de pipeline implicite. Ça leur donne des future.then
Une petite démo de soustraction de background et de détection de mouvement
Un des trucs qu'il fait est une décomposition LU en parallèle. Son schéma donne un aperçu des points de rendez-vous des futures
Il vante leur pratique. Je suis d'accord avec lui, mais leur interface de programmation me semble rugueuse pour des non-programmeurs.
Il recommande des concepts pour le parallélisme avec C++ : DataParallel, Associative, etc. Puis il souhaite faire de la séparation de squelette sur la base de concepts, ce qui permettra une validation statique de certaines propriétés fonctionnelles.
Joel Falcou émet le souhait que SIMD soit intégré à même le langage. Il émet aussi le souhait d'un ast_of sur la base de sizeof. À réfléchir.
Lors des questions/ réponses, Joel Falcou lance des attaques frontales contre C++, disant qu'il migrerait à D ou à Julia immédiatement si ses clients étaient prêts à l'accepter. Hum.
Question : avez-vous réussi à convaincre des scientifiques élevés avec Fortran d'utiliser NT2?
Joel Falcou : nous avons eu un succès « local », auprès de savants et de clients du monde de la finance. Pas des experts de programmation.
Question : comment vous comparez-vous à Eigen pour le calcul linéaire?
Joel Falcou : ce sont nos ennemis! Ils sont plus rapides que nous sur de petites matrices. Nous savons quoi faire pour les battre mais nous n'avons pas investi l'énergie pour le faire.
(pour le reste, c'est une discussion technique de spécialistes de calculs matriciels)
Fin du contenu technique
C'était intéressant, mais moins dans mon créneau, et pour l'aspect programmation, j'étais familier avec les techniques utilisées. J'aurais aimé que le côté Priceless soit plus exploité; c'est les métaphores qui m'intéressaient en priorité.
J'ai pris le temps suivant cette présentation et précédant le panel avec les gens du standard pour formater le texte de la journée, que vous venez probablement de lire si vous en êtes rendu(e) ici. Il y avait du contenu, et je n'aurais pas voulu oublier des éléments clés.
Quelques présentations auxiliaires ont lieu en périphérie : UndoDB, qu'on a vu brièvement dans un Lightning Talk, la bibliothèque audio JUCE que j'ai vu des étudiants utiliser par le passé, ce genre de truc.
Pour les voir en ligne : https://www.youtube.com/watch?v=7P536lci5po
La séance de 20 h 30 est un panel avec quelques-uns des principaux membres du comité de standardisation du langage C++, soit John Lakos, Chandler Carruth, Herb Sutter, Peter Sommerlad, Alisdair Meredith, Howard Hinnant, Michael Wong, Pablo Halpern, Stefanus Du Toit, Stephan T. Lavavej, Walter E. Brown, Nevin Liber et Marshall Clow. Jon Kalb semble être le modérateur; je ne sais pas s'il siège formellement sur le comité lui-même.
Il y a de l'ambiance dans la salle. J'ai l'impression que plusieurs ont pris une bière sur l'heure du souper .
Les membres se présentent, et mettent de l'avant leurs dossiers favoris :
Michael Wong explique son rôle en tant que représentant d'un pays (le Canada). Il demande à rencontrer les canadiens présents au colloque.
Jon Kalb demande ce qui fait réagir les gens quand on parle d'être hôte d'une rencontre. Herb Sutter explique.
Question : si je fais une fonction T identite(T x){return x;} et si j'appelle identite(identite(...(identite(x))), pourquoi y aura-t-il des mouvements plutôt qu'une élision?
Howard Hinnant : je ne sais pas pourquoi ce n'est pas là, sinon qu'on ne savait pas comment le faire. Faudrait le faire.
Herb Sutter : si on passe un objet par valeur, les mouvements multiples deviennent un seul mouvement.
Chandler Carruth : un dénommé Richard Smith étudie spécifiquement cette question
Question : peut-on faire un alias aux données plus proprement qu'avec memcpy()
Chandler Carruth : je ne pense pas que le standard va t'aider à faire plus d'alias. Je pousse pour réduire les alias, pas les augmenter.
Marshall Clow : quand tu regardes memcpy() dans ton code, tu vois « appel de fonction », mais c'est pas nécessairement ce qui se passe.
Question : ce qui m'agace est d'expliciter ce que je fais par un commentaire.
Chandler Carruth : je pense que memcpy() dit exactement ce que tu fais quand tu l'appelles.
Question : est-ce que memmov() est aussi acceptable que memcpy()?
Chandler Carruth : oui
Marshall Clow : alors fais un memmov() sur l'objet lui-même
Question : pourquoi {1,2,3} n'a-t-il pas de type?
Stephan T. Lavavej : le coeur du langage n'aime pas les bibliothèques
Chandler Carruth : quel serait le type d'une liste non-homogène? Ce serair un littéral tuple. Ça affecterait les fondements du langage
Question : a-t-on besoin de plus de compilateurs? De moins de compilateurs?
Herb Sutter : ce type de question est posée souvent, et l'arrivée de Clang montre qu'on a toujours besoin d'innovation
Chandler Carruth : c'est difficile d'implémenter un compilateur C++. Je ne sais pas s'il y en aura d'autres. Je ne sais pas comment Clang y est arrivé. Ça coûte une fortune à faire. Vive la diversité, cela dit! C'est important aussi qu'on ait plusieurs implémentations pour comparer les résultats et trouver nos erreurs.
Michael Wong : je suis souvent d'accord avec Chandler Carruth, mais j'ai travaillé sur deux compilateurs déjà et j'observe un nouveau compilateur aux dix ans à peu près. Ça prend de la diversité, de l'innovation, du renouvellement
Herb Sutter : ça n'en prend pas nécessairement beaucoup, mais quelques-uns est mieux qu'un seul. Pas juste pour les compilateurs, d'ailleurs; en général.
Question : y en a-t-il parmi vous qui s'intéressent aux graphiques de haut niveau? (personne sur la scène... Chandler Carruth : le comité comprend 80 personnes, pas juste nous!)
Chandler Carruth : il nous faut des spécialistes du monde du jeu. On aimerait les voir sur le comité avec nous.
Lawrence Crowl : C++ ne peut supporter tous les types d'applications, mais on peut offrir de bons niveaux d'abstraction pour que le monde du jeu puisse développer des abstractions de qualité dans son domaine
Walter E. Brown : aucun membre du comité n'est payé pour être là. Les employeurs acceptent qu'on soit là, mais nous sommes bénévoles. Être membre d'un comité ISO, aux États-Unis, coûte 1200 USD. Nous voulons toujours plus d'aide! Le standard C++ 14 fait autour de 1300 pages, dont environ 400 forment le langage, le reste des bibliothèques. C++ est petit en comparaison avec la compétition. Nous voulons de nouvelles bibliothèques, mais il faut qu'elles soient bonnes!
Michael Wong : si vous ne pouvez venir au comité, rejoignez le représentant de votre pays. Je veux comprendre vos besoins.
Pablo Halpern : nous avons plus d'implémenteurs de compilateurs et de bibliothèques qu'autre chose dans le comité présentement. Cela dit, des programmeurs qui utilisent le langage, c'est précieux à avoir sur le standard! Un truc comme http://isocpp.org nous aide à mieux rejoindre les gens. Nous voulons comprendre vos besoins.
Nevin Liber : je suis là parce que certaines communautés (la finance, pour moi) sont sous-représentées.
Marshall Clow : le comité de standardisation n'écrit pas de logiciel. Nous écrivons des spécifications, qui seront ensuite implémentées.
Question : j'ai entendu qu'il y aurait une proposition pour standardiser l'ordre d'évaluation des paramètres. En portant Clang sous Windows, j'ai voulu me conformer à l'ordre de MSVC pour harmoniser l'ordre de création et de destruction des objets, et j'ai dû changer ce que Clang faisait...
Herb Sutter : sous x86, nous sommes de droite à gauche, mais sous ARM nous sommes de droite à gauche. On envisagera peut-être une standardisation partielle...
Chandler Carruth : je suis contre. Le code qui dépend de cela est du code fragile. Je préfère garder cela non-spécifié pour éviter que les gens écrivent du code qui en profite.
Question : j'aime pouvoir écrire >> dans un template sans insérer un espace entre les chevrons, mais j'ai découvert que je devais insérer un espace si >> est suivi par =...
Stephan T. Lavavej : je ne m'en était même pas rendu compte (il relate un bogue de trigraphe dans le passé).
Michael Wong : les trigraphes vont partir parce que la majorité des gens sont contre. Je suis un de ceux qui sont fortement contre, dû aux systèmes EBCIDIC pour lesquels certains fichiers d.'en-tête ne peuvent être représentés sans trigraphes. Ma compagnie (IBM) est préoccupée car cela endommage la portabilité pour plusieurs de nos clients. Je devrai parler pour mon entreprise à la prochaine rencontre. Je suis très ASCII, pas très EBCIDIC, mais le monde ne s'arrête pas à moi. C'est controversé.
Herb Sutter : ceci est représentatif de toute proposition. Cela explique l'importance des rencontres du comité et de la diversité d'opinions.
Stephan T. Lavavej : pour un truc comme >>=, il y a un système de Core Issues ou de Library Issues selon le cas. C'est plus simple que d'écrire une proposition pour le standard.
Stefanus Du Toit: si c'est bêtement une erreur de formatage, le source LATEX du standard est sur Github
Herb Sutter : Stefanus Du Toit est responsable pour avoir fait du standard un produit à code ouvert!
Question : on a vu plusieurs langages « systèmes » récemment, comme D ou Rust, un nouveau Fortran, un nouveau Ada. Est-ce qu'il vous arrive de vous inspirer de ces trucs ou travaillez-vous plus en isolation?
Chandler Carruth : ce n'est pas le comité qui fait le design de quoi que ce soit. Heureusement : le design par comité, traditionnellement, n'est pas génial! Nous recevons des propositions écrites et nous les examinons.
Pablo Halpern : plusieurs propositions sont des versions C++ de particularités d'autres langages.
Lawrence Crowl : il ne faut pas oublier les implications profondes des destructeurs de C++. Plusieurs caractéristiques d'autres langages n'ont pas de sens, ou d'intérêt, en C++. De même, les fermetures existent depuis longtemps, mais intégrer l'idée dans le monde de C++, a demandé beaucoup de travail original.
Nevin Liber : nous travaillons très fort pour assurer l'harmonie du langage.
Question : à propos des comparaisons et de tuple. Pourquoi n'en a-t-on pas?
Herb Sutter : on a eu un article. Bjarne Stroustrup adorait l'idée mais détestait l'implémentation. Bjarne Stroustrup pense apporter une solution générale à la prochaine rencontre, couvrant toutes les opérations du genre.
Marshall Clow : il est rare qu'un papier passe du premier coup. Ça n'arrive presque jamais.
Alisdair Meredith : Michael Wong a apporté quelque chose que je détestais, j'ai soulevé plein de défectuosités, mais il revenait et revenait et revenait... et éventuellement, il a réglé tous les bogues et j'ai dû m'avouer vaincu!
Peter Sommerlad : nous ne pouvons pas tout comprendre. Aidez-nous à vous comprendre. Pensez marketing!
Walter E. Brown : il arrive qu'on reçoive des bijoux de gens qui ne sont pas sur le comité (il relate un cas récent).
(je dois quitter pour prendre l'autobus, mais c'est très intéressant)
Ce sont de très grosses journées, mais je vais en avoir pour des mois à décanter toute l'information absorbée pendant ces quelques jours...
En allant vers le centre de congrès ce matin, quelques trucs remarqués :
Ma chic maman que j'aime m'a écrit un petit mot ce matin, faisant valoir que je m'ennuie de « tout le monde sauf elle ». Ben non, je ne m'ennuie pas de tout le monde :) Je blague, évidemment, mais c'est certain que ma petite famille, que je vois tous les jours normalement, ça fait un gros trou dans ma vie quand je ne les vois pas pendant presque une semaine
Quelques Lightning Talks ce matin :
Roland Bock : Wishful Thinking (voir https://www.youtube.com/watch?v=RpMVHQzhWIE). Il veut changer la nature des noms en C++. Il veut passer de :
struct sample { int i; }; sample x;
...à...
template <class T>
struct sample { T id; };
using ze_sample = sample<int>;
...à ceci :
template <class T, T X>
struct sample { T {X}; };
using ze_sample<char, 'a'>;
Il discute aussi de CRTP et de mixins, dans un cas où Parent<Enfant> voudrait traiter *this comme un Enfant& avant la déclaration de la classe Enfant. C'est intrigant.
My Four Year Bug : l'auteur travaillait sur une vieille application MFC, dont une méthode de 19000 lignes, qu'on lui avait confiée en 2008. Lors du passage de VS2005 à VS2010, tout va bien. Puis, VS2012... Dans l'application, le MRU (fichiers récents) était brisé. Ils l'ont « patché ». Arrive VS2013, le bogue revient. Fouille, ne trouve pas. Ne trouve pas. Demande de l'aide, ne trouve pas. N'arrive pas à faire une preuve de concept. C'était un simple changement silencieux de nom dans une fonction MFC, chez Microsoft. Ouch. Voir https://www.youtube.com/watch?v=9p00t65Beig
Ken Smith (son fond d'écran est un Crash Screen de Linux). C++ Hardware Register Access (voir https://www.youtube.com/watch?v=lrrQaa_-hzU). Option 1 : accès à une variable qualifiée volatile et à une adresse fixe (écriture dans de la mémoire brute). On comprend, mais on peut faire des niaiseries. Option 2 : bitfields, champs volatiles, adresse fixe. Évite des masques. Faut savoir comment le compilateur aligne les trucs. Il propose :
template <class mut_t, uuint_32 addr, int offs, int width>
struct reg_t
{
// pas eu le temps
};
Il ajoute des services pour lire et écrire. Ça lui permet entre autres de distinguer à la compilation les registres Read-Only des registres Read/Write. Plusieurs avantages (clair, sécuritaire, mappe directement aux spécifications, permet de simuler plus facilement les registres en période de tests, de profiler, de logger, etc.). S'optimise avec constexpr et tout. Sa preuve de concept est sur https://github.com/kensmith/cppmmio
Karl Niu, Project Manager du Front-End de Visual Studio. « Arriver aux concepts par un autre chemin » (voir https://www.youtube.com/watch?v=y1ir1B6QiE4). Les concepts, c'est cool, peu importe le chemin qui y mène. Ses idées :
Il tisse un lien entre Haskell et la métaprogrammation en C++ avec des templates. La différence est syntaxique
Boris Koplackov : C++ a besoin d'un Package Manager (voir https://www.youtube.com/watch?v=nshzjMDD79w). Il disserte sur la mécanique actuelle, trop lourde et qui nous fait perdre du temps. Je ne suis pas difficile à convaincre
Question : qu'est-ce qui manque avec les gestionnaires existants?
Boris Koplackov : à propos de nuGet, c'est propriétaire et Closed Source.
Pour le voir en-ligne : https://www.youtube.com/watch?v=tOxi-cH7n8A
Je passerai la prochaine heure avec Fedor Pikus, qui propose une présentation sur la visualisation de l'échelonnabilité (Scaling; je n'ai pas trouvé mieux que ce néologisme) dans les programmes concurrents (le sous-titre est Where Did My Performance Go?). Je pense que ça va être utile pour plusieurs de mes cours.
Avant la présentation, Herb Sutter (qui est ici, ce qui est raisonnable étant donné son intérêt pour le parallélisme et la concurrence) joue un peu de piano. Fort agréable.
À l'usage, Fedor Pikus change rapidement de diapo alors je manque des trucs de temps en temps. C'est malheureux.
Début du contenu technique
Fedor Pikus compte surtout parler de profilage de programmes concurrents : comment obtenir un profil détaillé et utile d'un programme concurrent? (avec plusieurs sous-catégories, p. ex. : pourquoi en a-t-on besoin? Crée-t-on de nouveaux problèmes? etc.)
Suppositions et limites :
Pourquoi mesurer ce genre de truc? Si tu code en C++, il devrait aller de soi que tu sois préoccupé de vitesse
Faut mesurer; nous sommes mauvais pour estimer les caractéristiques de performance d'un programme. De plus, on dépend de plusieurs bibliothèques alors c'est difficile de bien juger avec de l'information partielle
Pourquoi écrire des programmes concurrents? (bon, on sait ça)
Il existe des profileurs, traditionnels et pour programmes concurrents
Dans un programme concurrent, l'ordre d'exécution n'est pas connu mais influence la vitesse systémique (il montre quelques exemples pour mettre de l'avant qu'il n'y a pas nécessairement de relation directe entre vitesse et nombre de threads)
Il montre une visualisation graphique de la contention sur la base des valeurs tirées (nombre de threads, temps d'exécution réel, CPU Time; si on a System Time et User Time, on peut déduire qu'on passe pas mal de temps en attente d'un verrou)
On veut un microscope dans nos threads (un threadoscope!)
On veut savoir :
Un exemple où le temps total ne change pas (ni réel, ni CPU) quand on augmente les threads, on déduit un calcul sériel (pipeline)
Un exemple où le temps réel ne change pas mais le CPU augmente linérairement avec le nombre de threads, on ne voit pas de verrous, on parle d'un programme Memory-Bound et d'une bande passante mémoire saturée
Fedor Pikus cherche un pointeur lumineux, on n'en a pas; quelqu'un dit nullptr; on se bidonne un peu.
Fedor Pikus nous montre un graphique où on voit bien les paliers pour les accès aux caches L1, L2, L3 et à la mémoire vive
Fedor Pikus rappelle l'exemple de Herb Sutter lors du jour 1 avec plusieurs producteurs et plusieurs consommateurs, et relate les caractéristiques des divers exemples (des termes comme Negative Scaling apparaissent). En visualisant les événements (calculs, mises en attente), on peut déduire ce qui se passe. Réduire la taille les sections critiques aide.
La visualisation des lignes du temps est un outil pédagogique intéressant.
Comment instrumenter le programme? On commence par utiliser un profileur existant (Visual Studio, VTunes, Sun Collector, etc.). Ça génère pas mal de données, mais c'est instructif et parfois, ça suffit.
Mesurer peut affecter le comportement du programme (concurrence, TR)
Collecter de plusieurs threads peut provoquer des conditions de course
Entreposer les données sur disque (il peut y en avoir beaucoup!) provoque des E/S et affecte les métriques
Quelles sont les options?
Que veut-on collecter?
On veut collecter ce qui est nécessaire, sans plus, et le faire efficacement
Fedor Pikus crée un collecteur RAII
Que devra-t-on collecter? On a toujours besoin du temps réel d'exécution, du task id et du thread id; on a presque toujours besoin de nesting depth, temps CPU par thread; (j'ai manqué les « souvent » et les « parfois »).
Faut entreposer tout ça en mémoire (overhead!)
Faut des mesures de temps à haute précision
L'allocation de mémoire doit être Thread-Safe, peu coûteuse et minimalement disruptive. Heureusement, la nôtre est spécialisée, et peut être libérée en bloc (Memory Pool, alloué LIFO, mais faut être Thread-Safe)
Fedor Pikus décrit son collecteur (utilise plusieurs int64_t, une minuterie et quelques pointeurs)
La profondeur du collecteur est partagée par les divers Collector du même thread (donc pas de course!), incrémentée par le constructeur du Collector et décrémentée par le destructeur
La mémoire du profileur doit disparaître... mais faut que le profileur y ait accès. Le profileur voit la même mémoire que les Collector (le même Pool), mais peut accéder les blocs qui ne changent plus comme s'il lui appartenaient
En pratique, on a de la mémoire virtuelle ( bytes théorique sur x64... 128 To!). On peut l'utiliser comme si elle était presque infinie... Tant qu'on ne l'utilise pas toute en même temps!
Pour être efficace, il joue avec le Memory Mapping de la machine. Un accès en lecture ou en écriture donne une page pleine de zéros (ça ne fait pas ça avec malloc() car on n'est typiquement pas le premier client)
Sous Linux, madvise() permet de la redonner au système, mmap() permet de mapper des adresses logiques sur une zone de mémoire virtuelle, munmap() la redonne au système. La mémoire n'est pas « réelle » si elle n'est pas accédée. On fait correspondre ces services aux méthodes du Collector
Le truc facile est de flusher le Memory Pool complet. Il se peut qu'on doive faire un msync(). Ça bloque en écriture sur disque, mais sur un thread auxiliaire. Avec 32 threads sur 32 coeurs, il supporte un événement par ≈100 μ, par thread. Les appels de mmap() sont extrêmement efficaces
Il peut y avoir des problèmes, si on manque de mémoire par exemple, mais c'est rare
Fedor Pikus montre un exemple dans son code. Il montre son interface personne/ machine (du CSS + HTML sur Chrome). Du beau travail, très visuel, avec des MouseOver
Puisque le disque reçoit chaque seconde les écritures ([m]sync()), il peut même lire les données pendant qu'elles sont accumulées
Pour aller plus loin, on peut pré-traiter les données un peu. Par exemple, si on veut profiler new / delete, on va avoir trop de données, va falloir faire quelque chose avant de les montrer. Ça fait trop de données pour être utile
Question : peut-on faire des trucs avec ceci que tu ne peux faire avec VTunes?
Fedor Pikus :: c'est paramétrable en fonction des besoins
Question : aurait-on pu utiliser du thread-local storage pour le thread id?
Fedor Pikus : on aurait plusieurs fichiers
On peut utiliser mmap() sans fichier (fd de valeur -1). Pour la mémoire new/delete, on peut se faire un petit TaskRecord
Que fait-on quand un seul thread ne suffit pas? On en prend deux, mais ça devient subtil. Un pipeline peut aider
La programmation sans verrous est plus facile quand la mémoire est infinie. On peut faire comme-si, parfois
Question : livrez-vous le code avec les Collecteurs?
Fedor Pikus : oui, mais désactivés
Fin du contenu technique
C'était amusant, mais ça donne surtout le goût d'en écrire un «maison» En format réduit, ça pourrait être un bon travail pratique. À réfléchir...
Bon, j'ai fait une folie et je me suis acheté un livre. A Tour of C++, le dernier livre de nature pédagogique de Bjarne Stroustrup. C'est supposé faire une introduction au langage en 180 pages. Je n'apprendrai sans doute rien sur le plan technique avec ça, mais pour la démarche pédagogique, ou comme document d'appoint dans certains cours, ça risque d'être utile. En plus, mon voyage de retour (de demain soir à samedi matin) s'annonce loooong... Les dames de la librairie, qui sont avec nous depuis le début de la semaine, n'en avaient plus de copie hier, et on vu que je le cherchais alors elles m'en ont gardé une copie personnelle ce matin. C'est très gentil!
Je suis arrivé trop tard dans la grande salle pour avoir des croissants, mais je vais m'organiser avec une pomme et du café.
Pour le voir en-ligne : https://www.youtube.com/watch?v=rX0ItVEVjHc
Jon Kalb fait son petit laïus du jour. Il y aura du covoiturage demain au besoin (je vais regarder ça). Il semble aussi que Michael Wong est disposé à faire une rencontre informelle tantôt que les graphiques à haute performance. Je vais aller parler avec lui, je pense bien
Le Keynote aujourd'hui touche au monde du jeu (en fait, j'ai trois présentations directement associées au monde du jeu sur mon agenda, avec les deux de nos amis d'Ubisoft cet après-midi). Un dénommé Mike Acton qui dissertera sur le design orienté données, donc sur l'impact de l'organisation des données sur l'efficacité des programmes. C'est un sujet que je connais bien, mais des fois, juste de voir comment un autre le présente, ça donne des idées.
Un présentateur dont le nom m'échappe vient présenter Mike Acton. Un beau profil!
Début du contenu technique
Mike Acton est directeur technique chez Insomniac Games. Il explique ce que fait une équipe de développement de moteur de jeu, et explique que c'est le créneau le plus exigeant côté vitesse dans un jeu. Ils font aussi le développement des outils.
Ce qui compte pour eux :
Leurs langages :
Ils n'envoient pas de jeux sur Mars, mais...
Mike Acton y va par affirmations brèves :
Le data-oriented design n'est pas nouveau, mais est une réaction à la culture de C++, et à trois mensonges qu'elle a créé, soit :
Il poursuit avec ses phrases succinctes :
Ces trois mensonges nous nuisent sur le plan de la vitesse, testabilité, « optimisabilité », etc.
Il donne un exemple, celui du recours à un dictionnaire clé/valeur. C'est un mauvais réflexe. Il suggère que l'accès à la valeur est moins fréquent, sur le plan statistique, que l'accès aux clés. En joignant les deux dans une paire, on se nuit (c'est le pire cas) parce que la valeur se ramasse dans la cache même si on n'en a pas besoin. Grouper les clés ensemble et les valeurs ensemble donne une meilleure Cache Coherence
Le compilateur peut-il le faire pour nous? Non, c'est une caractéristique d'un problème spécifique. Il lui manque du contexte
Mike Acton compare la latence des pires instructions machines (FATAN par exemple) à la latence associée avec celle d'une Cache Miss, pour mettre en relief l'impact de cette dernière. Il montre ensuite une animation comparative d'un accès L1, L2 et RAM. À l'échelle. C'est punché! Pour vous dire, les gens dans la salle ont applaudi quand le petit carré représentant l'accès en RAM a fini par atteindre la ligne d'arrivée!
Sur une machine sans cache L3, la Cache L2 est le dernier rempart pour avoir du code rapide. Ainsi, un L2 Cache Miss est le cas le plus dommageable sur le plan de la vitesse d'exécution
Mike Acton montre le code d'un calcul de racine carrée, en C, puis en assembleur, et montre qu'on peut analyser les impacts sur la Cache. Il montre aussi que dans le code généré, le compilateur peur régler environ 10% de nos problèmes d'accès aux données. Il reste un 90% qui revient entre nos mains. Il fait ensuite une analyse du code machine généré pour chercher à faire mieux manuellement, en restructurant les données pour améliorer les accès à la cache. Ça lui donne un gain de vitesse par un facteur de 10.
Ne pas porter attention aux performances, ce n'est pas de l'ingénierie, c'est du dogme. On ne peut échapper à la réalité pour faire un travail correct
Autre exemple: mettre un bool dans une struct. Présumons-le même « paquetés », pour éviter de perdre de l'espace. L'accès à un byte demande d'ajouter des instructions machines pour le « sortir » d'un mot mémoire et impacte la qualité de l'utilisation d'une Cache d'instructions
Les accès générés par le compilateur sont faits de manière très inefficace, très conservatrice. Il montre le code de MSVC, le code de Clang. La qualité de l'un par rapport à l'autre varie selon les extraits. Déplacer des instructions dans et hors d'une répétitive impacte beaucoup la qualité du code généré
Certaines manoeuvres montrées par le présentateur s'adressent à n'importe quel type de membre d'une classe. Entre autres, les compilateurs raisonnent mieux sur des variables locales que sur des entités accédées indirectement.
Bon truc : tu veux savoir si un if est pris souvent ou non? Affiche '#' dans le cas oui et ' ' dans le cas non, ça te donnera un écran avec peu ou beaucoup de trous. Un indice visuel de la fréquence de l'un et de l'autre. Ça donne un estimé de la densité informationnelle
Les constructeurs complexes sont un signe que l'on se tire dans le pied
Le meilleur code n'est pas exécuté, ou n'est tout simplement pas là. Rien n'est plus rapide que de ne rien faire (truc que j'ai appris de François Jean!)
Un des trucs est de déterminer rapidement le contexte pour éliminer les branchements subséquents et faciliter le raisonnement
Mike Acton montre comment réorganiser une struct pour réduire les Cache Misses
Il montre un cas de surcharge d'opérateur, qui implique plusieurs sous-opérations sous la couverture, et explique en quoi cette absraction complique le raisonnement. Il insiste aussi sur le fait que derrière l'abstraction, il est possible que l'on n'ait pas le contexte suffisant pour optimiser le travail
Impact positif : l'organisation des données facilite l'entretien et le raisonnement.
Mauvaise nouvelle : il est plus facile de mal programmer que de bien programmer
Question : vous n'utilisez pas la STL et tout le tralala. Comment gérez-vous la duplication de code que vous générez en écrivant manuellement du code semblable pour plusieurs types?
Mike Acton : critique les templates comme un mauvais générateur de code. On n'en a pas besoin (eh ben)
Question : avez-vous écrit des outils pour identifier ces problèmes, et si oui, êtes-vous ouverts à les partager avec nous?
Mike Acton : examiner l'accès à la cache se fait bien avec un profileur
Question : si j'ai beaucoup de code à adapter, comment procéder pour l'adapter?
Mike Acton : prend les fonctions une à la fois et sois patient
Question : on aimerait pouvoir abstraire nos problèmes pour que le code soit portable, disons Linux et Windows. Optimiser pour une machine entraînerait un coût d'ingénierie important. N'y a-t-il pas un point où l'investissement en temps dépasse les retours?
Mike Acton : ce type de pensée donne des trucs comme Word qui prend 20 secondes à démarrer
Question : qu'en est-il de la portabilité? Vous supportez plusieurs consoles, non?
Mike Acton : on optimise pour un ensemble fini de plateformes
Question : et si on écrit un jeu PS3 puis on doit le livrer sur PS4?
Mike Acton : le problème est le mouvement PS3→PS4. On peut ignorer le problème et avoir moins, ou raisonner un peu plus pour risquer des gains
Question : ce que je comprend en regardant vos exemples est qu'ils se raisonnent bien quand on a des données simples, typiquement des tableaux, mais si on a autre chose?
Mike Acton : oui, un problème difficile, c'est un problème difficile
Question : devrait-on changer les algorithmes si les données changent?
Mike Acton : différentes données, différent problème. Oui!
Question : étant donné votre perspective sur C++, pourquoi ne pas utiliser C?
Mike Acton : si j'avais C99, peut-être.. C'est culturel
Question : profitez-vous de trucs de C++ 11, comme la sémantique de mouvement?
Mike Acton : oui, ce sont des outils, mais il y a plusieurs trucs de 1998 qu'on n'utilise pas
Question : que gagnez-vous en utilisant C++ plutôt que C?
Mike Acton : c'est culturel... Je ne suis pas la bonne personne pour répondre à cela... C++ est mieux supporté
Question : passer de C++ 03 à C++ 11 a-t-il eu un impact sur le code généré?
Mike Acton : pas vraiment
Question : utilisez-vous le Link-Time Optimization ou le Whole Program Optimization?
Mike Acton : non, c'est trop lent
Fin du contenu technique
Je constate (et ce n'est pas la première fois) que les gens du comité sont très ouverts, très humbles, très à l'écoute, alors que les spécialistes du domaine (comme le conférencier de ce matin) sont souvent plus convaincus, au point d'offrir un message peut-être plus dogmatique, du moins en apparence. Ses positions anti-exceptions et anti-templates vont faire jaser.
Je ne trouve pas Michael Wong alors je vais travailler un peu. En allant m'installer, j'ai croisé (et salué) Nicolas Fleury, que je reverrai en après-midi.
Tel que prévu, j'entends des trucs autour de moi comme « Je suis content de travailler à un endroit où on me laisse utiliser une plus grande partie du langage » ou encore « Ouais, y a-t-il des gens qui utilisent les exceptions? ». Chandler Carruth a posté tantôt « Moi, j'aimerais savoir quels sont les problèmes qu'ils perçoivent pour savoir comment mieux les aider ». Il y a beaucoup de croyances et de peurs dans le discours de nos deux derniers Keynote Speakers. Je n'ai pas vu de données solides pour appuyer leurs dires, et je suspecte que ce soit symptomatique. À suivre...
Pour le voir en-ligne : https://www.youtube.com/watch?v=dTeKf5Oek2c
Gros après-midi de prévu pour moi, avec Stephan T. Lavavej pour des techniques de programmation pure (celles appliquées dans la bibliothèque standard), le rôle de C++ dans les jeux AAA (par Nicolas Fleury), et la manière selon laquelle Ubisoft Montréal développe des jeux (par Jeff Preshing). Ensuite, j'ai le souper avec les autres membres du comité de programme, et une séance avec le public pour prendre leurs idées.
Sans surprises, Stephan T. Lavavej est amusant, dynamique, fait des blagues de chat (dans ses prestations, les programmes s'appellent cat ou meow et font meow quand on les teste). La salle est pleine à craquer. Le thème est STL Features and Implementation Techniques.
Quelqu'un de la salle lui demande s'il a des chats. Stephan T. Lavavej dit que non; il préfère faire de l'impartition de chats à ses amis.
Quelqu'un de la salle lui demande si, lors d'un rapport de bogue, il serait plus disposé à le lire et à s'en occuper s'il y a une référence à un chat dedans. Stephan T. Lavavej dit oui, en effet, parce qu'on risque de le lui passer.
Début du contenu technique
Stephan T. Lavavej travaille sur la STL de Microsoft depuis 2007. Leur version est basée sur celle de Dinkumware. Il préfère les questions à la fin, avec numéros de diapos
C++ 11, mais supporté dans la bibliothèque depuis C++ 14
Les littéraux sont dans un inline namespace (std::literals::string_literal)
En pratique, il y a une gymnastique de namespace à faire. Il ne faut pas faire :
using namespace std::namespace_literals::chrono_literals
Ça ne donne rien. Préférez :
using namespace std::string_literals // inline namespace
...ou...
using namespace std; // on les ramasse tous parce qu'ils sont inline
Typiquement, les littéraux vont venir avec les types grâce à ADL
Les conteneurs autres que std::list implémentent ==, alors on préfère ça par défaut quand les types d'éléments sont identiques
L'algorithme equal() a maintenant une version à quatre paramètres (da, fa, db, fb)
L'algorithme mismatch() aussi, is_permutation() aussi
Les algorithmes à 1,5 intervalles supposent des nombres d'éléments corrects, ceux à 2 intervalles s'en occupent
L'algorithme mismatch() retourne une paire d'itérateurs sur la première non-correspondance
Stephan T. Lavavej recommande les nouveaux algorithmes. Ils ne coûtent pas plus cher (belles optimisations) et sont robustes... Sauf sur std::list, trop mal foutue (heureusement, std::list::size() est maintenant )
Pour distinguer un 4e paramètre prédicat et un 4e paramètre itérateur, les deux étant passés par valeur, rien à faire de spécial dû à un ordonnancement partiel (ItB, Pred != ItB, ItB)
Y a-t-il d'autres algos à 1,5 intervalles dont il faut se préoccuper?
On a maintenant la forme equal_to<>() pour des types par défaut
Stephan T. Lavavej explique _Iter_cat(...) pour déduire une catégorie (gratuitement), ce qui aide à se défendre contre les macros, puis explique le Tag Dispatch (que je peux vous expliquer aussi si vous le voulez)
Les algorithmes sont optimisés par types. Une note passée en fusée : ne pas utiliser memcmp() pour des données de type double...? J'ai une idée de la raison mais je veux vérifier...
L'interface était monstrueuse. Elle est bien mieux. Il montre le Mallocator (malloc()/ free()) qui entre sur une diapo!
On peut lever bad_array_new_length quand un tableau ne peut être alloué
Pour assurer la « métacolle » entre C++ 03 et C++ 11, on a les allocator_traits. Faut pas le spécialiser : il s'arrange tout seul!
En C++ 11, le construct() des allocateurs de C++ 03 sont vilains. On est mieux d'écrire une versions variadique + Perfect Forwarding. Ça permet le mouvement, ça va plus vite... Gratuitement!
Des algos comme lower_bound() peuvent prendre un critère d'un type distinct de celui des éléments, p. ex. : dans le cas d'un conteneur de string triées par longueur.
Dans une map, les méthodes comme find(), lower_bound(), etc. peuvent accepter un prédicat quelconque. Ça fonctionne grâce aux foncteurs diamants comme less<> au lieu de less<K>
Passe amusante :
template <class U, class C2 = C, class Dummy = typename C2::Meow>... // universellement accepté! (P. J. Plauger)
Duh?
On a meow(const X&)
On a meow(X&&)
On a template <class T> void meow(T&&)
On peut avoir meow(const T&&) = delete;. Ça évite de ramasser des pointeurs sur des temporaires
On peut aussi avoir template <class T> void meow(T&&) = delete;
Ça permet à la STL d'éviter de ramasser des pointeurs sur des temporaires (p. ex. : cref())
Littéralement :
for(auto elem : range) // mauvais, même pour les int
for(const auto elem : range) // mauvais, même pour les int
for(auto && elem : range) // experts seulement
for(elem : range) // idéal, mais C++17
for(const auto && elem : range) // nonsense
Question : diapo 44, puis-je utiliser (const T &&) = delete avec VS2013?
Stephan T. Lavavej : oui
Question : diapo 22, in y a un for(;first1!=last1;++first1,(void)++first2)... WTF?
Stephan T. Lavavej : c'est pour les comiques qui surchargent operator,()
Question : et s'il y en a plus que deux?
Stephan T. Lavavej : dans notre STL, il y a quelques cas de 3, aucun de 4 ou plus. Insérer le (void) au 2e règle le problème en général à cause de l'associativité
Question : vous avez acheté la STL de Dinkumware?
Stephan T. Lavavej : c'est la meilleure décision de l'histoire de Microsoft! On a un service fabuleux de leur part, et nous n'avions pas l'expertise à l'époque pour faire un travail de cette qualité
Question : mais ton rôle alors...?
Stephan T. Lavavej : je fais des revues de code, je m'occupe de trucs non-portables, je fais des retouches de métaprogrammation pour optimiser des trucs
Question : comment testes-tu ça?
Stephan T. Lavavej : pas assez souvent. On a des rencontres hebdomadaires, des « semaines de test » où on s'agresse mutuellement pour essayer de tout casser, etc. On essaie aussi avec des options de compilation étranges
Fin du contenu technique
C'était dynamique et amusant, mais les diapos filaient à un rythme d'enfer!
Difficile d'aller chercher du café entre les présentations; il y a beaucoup de monde en circulation.
Pour le voir en ligne : https://www.youtube.com/watch?v=qYN6eduU06s
Nicolas Fleury, d'Ubisoft Montréal, vient nous entretenir de C++ dans les immenses jeux AAA. Il se présente comme un architecte technique.
Début du contenu technique
Nicolas commence par expliquer la situation d'Ubisoft Montréal, à sa connaissance le plus gros studio de développement de jeux vidéos au monde.
C'est un environnement Windows-centrique, mais qui vise plusieurs plateformes
Assassin's Creed Unity :
Rainbow Six : Siege, on parle de 8M C++ LOC. Ça prend cinq minutes à compiler.
Le coeur est relativement petit. Les graphiques vont dessus, puis le moteur, puis le gameplay. Les programmeurs junior commencent typiquement dans le gameplay. Ils ont besoin d'une API souple.
Ils utilisent C++ pour la vitesse et C# pour les bouts moins critiques, question de souplesse. L'éditeur est en C#
De C++, ils n'utilisent pas :
On utilise FastBuild, pour remplacer MSBuild pour C++. C'est à code ouvert (Franta Fulin, qui travaille chez Ubisoft maintenant). Peu de dépendances, utilise bien le CPU, est réparti et fait du Caching. FastBuild supporte UnityBuild, qu'on utilise depuis dix ans (des en-têtes précompilés)
Ça implique qu'il ne faut pas avoir de macros qui dépassent les frontières d'un fichier
Ils génèrent le code de Debug avec /Ob1 (Minimal Inline). L'air de rien, le code en Debug est plus rapide et ça se trace bien quand même
Ils dérivent les templates de classes non-génériques, pour que les détails spécifiques aux types soient plus petits.
Ils ont un IDL maison pour le modèle objet, qui permet de générer des régions de code dans les .h et les .cpp. Cela leur permet d'éviter certaines passes de métaprogrammation. Ça compile plus vite
Ils ont enfin un dialecte maison pour faire du Custom Edit and Continue, qui génère du C++. Ils utilisent leur propre modèle de vtbl « manuel »
Sur le plan des outils, ils ont :
La dernière génération de consoles a huit ans
Parle du principe 90/10. Les gens qui travaillent sur le 10 sont... particuliers
Le Frame Rate est une contrainte (60-70 fps). Si on doit livrer, et qu'à un mois de la fin, on a 20 fps, on a une équipe d'optimiseurs qui s'en occupent. Ils sont impressionnants
Souvent, la clé est l'organisation des données (donne des exemples)
Pour les singletons, ils ont un SingletonStorer pour les grouper et les créer/détruire dans le bon ordre. Ils font comme ma passe de singletons statiques, mais avec un pointeur, ce qui coûte quelque chose en pratique (indirection tend à amener un Cache Miss). Autre cas semblable : polymorphisme dynamique. Cache-Miss pour la vtbl, puis direction, et ce plusieurs fois si les vtbl varient en lieu. Les optimiseurs vont grouper les objets de même type ensemble, ce qui améliorera la Cache et réduira les indirections
Ils évitent l'allocation dynamique de mémoire. Le tas est lourd, global, et dans le monde du jeu, avec beaucoup d'allocations par Frame, on fragmente. Depuis Assassin's Creed 1, on examine l'espace requis typiquement et on remplace les tableaux par un InPlaceArray<T,sz>. Avoir des blocs de même taille partout réduit la fragmentation. Ils allouent sur la pile si c'est possible. Faire ça a permis, sur Prince of Persia, d'éliminer tous les irritants restants, et on pouvait jouer des jours durant.
On a une immense base de code multiprogrammée, avec certains bogues qui ne se produisent qu'en mode Release
On enlève les itérateurs checked, le debugger heap, et le Windows Fault-Tolerant Heap
On donne un cours sur le débogage de code compilé en mode Release. On peut survivre à un call stack vide
On a notre ubiNew qui permet d'étiqueter les allocations avec des const char*. Ça permet d'identifier les fuites.
Pour gérer la corruption de la mémoire, ils gardent une trace des allocations avoisinantes. Une des manoeuvres qu'ils font est d'allouer une page sur deux, la première read-write et la seconde read-only. Ça génère une faute matérielle si quelqu'un dépasse.
Question : comment quelles primitives de concurrence et synchronisation utilisez-vous?
Nicolas Fleury : mon collègue Jeff Preshing va en parler dans une trentaine de minutes
Question : comment avez-vous utilisé un autre débogueur ou est-ce impossible avec les jeux?
Nicolas Fleury : moi, je n'ai pas essayé. C'est peut-être possible
Question (Mark Acton) : avez-vous un mécanisme pour sélectionner ou couper des features d'un jeu à l'autre?
Nicolas Fleury : we have an extremely organic approach. We fork from existing projects, it's chaotic, we're very bottom-up but it works! On fait bouger les gens d'un projet à l'autre, ça fait circuler les bonnes idées.
Question : à quel point Unity Build fait-il mieux que Link-Time Code Generation?
Nicolas Fleury : je ne sais pas.
Question : utilisez-vous des particularités de C++ 11?
Nicolas Fleury : oui, quelques-unes. J'ai enlevé mes diapos pour des raisons de temps. Sur les nouvelles consoles, avec Sony passant à Clang et MSVC pour XBox, on a un bien meilleur support qu'avant. J'ai un itérateur de débogage qui est Move-Only, entre autres
Fin du contenu technique
Il s'en est bien tiré. La foule a semblé apprécier, et réagir positivement à ses commentaires et à ses prises de position.
Petite pause. Je suis fatigué... J'ai fini par accepter de prendre quelques M&M pour combattre le sommeil.
Je suis content d'avoir pu saluer Jeff Preshing et le remercier d'avoir accepté de venir présenter à cette conférence. Lors d'une des formations que j'ai donné aux gens de l'industrie du jeu en 2014, j'ai présenté son site comme référence et j'ai vanté (avec raison) son travail, et certains des participants à ma formation sont de ses amis, avaient pris des photos en preuve et l'en avaient informé. Il s'en souvient, ce qui est chic.
J'ai aussi revu Bruce Dawson, mais ça a du sens de le voir ici étant donné qu'il travaille pour Valve et que les gens du monde du jeu aiment bien voir ce que la compétition/ les futurs collègues (pour paraphraser mon chic ami Patrick Hubert) fait
Michael Wong est allé piquer une jasette avec Jeff Preshing. Bonne chose qu'ils se parlent ceux-là.
Pour le voir en ligne : https://www.youtube.com/watch?v=X1T3IQ4N-3g
Sur son blogue, Jeff a fait un suivi sur sa présentation avec quelques compléments d'information fort pertinents : http://preshing.com/20141024/my-multicore-talk-at-cppcon-2014/
Jeff Preshing, donc, est ici pour nous entretenir de How Ubisoft Montreal Develops Games for Multicore – Before and After C++ 11
Je pense qu'il a eu de la difficulté à trouver un connecteur correct pour son Mac...
Début du contenu technique
On fait des jeux. Tous ces jeux roulent sur des ordinateurs multicoeurs.
Chaque coeur d'un processeur partage les instructions et l'espace adressable. Les threads peuvent s'exécuter sur n'importe quel coeur.
Depuis dix ans, on s'est améliorés pour en tirer profit. Je constate que la communauté C++ et la communauté du jeu progressent séparément; je veux les rapprocher (fait se rapprocher deux bulles qui se font des câlins)
Deux parties : programmer chez Ubi, puis programmer avec des atomiques
Fait un survol de l'historique des consoles
Ne font pas de différence entre coeurs et threads h/w en pratique.
Dans le passé (année 2000), on faisait un cycle d'opérations moteur + graphique, en séquence.
On a trois grands modèles de multithreading maintenant :
Portez attention aux objets concurrents (dont au moins un en écriture). On peut faire ça à l'aide des primitives du système d'exploitation ou à l'aide des atomiques
Moteur, puis graphique alors que le moteur continue. Ça peut t'amener un gain de 2 par tranche de temps
Ça prend un sémaphore pour synchroniser les moments où on « passe le puck »
Pour la concurrence (le moteur modifie des bonhommes, le graphique les affiche), on peut soit garder deux copies de chaque objet côte à côte, soit avoir une version moteur et une version graphique
On fait du Content Streaming. Un Loading Thread qui dort jusqu'à un événement, se réveille, charge le nécessaire, dort. Ça prend une synchro, typiquement une file synchronisée (faible contention). Avec six coeurs typiquement, faut que le chargement dorme pour l'essentiel du temps
On peut la raffiner en ajoutant des requêtes pour annuler, interrompre ou re-prioriser les requêtes. On implémente ça avec des objets maison
On voulait du parallélisme plus précis, de plus fine granularité
Avec des worker threads, et une file synchronisée de tâches, ça se passe bien.
On groupe les tâches en « métatâches », ou TaskGroups, et il arrive que plusieurs threads partagent le même TaskGroup, ce qui implique de la synchronisation
Les TaskGroups ne sont plus une simple file d'attente; on peut voir une file par worker, par exemple
Avec un TaskGroup, on peut imposer un ordonnancement, p.ex.: dire que la physique doit être traitée avant l'IA
On peut ajouter plusieurs fonctionnalités (affinités, batch, priorités, tâches par liste, etc.)
On a plusieurs objets assujettis à forte contention, et on livre nos jeux sur plusieurs plateformes
Une bibliothèque d'atomiques est très « bas niveau ». Ça touche
Une barrière légère ordonnance les read-from mémoire ou ordonnance les store-to mémoire
Une barrière complète s'assure que les stores précèdent les loads correspondants (atomic_thread_fence()). Ce qu'on a implémenté mappe nos opérations à des opérations machines appropriées
Il est difficile de programmer correctement avec des atomiques. On s'en sert quand on constate des goulots d'étranglement. On a fait des bêtises mais on a appris
Jeff Preshing montre une file Wait-Free, un consommateur, un producteur, un tableau statique à titre de substrat avec deux positions (read, write), le writePos est volatile
Version intiale : fonctionnait bien sur le poste du programmeur, crashes intermittents et étranges le lendemain, le tout sur XBox360 (un PowerPC, qui réordonnance les read et les write en apparence indépendants)
Même sur x86, le compilateur aurait pu réordonnancer nos instructions et nous jouer le même tour.
Avec une barrière légère, on règle le problème
Jeff Preshing fait un bref résumé de ce qu'il a montré jusqu'ici
Question : votre environnement est-il préemptif pour les threads?
Jeff Preshing : oui. Ça pose problème quand on fixe des affinités entre thread et coeur
Question : vous utilisez x86, ARM, PowerPC... Est-ce la même codebase?
Jeff Preshing : pour au moins 90%. Il y a des trucs spécifiques aux plateformes. L'abstraction des atomiques que nous avons fait en est une.
Question : quand avez-vous besoin de pleine cohérence séquentielle?
Jeff Preshing : à Ubisoft Montréal, personne ne sait exactement ce que ça veut dire :)
Question : comment testez-vous ces trucs?
Jeff Preshing : on fait des stress tests sur les trucs délicats.
Jeff Preshing dit que ce sera une brève introduction
La bibliothèque <atomic> est construite à partir de principes portables. Je trouvais l'idée intéressante quand j'en ai entendu parler, notre approche étant un peu brouillon à mes yeux
Règle 1 : C++ 11 interdit les Data Races, donc les accès concurrents avec au moins une écriture. Data Race→Undefined Behavior. Sur une atomic<T> : Ok
Cette règle tient au fait que C++ 11 supporte à peu près tous les processeurs, dont certains très vilains comme des processeurs 8 bits ou 16 bits, où une écriture 32 bits n'est pas atomique.
On a beaucoup de code qui triche (volatile int au lieu de atomic<int>), parce qu'on est typiquement sur des machines 32 bits. Le standard ne définit pas ce qui se passe mais la machine nous tolère
Règle 2 : en pratique, <atomic> est deux bibliothèques, soit les atomiques SC (inspirées – sans être pareilles – des volatiles de Java) et les atomiques de plus bas niveau (inspirées – sans être pareilles – des volatiles de C)
Il est plus simple de raisonner sur des atomiques SC, mais elles sont plus lentes. Et inversement.
Les atomiques SC s'intéressent surtout à l'entrelacement des opérations (il montre un exemple simple).
Les atomiques « bas niveau » sont une autre bête (montre un exemple avec memory_order_relaxed)
atomic<int> a;
a.store(1, memory_order_seq_cst); // 2e paramètre optionnel
c = a.load(memory_order_seq_cst); // paramètre optionnel
On peut aussi utiliser = tout simplement
Jeff Preshing montre encore une fois un exemple d'atomiques «bas niveau» et illustre les risques associés
Question (Michael Wong) : je suis l'un des responsables de memory_order_relaxed, avec Lawrence Crowl. Laquelle des deux formes utilisez-vous?
Jeff Preshing : à Ubisoft Montréal, on utilise encore nos outils maison. Notre bibliothèque est bas niveau (dans l'audience, on mentionne que le PowerPC est important pour eux, alors memory_order_relaxed et les barrières, c'est important pour eux)
Imaginez que chaque thread a sa propre copie de la mémoire. Ça aide à réfléchir. En pratique, chaque CPU a sa propre Cache après tout. Les changements finissent par se propager, mais le rythme est imprévisible.
Sachant cela, comment fonctionnent les atomiques SC? C'est étrange, loin de la réalité :)
En pratique, le compilateur génère pour nous les bonnes instructions pour assurer cette cohérence
Elles sont plus rapides!
Elles nous permettent aussi de migrer nos bibliothèques d'atomiques maison vers C++ 11 sans pertes sémantiques
Jeff Preshing transforme sa file pour qu'elle fonctionne avec des atomiques de C++ 11
La beauté des principes fondateurs des atomiques de C++ 11 nous indiquent que quand un consommateur voit une écriture faite par un producteur, les deux threads synchronisent et ce qui précède l'écriture précède aussi la lecture
Avec cette synchro, Jeff Preshing montre que les barrières peuvent disparaitre!
Avec des atomiques SC, on écrit le code initial, mais il devient correct dû aux garanties SC. Tout store est un release et tout load est un acquire
Jeff Preshing dit avoir compilé son exemple avec optimisations, l'avoir exécuté, avoir regardé le code machine généré... C'est pas mal optimal!
Jeff Preshing : fait un petit sommaire
Question : comment empêches-tu tes collègues de briser les suppositions du code?
Jeff Preshing : je leur parle
Question : as-tu des conseils pour ceux qui n'utilisent que x86?
Jeff Preshing : commence avec les SC. Si t'as un objet sous très forte contention, alors seulement tu vas plus profondément. Si t'es seulement sur x86, t'as même pas besoin des atomiques de C++ 11, tu peux être spécifique à cette machine
Question : es-tu triste de ne pas pouvoir utiliser memory_order_consume?
Jeff Preshing : pour le moment, ils font tous comme si c'était memory_order_acquire... Je ne sais pas à quel point ça change quelque chose sur le matériel qu'on utilise aujourd'hui
Question (Stephan T. Lavavej) : je recommande à tous d'utiliser les atomiques SC. C'est super difficile
Jeff Preshing : ouaippe
Question (Michael Wong) : on veut fixer memory_order_consume, c'est utile dans un contexte général, mais on ne sait pas comment
Question (Lawrence Crowl) : consume est super-difficile
Jeff Preshing : c'est le niche d'entre les niches, en effet.
Fin du contenu technique
Wow. Difficile de demander mieux. Considérant le niveau de difficulté, c'était essentiellement parfait.
Je voulais bavarder avec Michael Wong après la présentation pour voir ce que je pourrais faire pour la partie canadienne du comité ISO, mais il ne pouvait pas venir au souper avec nous, ayant une obligation ailleurs. On va bavarder demain.
Je suis allé au souper du comité de planification, comité auquel j'ai participé. J'ai principalement discuté avec Jens Weller, James McNellis, Michael Caisse et Jeff Trull, tous sympathiques, et pris une bière (une Arrogant Bastard, très correcte). Mon Pad Thaï était correct, mais j'en fais du meilleur à la maison. Le service était bon mais lent. Et ils ont carté deux de mes collègues mais pas moi... Semble que j'aie l'air d'avoir plus de 35 ans, qui est leur seuil minimal d'estimation.
Je constate l'irritation de bien des gens face aux Keynotes d'aujourd'hui et d'hier, qui préconisaient d'utiliser C++ comme un C d'autrefois, pour des raisons plus près des croyances que de la science. Je pense qu'on aurait pu avoir les deux mêmes, mais pas deux jours consécutifs, et nous aurions eu une toute autre expérience.
La rencontre où on discute de ce qui peut être amélioré nous permet de discuter de ce qui s'est bien et mal passé. On discute de plusieurs trucs mineurs, mais j'espère que cela ne donnera pas l'impression aux organisateurs que ce n'était pas bon... Il y a eu des irritants, mais c'était un _excellent_ colloque sur le plan du contenu (et il reste encore des présentations de qualité demain).
On organise celle de l'an prochain. Je vais essayer d'aider un peu mais encore comme cette année (je suis trop occupé pour prendre d'autres responsabilités). Je vais en avoir pour longtemps à absorber toutes les idées que j'ai eues cette semaine, à écrire des articles, à organiser ma pédagogie... Ce sera extrêmement rentable.
On a eu autour de 200 propositions, et on a eu autour de 100 présentations. Pour un colloque qui en est à sa première année, c'est brillant!
Tiens, je viens de rencontrer Artur Laksberg. Il fait des trucs intéressants du côté multiprogrammation chez Microsoft. Je n'avais pas remarqué qu'il était ici.
Je suis arrivé avec un léger retard ce matin, dû à l'organisation normale de départ de l'hôtel (j'ai manqué mon autobus, bêtement, par quelques minutes). J'ai donc manqué les premiers Lightning Talks.
Je suis arrivé juste à temps pour voir la présentation de Michael Caisse sur boostache, un générateur de documents à partir de modèles. Son truc fait pas mal de métaprogrammation, et fusionne une AST de données et une AST d'instructions à la compilation. Le gars est franchement intéressant. https://www.youtube.com/watch?v=hTnoEjqUZjI
Ensuite, Marc Eaddy a présenté un truc nommé Pimp My Log() (!) en combinant macros et templates. J'ai manqué des bouts parce que je suis un peu désorganisé, mais il fait circuler des pair<A&&,B&&> dans ses fonctions. Va falloir que je regarde ça plus tranquillement :) https://www.youtube.com/watch?v=TS_waQZcZVc
Pour le voir en ligne : https://www.youtube.com/watch?v=vzge-Fkzhks
Je suis ensuite allé à la présentation de Michael Wong sur la mémoire transactionnelle. J'ai vraiment compris ce truc l'an passé (je ne lui accordais pas assez d'importance avant d'avoir une illumination en voyant un problème qu'on ne peut résoudre qu'avec ça... et on parle d'une problème vraiment fondamental!) alors je suis plutôt content que la sommité sur le sujet soit avec moi ce matin.
Il compte parler plus des raisons pour ce nouveau mécanisme, leur utilité, plus que leur implémentation (mais ça aussi m'aurait intéressé!).
Début du contenu technique
Michael Wong est le chairman du comité travaillant sur la TS de mémoire transactionnelle (et il porte plusieurs titres, règle générale). C'est un gros groupe qui travaille sur ce feature (d'une complexité sans nom à implémenter!)
L'idée est d'avoir un accès aussi simple à mémoire transactionnelle qu'aux verrous grossiers; s'échelonner aussi bien que des verrous plus fins; et se composer (un problème des verrous est que ça ne compose pas en général)
Plusieurs trucs au menu :
En 1993, où étiez-vous? Michael Wong fait un retour (amusant), mais surtout (pour nous ce matin) Maurice Herlihy et Elliott Moss ont écrit l'article séminal Architectural support for lock free data structures
Michael Wong montre un tableau à trois colonnes de caractéristiques de systèmes concurrents : agents asynchrones, collections concurrentes, états partagés mutables. La mémoire transactionnelle intervient en tant qu'abstraction pour gérer les états partagés mutables, comme le font les verrous. On a des futures pour aller au-dessus des threads; l'équivalent pour les verrous sera la mémoire transactionnelle
Chaque transaction semble s'exécuter de manière atomique. Plusieurs transactions peuvent s'exécuter concurremment mais le résultat doit être équivalent à celui de l'une des exécutions séquentielles possibles. On parle de spéculation optimiste ici
Variante : Lock Elision (ne pas prendre de verrous du tout! Ça fonctionne... si personne ne nous dérange; ça implique un rollback si quelqu'un a interféré avec nous
synchronized { // sais pas si le code est raisonnable
node.next = succ;
node.prev = pred;
node.pred = node;
node.next = node;
}
La mémoire transactionnelle intervient quand on veut synchroniser (propriétés ACI(D)) des étapes dans un algorithme. Les atomiques sont utiles pour synchroniser les accès sur une zone mémoire :
Quelques critiques:
Est-ce que ça peut aider à économiser du courant? Est-ce que ça peut servir sur des appareils mobiles? Michael Wong parle des attentes des gens. À son avis, on est passé du Hype au Bof et on a remonté au point où ça commence à être productif
Pourquoi en a-t-on besoin?
Pourquoi est-ce difficile?
On veut construire sur la spécification de C++ 11, on respecte le Catch Fire Semantics des programmes contenant des Data Races
On veut minimiser les nouveaux mots clés. On ne veut pas briser le code non-transactionnel. On veut quelque chose de « débogable », et un maximum de vérification statique. Dans le doute, le programmeur fera ses choix
Cas d'utilisation : la programmation générique ne fonctionnera jamais avec des verrous. On n'a pas assez de contexte! Un simple:
template <class T>
void f(T &x, T &y)
{
unique_lock<mutex> _{m};
x = y;
}
... est en fait une sorte de Callback (brillant constat! Wow!), qui peut impliquer une saisie du verrou m et provoquer un deadlock
Il n'y a pas de solution générale à ce problème si on se limite à des verrous
On peut bannir les verrous... mais t'sais... C++, c'est vaste!
Avec mémoire transactionnelle, on a :
template <class T>
void f(T &x, T &y)
{
transaction {
x = y;
}
}
...et il n'y aura jamais de deadlock. La mémoire transactionnelle est nécessaire pour atteindre un fonctionnement correct de programmes impliquant de la programmation générique
Les principaux cas d'utilisation :
Michael Wong parle du Issaquah Challenge avec Paul McKenny : http://isocpp.org/files/papers/N4037.pdf
Il donne des exemples de manipulation concurrente de noeuds dans un arbre AVL
La mémoire transactionnelle est capable de haute concurrence si on a surtout des lectures
Michael Wong présente quelques diapos montrant des expérimentations avec des étudiant(e)s, qui laissent entendre que les erreurs dans le code étaient plus rares avec la mémoire transactionnelle qu'avec des verrous. Le nombre de LOC est semblable avec la mémoire transactionnelle, mais les efforts sont investis plus dans l'optimisation que dans la rectitude puisque cela a demandé moins d'efforts
Ça ne rendra pas la programmation parallèle simple, mais ça ramène le problème à des niveaux plus humains
En pratique, on a trouvé peu de cas de transactions imbriquées
Côté vitesse :
Le compilateur gcc le supporte déjà, depuis 4.7, sur la base des spécifications de l'époque
Comment faire un Rollback d'une action irrévocable? Michael Wong revient sur l'analogie des feux de circulation : avec de la mémoire transactionnelle, tu fonces à travers la ville, sans regarder les feux, et tu recules quand c'est dangereux. Ça fonctionne très bien si on n'est pas nombreux à l'intersection (faut pas faire ça au centre-ville).
Les actions irrévocables existent : afficher à l'écran, lever une exception... On a des approches, comme permettre au code client d'avoir un plan B...
Dans un jeu multijoueurs, ça prend de la synchro. Il faut le A et le C de ACID, et on a au moins deux entités concurrentes, en plus de plusieurs objets (donc un risque d'avoir plusieurs verrous). Des verrous fins risquent un deadlock et n'offrent pas l'atomicité. La mémoire transactionnelle règle le problème
La mémoire transactionnelle est typiquement plus lente avec peu de threads, plus rapide avec plusieurs.
Les mots clés :
atomic_noexcept | atomic_commit | atomic_cancel { ... } // composable, rollback
synchronized { ... } // composable, pas de rollback
transaction_safe // mot clé
[[transaction_unsafe]]
On aura :
// analogie: atomiques SC
atomic_noexcept { x++; }
atomic_commit { x++; } // peut importer si une exception est levée et on veut le savoir
atomic_cancel { x++; }
// analogie: atomiques relaxées
synchronized { x++; }
Impact intéressant:
void f() { cout << "Yo" ; }
atomic_noexcept { f(); } // thread 0
atomic_noexcept { f(); } // thread 1
On peut avoir YoYoYo si un conflit survient. Avec synchronized { f(); }, ça n'arrivera pas.
Si on est transaction_safe, on ne touche pas à de l'assembleur ou à des entités qualifiées volatile.
Soit ceci :
atomic_cancel {
x++;
if (f()) throw 3;
}
... est-ce que le x++ a eu lieu si on lève une exception? Matière à débats...
Un atomic_cancel est une sorte de trou noir, les exceptions n'en sortent pas. On réfléchit à ça.
On vise l'horizon de novembre 2015 pour une acceptation dans le standard.
Question : comment peut-on détecter des conflits logiciels?
Michael Wong : il y a des mécanismes. On peut faire de la détection pessimiste ou optimiste. Quatre cas: succès, abandon, détection hâtive, pas de progrès)
Question : donc ça nous prend une copie des données avant-transaction?
Michael Wong : parfois, oui. Selon les approches, on peut avoir un commit rapide ou un rollback rapide (pas les deux)
Question : quelle est taille du Rollback Buffer?
Michael Wong : ça dépend de l'implémentation
Question : Paul McKenny a avoué hier que sa solution (complexe) n'échelonne pas. On a besoin de mémoire transactionnelle
Michael Wong : en effet
Fin du contenu technique
C'était désorganisé mais intéressant. Michael Wong est brillant et semble vouloir aider tout le monde. Il connaît sa matière en profondeur. J'ai hâte de pouvoir expérimenter avec la mémoire transactionnelle pour mieux comprendre les idiomes qui y seront associés.
Petite jasette anodine avec Marshall Clow en descendant les marches, à propos de la qualité élevée du contenu des diverses présentations. Ce matin, par exemple, en même temps que celle où j'étais, Gor Nishanov mettait de l'avant une nouvelle métaphore de parallélisme et concurrence (des continuations concurrentes, si j'ai bien compris) dans le local voisin; je vais regarder ça avec attention quand ce sera en ligne.
Poignée de mains avec Jens Weller au passage. Drôle de gars; une façon d'être un peu taciturne, ne sourit pas beaucoup, mais un humour un peu pince-sans-rire.
Pour le voir en-ligne : https://www.youtube.com/watch?v=xnqTKD8uD64
Le Keynote de ce matin sera donné par Herb Sutter, et se nomme Back to the Basics! Essentials of Modern C++ Style. Ça ne peut qu'être excellent. Je m'attends aussi à des prises de position sur le passage de paramètres Sink étant donné les échanges entre lui et Scott Meyers au cours des derniers mois.
Début du contenu technique
On va parler boucles, pointeurs, références, pointeurs intelligents, déclarations de variables, passage de paramètres
Herb Sutter arrive, présente « Herb » (il se met une casquette et se dit membre de Complexity Anonymous). Il se dit dans un programme en douze étapes pour se sauver de la complexité
Plus sérieusement, quand on est un expert, on oublie qu'il y a environ trois millions programmeurs sur la planète. En comparaison aux contributeurs de libstdc++ (~30) et libc++ (~5-7), c'est pas pareil.
Même pour Boost, on parle de ~300 personnes. Et pour ISO WG21? ~300 max.
On veut alimenter les experts, mais faut pas oublier l'écosystème.
Herb Sutter rappelle que Bjarne Stroustrup, jour 2, n'a utilisé aucune occurrence de && dans ses exemples!
Herb Sutter recommande d'acheter A Tour of C++... Il est Sold-Out... Qui a la dernière copie? Devinez! :)
Cette présentation parle des comportements à adopter par défaut.
Don't overthink (initially). Optimisez pour la clarté d'abord. Évitez les optimisations et les pessimisations prématurées
Faites ceci :
for(auto & a : c) { .. use(a); ... }
C'est la chose à faire, que c soit const ou non!
Bientôt, à cause de Stephan T. Lavavej (qui crie Meow!), on écrira :
for(a : c) { use(a); }
et on aura le conmportement optimal par défaut. La variable devra être un nom qui n'est pas encore utilisé dans la portée
Utilisez les pointeurs intelligents, mais vous pouvez aussi utilisez des pointeurs bruts et des références brutes
Ce qu'on ne veut pas, c'est des new/delete « nus » (le moins possible, en tout cas), et on ne veut pas être responsables des pointés. C'est pour ça qu'on veut unique_ptr et amis!
Utilisez unique_ptr et make_unique par défaut, shared_ptr et make_shared au besoin
Tout pointeur impliquant un comptage de références... ne le passez pas en paramètre, même par référence (!), à moins de vouloir que l'appelé contribue au partage. Les indirections coûtent trop cher
Copier des pointeurs avec comptage de références dans une boucle est assassin (tu peux avoir un accroissement de vitesse de l'ordre de 4x)
Préférez unique_ptr par défaut.
Passez par valeur à une fonction Sink. L'appelant devra expliciter son intention par std::move(). Passez par adresse ou référence brute si non-Sink
Il n'y a pas de raison légitime de passer un const unique_ptr<T>&
Avec des shared_ptr, le passage par valeur peut-être légitime
Les pointeurs intelligents par & sont Ok si on veut leur faire reset() ou un truc semblable
Un may_share(const shared_ptr<T>&) peut être raisonnable si le partage est possible, pas garanti. Ça peut épargner une copie coûteuse
Un cas intéressant de réentrance avec un shared_ptr<X> :
shared_ptr<X> glob;
void f(X &x)
{
g();
use(x);
}
void g()
{
glob = ...
}
void mechant()
{
f(*glob);
}
On a un vilain risque d'accéder un objet détruit. Je l'ai vécu (indirectement) cet été dans un contrat!
Réécrire mechant en gentil :
void gentil()
{
auto pin = glob;
f(*pin); // bingo!
}
Herb Sutter envoie une photo d'un personnage de Spinal Tap et blague sur les cheveux de Scott Meyers
Herb Sutter est un partisan du recours quasi universel à auto. Par exemple :
auto var { init }; // type track, deduce. Plus facile à entretenir
auto var = type { init }; // type sticks, commit. Le type fait explicitement partie de l'expression. Peut être élidé ou déplacé
...ou encore :
type var { init }; // parfois, mais plus rarement
Herb Sutter propose une fonction append_unique(container&,value) où auto fait très bien le travail, et est très lisible malgré tout. On a trouvé le nom de la fonction à partir de l'algorithme
Utiliser auto permet d'écrire pour une interface plutôt que pour une implémentation. Ça nous amène doucement vers les concepts
Avec la déduction de type, on élimine des erreurs humaines! L'implémentation devient moins fragile face aux changements de sémantique dans l'interface
Le code devient aussi plus robuste face aux changements d'implémentation; les types impliqués évoluent en fonction du reste du code
On n'aura pas de conversions implicites. Le code sera plus rapide!
Utilisabilité quand on veut associer un nom à une entité, par exemple une variables pour une λ, bind et autres cossins.
Utiliser auto à gauche est homogène, même pour les fonctions et les λ
Et pour les entiers, genre int x = 42;? Pensez aux littéraux, maison ou non : en écrivant auto x = 42.f;, on évite peut-être une conversion! Idem pour auto x = "42"s; ou auto c = "{42}"_ctx;
Herb Sutter fait remarquer que using a une forme semblable à celle de auto.
On évite auto a = atomic<int>{...}; pour les types comme std::atomic qui sont non déplaçables; idem pour les trucs lourds à déplacer, comme les tableaux bruts
Même ceci :
unique_ptr<Base> p = make_unique<Derived>(...);
...peut s'écrire :
auto p = unique_ptr<Base>{ make_unique<Derived>(...) };
sans pénalités.
Grande qualité avec auto : on n'a jamais de variables non-initialisées!
Ici, il y a une différence entre « par défaut » et « optimal »
New features get overused – Bjarne Stroustrup
It's about the lvalues, after all! – Scott Meyers
Il faut penser à tout ce que l'appelant pourrait vouloir nous passer...
Herb Sutter remercie plusieurs personnes ayant participé à la réflexion dans ce cas. La recommandation pour C++ 98 était :
Extrant pur : X f() ou f(X&) si coûteux. La forme X f() est Ok souvent à cause de RVO
Intrant/ extrant : f(X&)
Intrant pur : f(X) si petit, f(const X&) le reste du temps
La recommandation pour C++ 11 : est... la même!
Extrant pur : X f() ou f(X&) si coûteux. La forme X f() est Ok souvent à cause de RVO
Intrant/ extrant : f(X&)
Intrant pur : f(X) si petit, f(const X&) le reste du temps
Le mouvement influence les raisons; un truc incopiable devra être déplacé par l'appelant s'il est déplaçable
Avantage de C++ 11 : les comportements par défaut sont juste plus applicables qu'avant, et plus efficaces
Pour optimalité:
Extrant pur : X f() ou f(X&) si coûteux. La forme X f() est Ok souvent à cause de RVO
Intrant/ extrant : f(X&)
Intrant pur : f(X) si petit, f(const X&) le reste du temps
On ajoute des cas pour optimiser en fonction de f(X&&), quitte à garder aussi f(const X&) si c'est approprié
Notez cependant que dans bien des cas, le code par défaut sera optimisé et rendra l'optimal redondant
On a des cas particuliers, évidemment (p. ex. : Perfect Forwarding)
On écrira des versions && seulement pour optimiser en fonction des rvalues
Et f(X) + move? Conseil de Howard Hinnant, mais il y a des pièges et ça peut nous ralentir. Faut y aller avec prudence pour éviter de créer des temporaires inutilement (donne l'exemple d'un set_name(...)... const string& est encore la meilleure chose à faire!) En plus, ceci :
void set_name(string s) noexcept { name_ = move(s); }
...peut poser problème (le noexcept est peut-être un « mensonge »... l'appel peut lever un exception, l'appelé non, mais ça peut surprendre l'appelant)
On peut faire une version « perfect-forwardable » de template <class T> void set_name(T&&), mais elle implique un enable_if pour restreindre T à string, et un is_nothrow_move_assignable pour éviter le désastre et rester noexcept
Copier une string ne devrait pas automatiquement passer par l'idiome d'affectation sécuritaire (on se sauve une paire new[]/delete[] environ 50% du temps!)
Pour un vecteur et une grosse string, truc amusant, la construction coûte plus cher que l'affectation (on peut souvent réutiliser l'espace)
Pour les constructeurs, le passage par valeur est préférable à des optimisations à la pièce, parce que ça évite une explosion combinatoire de cas particuliers
Scott Meyers parle de Universal Reference, mais va faire une mise à jour dans son livre qui va à l'impression... là!
Herb Sutter mentionne que le penser ainsi aide à se souvenir que c'est destiné à du Perfect Forwarding. Ça traduit mieux l'intention, c'est plus pédagogique
Ceci :
pair<iter, bool> res = f();
it = res.first;
result = res.second;
devient
auto res = f();
it = res.first;
result = res.second;
...devient (cool!) :
tie(it, result) = f();
Semble qu'il y ait une proposition pour déclarer les variables à même tie()!
Herb Sutter fait un égoportrait (un Selfie) avec les étudiant(e)s et les gens qui ont cinq ans et moins d'expérience avec C++. Ils sont nombreux
Question : intervalles bientôt?
Herb Sutter : oui
(Chandler Carruth voulait poser une question mais Herb Sutter a « coupé la ligne »; Chandler Carruth a l'air de mauvaise humeur. Quelque chose s'est passé, je pense, Il y a peut-être des clans ici... Sur Twitter, quelques personnes, dont Chandler Carruth, se disent opposés au style « auto dans la mesure du possible », et James McNellis indique qu'il travaille sur un article pour expliquer son opposition à cet effet... À suivre...)
Fin du contenu technique
Encore une fois, j'aurai été bien servi. Cette fois, sur le plan de l'idiomatique et des saines pratiques.
J'ai pris un peu de temps pour déterminer comment je me rendrais à l'aéroport. Au Meydenbauer Center, le bureau est au troisième étage, ce que je ne savais pas, alors je n'étais pas certain comment organiser mon transport. Il y a un théâtre au premier où le gérant, charmant monsieur, travaillait. Il m'a guidé et m'a parlé des options (et du trafic). On a fini par convenir que le taxi serait l'option la plus sûre, suite à quoi je suis allé au troisième où la réceptionniste, aussi très gentille, m'a organisé le tout. Je quitterai le centre à 15 h 30, heure de Seattle pour sauter dans un taxi jaune et me farcir le trafic de Seattle jusqu'à l'aéroport. J'ai hâte de revoir ma famille!
S'il y a un vrai défaut au colloque, c'est qu'il y a trop de contenu. On parle d'une centaine de présentations, par des sommités vraiment majeures dans bien des cas, mais on n'a qu'environ 22-23 plages horaires distinctes alors on finit par en manquer. Ce matin, je suis allé à la présentation de Michael Wong, mais j'ai jonglé avec l'idée d'aller à celle de Gor Nishanov juste à côté (un autre sujet directement dans mes cordes). Je vous raconte ça parce que, alors que je discutais avec la réceptionniste et qu'elle était mise en attente par la firme de taxi, Gor Nishanov est passé juste à côté. Je lui ai dit que j'aurais bien aimé être à deux endroits en même temps, car ce que j'entendais à propos de ses idées était vraiment intrigant; gentiment, il a pris un post-it et m'a donné l'adresse d'où je peux télécharger ses diapos. Très gentil de sa part Sa présentation est sur https://www.youtube.com/watch?v=KUhSjfSbINE
Pour le Panel, je me suis trouvé un endroit confortable. Avec la pile un peu faiblarde de mon ordinateur portatif, il faut toujours que je sois près d'une source de courant, ce qui ne pose pas vraiment de problème dans les petites salles mais devient irritant dans la grande salle Fermat où ont lieu les rencontres de la communauté toute entière. Pour cette raison, je me place souvent par terre non-loin des murs, mais j'ai une vue de moindre qualité sur la scène et, après 90 minutes, j'ai un peu mal au dos. Cette fois, j'ai une chaise (au fond de la salle, mais tout de même)...
Pour le voir en-ligne : https://www.youtube.com/watch?v=AfI_0GzLWQ8
Le Panel qui fermera le tout pour moi sera Paying for Lunch: C++ in the ManyCore Age, modéré par Herb Sutter et ayant pour panélistes Pablo Halpern, Jared Hoberock, Artur Laksberg, Ade Miller, Gor Nishanov et Michael Wong.
Herb Sutter présente les membres du Panel et leur pedigree
Début du contenu technique
Herb Sutter : pourquoi est-ce important, et quel est le plus important problème à résoudre?
Jared Hoberock : le plus gros problème est de rendre ça plus facile. C'est un problème d'éducation. Faut l'enseigner plus tôt.
Michael Wong : je veux que C++ demeure pertinent; parallélisme et la concurrence sont clés en ce sens. C++ ne sert pas qu'à écrire des pilotes.
Artur Laksberg : on sous-utilise nos machines.
Herb Sutter : et les téléphones?
Pablo Halpern : ça compte. Ces appareils ont un GPU et deux coeurs. Travailler plus longtemps, ça consomme plus de pile. Le plus gros défi que nous avons est que les substrats à notre disposition changent beaucoup. La simplicité est importante
Question : parallèlisme vs concurrence?
Pablo Halpern : concurrence : on partage des ressources, on communique. Parallelisme : exploiter le matériel en faisant plusieurs choses en même temps. Le basketball est concurrent, le 8000 mètres de course est parallèle
Question : devrait-on attendre que le matériel se stabilise avant de s'y mettre?
Jared Hoberock : mieux vaut l'apprivoiser
Michael Wong : j'ai comparé plusieurs modèles de programmation parallèle, et ce qu'on a de part et d'autre commence à converger (OpenMP, AMP et autres)
Ade Miller : il y a des patterns aussi, qu'on comprend et qu'on peut implémenter sur plusieurs architectures. Je vous encourage à réfléchir vos solutions à un plus haut niveau d'abstraction
Pablo Halpern : si on n'amène pas de modèles logiciels de qualité, les gens de matériel vont en faire et ce sera de la bouette (je paraphrase à peine)
Gor Nishanov : (j'ai pas compris)
Question : pourra-t-on allouer et gérer des coeurs comme on le fait avec la mémoire?
Michael Wong : on peut déjà le faire. Ce qu'on veut est une métaphore homogène et élégante
Jared Hoberock : le parallélisme et la localité sont la pointe de l'iceberg
Herb Sutter : le fait qu'on appelle thread::max_concurrency() est-il un signal d'échec?
Michael Wong : oui, peut-être... Ça dépend des besoins des usagers
Jared Hoberock : ça prend de bonnes valeurs par défaut. Je travaille surtout avec les GPU...
Une question de l'assemblée demande pourquoi on a tant de flexiblité. Pablo Halpern relate la complexité et la diversité du matériel visé
Question : à propos de std::async, que fait-on?
Artur Laksberg : on a essayé de s'en débarasser. Ça va rester. On compte offrir de meilleures options
Michael Wong : il y a des gens qui estiment que async fait bien son travail aussi
Question : nos outils de débogage s'améliorent-ils?
Pablo Halpern : mais oui! On peut instrumenter avec Clang, détecter certaines courses... Pour le moment, l'instrumentation intrusive est utile
(les gens qui posent des questions sont parfois difficiles à comprendre, et il en va de même pour certains des panélistes)
Question : peut-on configurer std::thread un peu plus? Si je voulais choisir le Stack Size, par exemple... (note personnelle : c'est une question typique des gens qui font des jeux vidéo, ça)
Michael Wong : écrirais-tu une proposition?
Herb Sutter : par native_handle()?
(une question vient à propos de ce que les compilateurs peuvent automatiser; Michael Wong explique pourquoi, du moins en C++, c'est extrêmement complexe)
(une question vient à propos de la sécurité; Michael Wong parle de fonctions pures et de fonctions Transaction-Safe; Artur Laksberg ajoute que const fait un peu ça aussi)
Herb Sutter : quel est le principal problème de nos usagers?
Jared Hoberock : nous il existe beaucoup de code qui ne peut être parallélisé, n'ayant pas été pensé pour cela
Ade Miller : la qualité de l'éducation est un obstacle
Michael Wong : en particulier le découpage d'un travail complexe en tâches, mais de manière à ne pas assassiner la Cache. Avec les transactions, j'essaie de sauver la programmation générique
Question : en programmant le GPU, le transfert des données tue souvent les gains. A-t-on une solution?
Jared Hoberock : nous sommes agnostiques sur ce point, mais la solution est un allocateur
Pablo Halpern : ça me semble être une voie de contournement pour un problème d'abstraction, un peu comme la question de la Cache, qui nous impacte mais sur laquelle le langage est muet
Question : et quel est le futur de .then()? (un jeu de mot geek!)
Artur Laksberg : c'est dans le Concurency TS. C'est implémenté dans PPL si tu veux expérimenter. Boost.future est un autre cas de futures avec continuations
Gor Nishanov : mes Stackless Resumable Functions font un truc équivalent
Artur Laksberg décrit les exécuteurs
Question : et pour distribuer sur plusieurs machines?
Pablo Halpern : je n'ai rien vu passer au standard à ce sujet. Il nous manque d'expertise en ce sens. On en a besoin. MPI existe mais... (note personnelle : ça pourrait être ma contribution...)
(discussion sur des manières d'aider des outils à voir si le traitement d'objets dans le code source est cohérent dans sa synchronisation)
Ade Miller recommande Parallel Programming with C++, disponible gratuitement en ligne
Pablo Halpern dit qu'on vise toujours des abstractions minces, mais qu'il n'est pas sûr qu'on fasse les choses correctement dans le cas du parallélisme
Gor Nishanov : on ne commence pas par une abstraction. On commence par résoudre un problème, puis on réfléchit aux abstractions appropriées
Question : comment encourager un bon modèle de programmation à partir des fonctions du standard? Elles me semblent encourager un seul modèle de programmation...
Pablo Halpern : C++ 11 parle de threads et de mutex. C'est à bas niveau, et nous le savons. Nos groupes de travail veulent nous amener plus loin
Herb Sutter : que devrait-on retenir?
... mutisme...
Michael Wong : il faut penser à la décomposition en tâches parallèles
Jared Hoberock : je cherche le bon niveau d'abstraction
Pablo Halpern : choisissez parmi les outils existants (incluant les trucs comme TBB) et essayez de voir si ça peut vous aider, le temps que le standard se stabilise.
Artur Laksberg : on règle des problèmes différents du passé?
Gor Nishanov : comment harmoniser asynchronisme et RAII
Ade Miller : regardez les choses à un haut niveau d'abstraction d'abord
Fin du contenu technique
Herb Sutter : merci d'être venus. C'était le festival de C++ le plus grandiose que j'aie vu. Merci aux 200+ bénévoles
Herb Sutter : merci spécial à Jon Kalb
Jon Kalb ferme le tout vers 15 h 20. Il demande qui est épuisé, qui a appris quelque chose... Toutes les mains se lèvent dans chaque cas. Qui a des collègues qui bénéficieraient de venir ici? (idem)
Je dois quitter. Kate Gregory, qui est venue profiter du courant avec moi, transmettra mes félicitations à Jon Kalb.
À l'aéroport, 17 h heure de Seattle. En cherchant une prise de courant, je suis tombé sur Louis Dionne, qui sera sur les mêmes vols que moi, outre le fait qu'une fois à Montréal, il lui restera un transit vers Québec à faire. Il semble satisfait de son séjour, mais me dit être plus dans le moule C++ Now (ce qui était BoostCon), un colloque d'auteurs de code de bibliothèque, que dans celui de cppcon, de haut niveau mais plus généraliste (généraliste au sens de « qui vise à couvrir le langage dans son ensemble »).
Za, tu serais fière de moi: j'ai pris le temps de souper. C'était très ordinaire (un peu de légumes sautés, du général Tao tiède, des nouilles surcuites et du riz un peu mou), mais étant donné le coût d'un repas dans l'avion, j'ai décidé que ce serait un investissement.