Guillaum a écrit 472 commentaires

  • [^] # Re: Bémol

    Posté par  (site web personnel) . En réponse au journal Optimisez votre code !. Évalué à 9.

    Dans mon industrie, on cours après les % de performance qui peuvent impacter le coût d'un projet d'une manière non significative (le coût du calcul est souvent supérieure à la masse salariale de nos projets). Ceci impact directement notre façon de programmer. On passe du temps à optimiser pour 5%, on est pret a transformer un code générique de 3 lignes en 2000 lignes d'une soupe infâme pleine de cas particuliers.

    Après de nombreuses années dans cette industrie, je ne sais toujours pas quel est la stratégie gagnante. Si on est 10% plus lent que la concurrence, on perd des clients, mais si on prend 6 mois a mettre en place un nouvel algorithme parce que le code est trop complexe, on perd des clients aussi.

    Et sinon, pour la blague sur la RAM, on s'amuse à quantifier des float sur moins de bits (en fonction du contexte), parce que 1 ou 2 bits à droite ou à gauche, au final cela fait quelques Giga de RAM en plus ;)

  • [^] # Re: Existant ?

    Posté par  (site web personnel) . En réponse au journal Calculs numériques en précision absolue.. Évalué à 5.

    Sachant que libgmp est massivement utilisée, c'est au moins le support des "bigint" et "rational" en python et Haskell, sans doute aussi dans d'autres langages.

  • [^] # Re: Écrivons en français

    Posté par  (site web personnel) . En réponse au journal Java 9 est dehors. Évalué à 8.

    Il est électricien ?

  • [^] # Re: AH ah ah ...

    Posté par  (site web personnel) . En réponse au journal Java 9 est dehors. Évalué à 2.

    Donc un langage fortement et statiquement typé, mais avec de l'inférence de type correcte pourrait te plaire ?

    J'utilise tous les jours Haskell pour "scripter": le code est concis grâce à l'inférence, mais j'ai beaucoup plus d'outils à ma.disposition pour m'exprimer et économiser du temps tout en rendant le code un peu plus robuste. (Les scripts qui plantent au bout de 45 minutes à cause d'une variable mal nommée cela m'énerve ;)

  • [^] # Re: Structures non mutables performantes

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 3.

    Au sujet de la gestion automatique de la mémoire, je dois dire que je n'y connais pas grand chose. Tout comme sur le fonctionnement interne des CPU et de leur cache.

    What every programmer should know about memory est une lecture vivement conseillée dans ce cas.

    En revanche, j'ai reréfléchi à ton implémentation de update et je doute qu'elle soit optimale pour l'usage de cette structure. […]

    Il est vrai qu'une suite de set vas créer une cascade de Diff qu'il faudra résoudre lors du premier get. En gros, le premier get est en O(n) avec n le nombre de set fait pendant ce temps. Mais en analyse amortie, je pense que c'est équivalent, puisque le temps "gagné" à ne pas faire le reroot lors de chaque set sera "utilisé" lors du premier get.

    Par contre, cela change pas mal le comportement en mémoire. Dans mon cas, le garbage collector ne vas pas supprimer les n Diff intermédiaires, tant qu'il n'y aura pas de reroot, même si les vecteurs précédents ne sont pas utilisés.

    Quoi qu'il arrive, cette structure se comporte très mal quand tu utilises les anciennes copies. cela coûte très chère si tu essayes d’accéder consécutivement à deux version assez éloignées, car il faut reroot en permanence entre les deux, avec un O(n), n étant le nombre d'étape séparant les deux.

    En Haskell, dans mon implémentation paresseuse, c'est encore plus drôle si tu met à jour des cases avec le contenu d'un vecteur précédant (voir futur si tu as envie, c'est la beauté d'Haskell). Imagine le code suivant :

    vec = ... -- on a un vecteur initialisé
    vec2 = update vec 0 (index vec 0 + 1) -- incremente la case 0
    vec3 = update vec2 0 (index vec2 0 + 1) -- incrémente encore la case 0
    print vec3

    chaque opération est paresseuse, donc en fait, vec2 et vec3 ne sont que des opérations paresseuses. Quand tu vas vouloir afficher vec3, il vas d'abord construire le Diff sur vec2 qui est un Diff sur vec. Puis, pour afficher la case 0, il va reroot sur vec3 jusqu'à réaliser que la case 0 c'est index vec2 0 + 1, donc il va reroot sur vec2, pour réaliser que c'est une fonction de vec, donc il vas reroot sur vec. Fin de l'évaluation paresseuse de la case 0. Pour passer à la case 1, il doit de nouveau reroot sur vec3, et là c'est bon. On peut bien évidemment contrôler ce comportement en forçant l'évaluation paresseuse judicieusement, mais quoi qu'il arrive il y aura des comportements tordus.

    Par contre, j'ai benché mon code en ajoutant des lectures, puisque sinon la seule chose que on faisait c'est empiler des création de Diff, et il est clair qu'il y a une différence non négligeable de performance car le reroot doit être effectué, et en fonction du pattern d'utilisation, les performances peuvent être catastrophiques.

    En gros, nous avons une structure persistante qui remplace un vecteur, mais dont les performances se cassent la gueule en fonction du nombre de versions partagées, et dont la place mémoire dépend du nombre de versions partagées et du nombre d'étapes entre les versions partagées. Bref, j'en arrive à conclure que c'est une structure amusante, mais pas si performante que cela. Je pense que on peut faire beaucoup mieux avec un HAMT qui conserve des propriétés de modification / insertion en O(log n) avec une très grosse base pour le log. La librairie unordered-containers utilise une base 16 pour le trie.

    Après, j'utiliserais sans doute un vecteur mutable, ou, quand cela sortira, des vecteurs en type "linéaires".

  • [^] # Re: Structures non mutables performantes

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 2.

    Je suppose qu'en C++ la nécessité de recourir au weak_ptr doit venir du côté mutuellement récursif des deux type 'a t et 'a data.

    Cela vient du fait que que C++, les shared_ptr doivent être "clonés" à partir d'un précédant shared_ptr pointant vers le même objet, d'où le besoin de concerver cette information dans un weak_ptr. Dans une implémentation ou la gestion de la mémoire est assurée par un garbage collector traçant, le problème est tout autre, puisque pour cloner une "référence" vers soit, il suffit de cloner son adresse, et le GC traçant, lors de sa passe de recherche, trouvera qu'il existe plusieurs références vers la même adresse. Avec les avantages et inconvénients des deux méthodes : le shared_ptr n'a pas besoin d'un scan entier de la mémoire pour fonctionner.

    Je suis étonné qu'une copie complète soit plus rapide sur de petits vecteurs, j'aurais parié le contraire (je suis surtout étonné par l'écart de temps : 4 ms vs 30 ms)

    Copier un petit tas de pointeur, t'as toutes les chances que cela tombe dans une ligne de cache, donc en gros c'est assimilé à une unique lecture / écriture en RAM, ce qui est assez rapide. Cela doit sans doute venir de là.

    Néanmoins, le code de ta fonction update est « erroné »

    Oui, en effet, il est différent du tient, je t'avoue ne pas avoir recopié, mais implémenté from scratch en me basant sur ce que j'avais compris ;)

    C'est un compromis différent, plus "paresseux" puisque le reroot n'est fait qu'au moment de la lecture de la structure. Je ne pense pas que cela change grand chose au final.

  • [^] # Re: Structures non mutables performantes

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 4.

    Ça dépend où tu as fait tes études.

    En effet. Dans mon école d'ingénieur on passait beaucoup de temps à faire du chiffrage et de la planification de réunion, mais peu de temps à faire de la technique ;( Il parait que cela a changé depuis.

    J'en ai bouffé des structures de données, et nos TDs / TPs consistaient à coder les structures de données classiques (listes, arbres binaires équilibrés) en C++ / Java.

    Pour être passé du coté obscure de la force et enseigner à mes heures perdues, je suis impressionné par le nombre d'étudiants qui arrivent en master 2 avec aucune notion de complexité des structures de donnée.

    Les cours de structures de donnée que j'ai pu donner en licence sont souvent mélangés avec d'autres problématiques, comme les templates en C++., ou le réseau, ou l'image. Au final le message est dilué et seul les étudiants curieux qui m'offrent un café pendant le TD ont le droit à mes discussions enflammées sur les structures de donnée.

    J'ai souvent tendance à dire "non mutable" en place de "persistante" pour la simple et bonne raison que souvent personne ne sait ce que c'est. Ainsi "non mutable" provoque souvent la question "mais comment on fait pour la modifier si elle est non modifiable", alors que persistante, c'est souvent confondu avec, comme tu le dis, les moyens de persistances types BDD ou sérialisation.

  • [^] # Re: Structures non mutables performantes

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 3.

    La réciproque est fausse, à savoir qu'il existe des structures de données persistantes faisant usage d'effets de bord.

    J'ai eu cette révélation quand j'ai commencé à faire de la Memoization en Haskell.

    Tu as bien compris ce que je raconte, la liste l partagée entre l1 et l2 peut être vue comme un shared_ptr. Sauf que en Haskell (et je crois en OCaml), la gestion de la mémoire est faite avec un GC traçant, alors que là en C++ c'est un comptage de référence, avec ses avantages et inconvénients.

    Tu as bien compris l'usage de shared_ptr, sauf que dans ce cas de figure c'est loin d’être trivial puisque en fait il faut garder un weak_ptr sur chaque shared_ptr… Bref, je me suis amusé un peu et cela donne ça :

    https://gist.github.com/guibou/73c33d9e511385f4c52b313cb3d4d0e6

    Version C++ et Haskell.

    La version C++ est peu propre, il y aurait pas mal de choses à traiter pour faire un vrai truc qui marche. La version Haskell et pas mal mais fait peur car en Haskell il faut faire un choix :

    • Pas d'effet de bord, c'est facile
    • Des effets de bord, ils sont alors visible par le biais de :
      • IO, des effets de bords
      • ST, des effets de bords qui sont limités à une fonction et que le compilateur peu garantir comme étant limités à une fonction.
      • unsafePerformIO, des effets de bords qui ne sont pas limités à une fonction et pour lequel c'est ton boulot de développeur de garantir que tu ne fais pas n'importe quoi. C'est ce que j'ai essayé de faire, mais sans garantie ;)

    Note, si mon benchmark n'est pas faux (j'ai des doutes quand je manipule des effets de bords de ce type), alors :

    • modifier un vecteur non mutable (i.e.: créer une copie) c'est super rapide sur des petits vecteurs, mais cela devient linéairement lent avec la taille du vecteur qui augmente
    • modifier un vecteur avec ta technique c'est en O(1)
    • modifier un vecteur "vraiment" mutable vue de l’extérieur est un poil plus rapide.

    Merci pour la discussion, je me suis amusé.

  • [^] # Re: Haskell pour les nuls

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 2.

    Cool, vas y, je jouerais avec ;) Je suis près à parier une bière sur le résultat ;)

  • [^] # Re: Structures non mutables performantes

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 2.

    Le principe consisté à conserver toutes les opérations de transformations effectuées sur une structure modifiables à la manière d'un gestionnaire de version. C'est ce que tu fais aussi avec tes graphes ? D'ailleurs l'exemple des tableaux persistants est tiré du livre de mon commentaire précédent.

    J'ai regardé ton exemple, et si je comprend bien, tu maintient une liste de diff, mais dans le sens inverse. C'est à dire que reroot applique l'ensemble des diff sur le tableau, et modifie les références vers les anciens tableaux pour remplacer ceux-ci par un diff. Ce qui veut dire que si tu accèdes alternativement à un ancienne et une nouvelle version du tableau, tu passes ton temps à faire de la cuisine interne de pointeur pour que le tableau réellement stocké soit à un moment l'ancien et à un moment le nouveau ? Si tu pensais que les shared_ptr était un moyen d'obtenir un pointeur sur un pointeur et de pouvoir modifier le pointeur interne, ce n'est pas le cas. Sinon je ne vois pas trop comment utiliser des shared_ptr dans ton implémentation.

    Un shared_ptr n'est rien d'autre qu'un pointeur qui est responsable de la durée de vie de ce qui est pointé. Quand le shared_ptr est copié, le compteur de référence est incrémenté (atomiquement). Quand il est détruit, le compteur de référence est décrémenté, et si il atteint 0, la valeur pointée est aussi détruite. C'est un garbage collector à comptage de référence. Il y a quelques subtilités en plus car le shared_ptr peut pointer sur un objet et maintenir la durée de vie d'un autre objet, c'est pratique si tu veux pointer directement sur les éléments d'une grosse structure et que tu veux garder la grosse structure en vie tant qu'il existe un pointeur sur l'un de ses enfants. C'est un truc inutile dans un contexte avec un garbage collector, puisque c'est le garbage collector qui fait ce travail. Mais en C++, c'est le développeur qui est responsable de la durée de vie de ses éléments alloués sur le tas, et la mode est de plus en plus à l'usage de unique_ptr et shared_ptr pour simplifier ce travail. Les shared_ptr sont un peu l'équivalent du GC de Haskell / OCaml et les unique_ptr sont un peu l'équivalent de la gestion de ressources sur des types linéaires (Grosse simplification, on vas un jour me ressortir cela en entretien et j'aurais honte ;)

    Ce que je fais avec mes graph, ils sont non mutables. Quand j'en veux un nouveau, j'en crée une copie intégrale en réutilisant un maximum l'ancien graph. Dans le pire des cas, c'est en O(n) avec n la profondeur maximum du graph. Et je met ce nouveau graph dans ma liste d'opérations effectuée.

    En gros, j'ai une liste [graph0, edition (graph0), edition' (edition (graph 0))], mais en pratique l'utilisation mémoire n'est pas équivalente à 3 graph. Après il suffit de bouger le "pointeur" (qui peut être un zipper) sur cette liste pour annuler / refaire. La seule subtilité c'est ce qui se passe quand une nouvelle édition arrive alors que on est pas en queue de liste. Soit on détruit les états suivants (le plus classique). Example, si on annule et que on fait edition'', la liste sera [graph0, edition (graph0), edition'' (edition (graph 0)]. Mais on peut aussi crée un arbre, tel que :

    - graph0
       - edition (graph0)
            - edition' (edition (graph0)
            - edition'' (edition (graph 0))
    

    Mais c'est dur à conceptualiser pour les utilisateurs, soit on peut considérer que l'annulation EST une opérations, et donc obtenir la liste suivante : [graph0, edition (graph0), edition' (edition (graph0)), edition (graph0), edition'' (edition (graph0))]. Ce qui est amusant c'est que chaque étape ne coûte rien en mémoire.

    Dans ce contexte, j'utilise des shared_ptr pour les pointeurs vers chaque noeuds du graph. Quand je réutilise un nœud dans le graph suivant (ce qui arrive très souvent), alors le shared ptr est incrémenté. En gros cela me gère gratuitement la durée de vie des noeuds de tous les états de mon arbre.

    Quand il est tant de faire un peu de ménage, je peux par exemple supprimer les éléments les plus vieux de mon historique. Soit ceux-ci sont encore utilisés par un graph plus récent, et ils sont conservé, soit ils ne le sont pas, le compteur du shared ptr tombe à zéro, et l'élément est supprimé et récursivement le même mécanisme est appliqué à tous ses enfants.

    Ce qui est beau avec cette approche c'est qu'il n'est pas nécessaire de stocker la liste des opérations et il n'est pas nécessaire de réfléchir à comment inverser une opération, le code est assez trivial puisque il consiste en une gestion d'une pile d'opération, et chaque nouvelle opération crée une copie modifié du graph et ajoute celle ci dans la pile.

    Merci pour la référence bibliographique, je lirais cela dans le train ;)

  • [^] # Re: Structures non mutables performantes

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 3.

    Très bon ;)

    Je complète avec un exemple d'usage que j'ai eu en vrai. Je manipule un très gros graph dans une interface graphique. Tous les paramètres de l'interface, jusqu'au champs qui a le focus, sont stockés dans un arbre. Pour info, une fois sérialisé (en binaire), le graph fait plusieurs centaines de Mo, voir quelques Giga. Chaque modification crée une nouvelle copie de cette structure et est ajouté dans une liste de modification (en fait c'est un graph). Faire annuler revient à revenir en arrière d'un cran dans le graph. Faire "redo" revient à avancer en avant d'un cran dans le graph. En moyenne chaque copie du graph coûte O(log(n)) en mémoire, ce qui est négligeable et permet de conserver une grande quantité d'étape d'undo/redo. On peut même les sauvegarder avec le fichier. C'est gratuit à faire avec des structures persistantes, modulo qu'en C++ je dois me balader avec des shared_ptr de partout, mais cela fonctionne.

  • [^] # Re: Haskell pour les nuls

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 6.

    En toute humilité je n'ai pas le niveau intellectuel suffisant pour avoir saisi, ne serait-ce que le sens général, du concept de fusion en Haskell […]

    C'est un mécanisme qui permet de supprimer des opérations intermédiaires lors d'un calcul. Ainsi un calcul décrit avec de nombreuses structures de donnée temporaires peut au final ne rien allouer.

    Que Haskell soit tout à la fois un langage de programmation et un compilateur est une nouvelle donne.

    Pour différents langages, il existe différents moyen de faire exécuter le programme par un processeur. On peut interpréter ce code, ou le compiler en natif pour la machine ciblée. Pour de nombreux langage, il existe autant d’interpréteur que de compilateur. Il existe des interpréteurs Haskell et des compilateurs Haskell, il existe des interpréteurs C et des compilateurs C. En fait, tous les langages qui ont un frontend pour LLVM ont un interpréteur et un compilateur qui s'appelle LLVM. Ainsi, il existe des interpréteurs pour C, C++, Fortran, …

    Le gros avantage des interpréteurs est qu'ils lissent toutes ces contingences puisqu'ils créent leur univers et donc les règles qui leur sont propre ;

    Je ne comprend rien là.

    où j'ai un souci avec les interpréteurs en général (mais je n'ai jamais essayé Haskell donc je n'ai pas d'opinion à son sujet)

    Tu as bien compris que Haskell n'est qu'un langage qui peut être compilé ou interprété en fonction des besoins ?

    c'est quand on a à générer un très grand nombre de fichiers en lecture / écriture. En ce cas l'interpréteur doit sortir de son monde intérieur pour se tourner vers le système d'exploitation et il me semble bien que c'est une situation où les performances s'effondrent.

    Quand tu lis un fichier, que ce soit en python, en Haskell, ou en C, ton dénominateur commun c'est l'appel système et à peu de choses près, tes performances dépendent de cet appel système.

    En ce cas, on tombe dans un traitement séquentiel par lot et la performance tombe à cause du double traitement des I/O (une fois dans l'interpréteur et l'autre dans l'appel système sous-jacent).

    Mais l’interpréteur ne fait RIEN de plus que faire un appel système. Il y a des fois une surcouche pour réaliser l'appel, mais celle-ci ne coûte souvent presque rien comparé au coût de l'appel système.

    Tu as des benchmark à l'appui pour ce que tu dis ? Je serais vraiment interessé.

  • [^] # Re: Haskell pour les nuls

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 5.

    Le problème de la discussion que nous avons c'est que tu ne veux pas accepter qu'un interpreteur, ou un langage avec un gros runtime (comme Haskell) peut donner de très bonnes performances.

    Certains langages de haut niveau permettent d'exposer des optimisations qui ne sont pas possibles dans d'autres langages, comme la fusion en Haskell.

    D'autres langages interprétés, comme python, java, php, lua, javascript, utilisent la compilation à la volée pour générer à l’exécution du code natif performant. Cela marche assez bien.

    Alors oui, on ne battra surement jamais de l'assembleur optimisé à la main pour un problème précis par un spécialiste (bonjour le niveau du spécialiste), mais est-ce que cela vaut le coup ? Si avec un code robuste / portable / maintenable de 10 lignes tu fais "juste" 10% mois vite qu'un code de 200000 lignes non portable et que personne sans 4 PhD ne peux comprendre, est-ce que cela vaut le coup ?

  • [^] # Re: Haskell pour les nuls

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 2.

    Merci, je viens d'apprendre que on pouvait linker le RTS en dynamic ;)

  • [^] # Re: Haskell pour les nuls

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 6.

    Mon agacement au sujet des langages interprétés (que ce soit avec interpréteur externe ou runtime embarquée dans l'exécutable) est qu'ils font confusion entre les notions de programmation et de développement.

    Puis-je te demander de développer un peu ? Je trouve que la différence entre langage interprété et compilé est de plus en plus faible. Chaque programme, quel que soit le langage qui a permis de le "compiler" embarque un "runtime" fournissant les fonctions de bases nécessaires à l’exécution d'un programme. Celui du C est minime, mais il existe. Celui du C++ est plus gros, mais reste raisonnable. Celui d'Haskell est bien plus gros, et cela est acceptable ou non en fonction du contexte, mais j'ai du mal à saisir ton point de vue.

    Ayant biberonné avec Basic, j'ai eu à pâtir de cette ambiguïté et en définitive éprouvé des frustrations dès que l'on pousse le langage dans ses limites. Je pense qu'il est malhonnête de présenter un langage comme relevant de la programmation alors qu'il est définitivement limité au développement.

    Tu peux développer encore ici la différence que tu fais entre "programmation" et "développement" ?

    Ou, pour parler plus clairement, si l'on souhaite étendre les fonctionnalités de base par modules à l'aide de ce même langage, on provoque des instabilités au delà d'un certain niveau d'imbrication des modules à l'intérieur des modules. Ce genre de choses se voit couramment en TeX/LaTeX. Ceci n'a pas trop d'importance pour quiconque ayant fait de longues études en informatique (car pouvant toujours se rabattre sur le langage C ou un assembleur). Mais, pour un hobbyiste de mon genre, quel temps perdu à apprendre un langage qui se révèle être une impasse !

    Personnellement, j'ai eu moins de surprises et de problèmes avec langages "interprétés" (Comme python, lua), ou avec de gros "runtime" (comme Haskell, Java (je ne sais pas trop ou mettre java, c'est interprété ou compilé ? ;)) qu'en C++, et pourtant je gagne ma vie en faisant du C++.

    Selon quels critères qualifies-tu un langage d'impasse ? Si tu te qualifies d'hobbyiste, je suis près à parier que n'importe quel langage (et implémentation) doit pouvoir convenir à ton besoin tant que tu n'as pas des problématiques d'embarqué ou de performance particulières.

    Ceci me fait prendre conscience qu'il serait opportun de rédiger une dépêche sur le Fortran moderne.

    Si tu te sens, commences dans l'espace de modération, d'autres suivront surement. Bon courage.

  • [^] # Re: Structures non mutables performantes

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 4.

    Okasaki […] il est complètement inutile dans le cas d'Haskell qui réalise l'évaluation paresseuse par défaut.

    Je ne suis pas forcement d'accord, la lecture du livre (j'ai lié sa thèse dans la dépêche, le livre est une version restructurée de celle-ci) m'a permis de comprendre les problématiques d'analyse de complexité amortie de ces structures dans un contexte paresseux, ce qui est carrément applicable à Haskell. Si je ne m'abuse, le livre fourni en annexe une version (datée) en Haskell de tous les codes.

    Cependant je suis d'accord avec toi qu'il s'agit d'une lecture purement théorique, peu sont les informaticiens qui auront à implémenter une structure de donnée (qu'elle soit mutable ou non, paresseuse ou non) étant donné la qualité de celles qui existent déjà dans les librairies accessibles dans tout langage. De plus cette lecture est totalement dépassé dans le sens où dans la vraie vie il y a d'autres problèmes (typiquement le cache) qui entrent en jeu dans les performances d'une structure de donnée. D'ailleurs je cherche encore une ressource accessible traitant de façon pertinente de l’implémentation de différentes structures de donnée (lazy ou non / mutable ou non) efficace sur des architectures de CPU modernes.

    J'ai voulu cependant introduire un peu le concept et fournir une bonne ressource car je trouve que on néglige trop souvent les structures de donnée dans l'apprentissage de l'informatique, et on néglige encore plus les structures non mutables qui offrent des propriétés très intéressantes.

  • [^] # Re: Quelques erreurs (mineures) dans le programme des philosophes

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 3.

    Bonnes remarques. Il s'agit de petits changements de dernière minute que je n'ai pas testé dans le fichier original. Honte sur moi.

    Pour la petite histoire, j'ai remplacé dans le main tIds <- mapM_ (forkIO . forever . runPhilosopher) philosophers par tIds <- mapM_ forkPhilosopher philosophers afin de simplifier la compréhension et ne pas devoir introduire / expliquer l'opérateur (.) ou une lambda ;)

    Pour Options.Generic, j'ai tout simplement remplacé un gros :

    args <- getArgs
    let nPhilosopher = case args of
       [] -> error "Vous devez fournir un argument"
       (x:_) -> case readMaybe x of
                    Nothing -> error "Cela doit étre un int"
                    Just i -> i

    Par nPhilosopher <- getRecord "bla bla bla".

    Cela m'apprendra ;)

    PS: à quand un compilateur dans linuxfr qui vérifie le sens des article, le code, et la grammaire ? ;) J'en aurais eu bien besoin. J'en profite pour remercier tous ceux qui ont fait un travail de relecture, sans eux cette daipaich auré aitè plu dur a lir. Merci.

  • [^] # Re: Haskell pour les nuls

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 6.

    Tu peux facilement utiliser un FFI pour aller appeler n'importe quelle fonction d'un autre langage. Haskell est même "premier de sa classe" dans ce domaine avec de superbes libraires de FFI pour s'interfacer avec du C, du C++ (là j'ai des doutes), du java, du python, du fortran, du R, et j'en passe. L'interface avec du C est aussi trivial que de taper ton code C au milieu de ton code Haskell avec le module inline-C (regarde les exemples).

    J'ai vu un tutoriel où, dans le cas d'un main en C, la procédure écrite en GHC doit au préalable être initialisée (ce qui me faisait pencher pour un interpréteur embarqué).

    Il faut initialiser entre autre le ramasse miette pour que ton code Haskell puisse allouer / désallouer de la mémoire. Après ce serait un interpréteur cela ne me choquerais pas tant que cela quand on vois les performances dingues qu'un interpréteur JIT peut donner (il n'y a qu'à voir Java ou PyPy). Bref, je pense qu'en dehors de certains contextes limités en mémoire, il ne faut pas cracher sur les interpréteurs. (Mais bon, GHC fait tout de même du code natif).

    Il en découle qu'il ne semble pas avoir d'avenir pour GHC pour jouer avec des interfaces graphiques (GTK ou QT) qui sont le plus souvent écrites en C/C++ .

    Il y a des bindings Haskell pour GTK, Qt, wxwidget, fltk, OpenGL, … et j'en passe. J'ai peu de retour à faire, (je n'aime pas faire des interfaces graphiques, alors le peu que je fais je le garde pour le boulot et là c'est en LUA ;), mais le code gtk en Haskell que j'ai vu avait l'air plutôt acceptable.

    Bien que les librairies d'interface graphique soient souvent écrites en C ou C++, c'est souvent un autre langage qui tape dedans, python, lua, …

    Concernant GHC en tant que substitut au C++ dans le domaine du calcul intensif, je m'interroge : quitte à s'investir intellectuellement dans l'apprentissage d'un nouveau langage, pourquoi ne pas préférer Fortran (version F2015, pas F77 !) ?

    Du peu que je connais de Fortran, j'ai l'impression que Haskell à une syntaxe et un système de type bien plus agréable à utiliser, mais je peu me tromper ;)

  • [^] # Re: Haskell pour les nuls

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 5.

    GHC est autant un interpréteur qu'un compilateur. Dans ton cas tu as compilé ton code Haskell en code natif. Cependant le code Haskell compilé avec GHC vient avec un "runtime" qui gère, entre autre, les entrée / sorties, l'allocation de mémoire, le ramasse miette, le parallélisme, …, et presque tous ces points sont nécessaires même pour le plut petit programme.

    Ce qui disqualifie Haskell dans des environnement aux ressources limités, mais dans un contexte plus classique, Haskell (et GHC) sont capable de générer du code natif assez performant. Dans mes applications habituelles, je suis, dans le pire des cas, 3x plus lent que du code de calcul C++ pure, mais souvent je suis dans les 10/15% plus lent. À voir si c'est suffisant ou pas.

    Ceci étant dis, si tu veux continuer ton expérience Haskell, je te conseille de regarde "stack" qui s'occupera de la compilation / strip / gestion des dépendances pour toi.

  • [^] # Re: Super dépêche mais 2 remarques

    Posté par  (site web personnel) . En réponse à la dépêche Sortie de GHC 8.2.1. Évalué à 2.

    Merci pour la remarque sur Nix. En effet, Haskell n'est pas le langage universel, mais je pense qu'il n'y en a pas ;) Par exemple, dans mon métier j'ai un ENORME besoin de performance, et Haskell n'est pas, à l'heure actuel, capable de rivaliser avec du code C++ tunné au millimètre près.

  • [^] # Re: Déjà fait !

    Posté par  (site web personnel) . En réponse à la dépêche Multiseat avec des pilotes libres, non libres et systemd. Évalué à 4.

    Tu arrives à avoir des performances en jeu qui sont acceptable avec une seule carte graphique ?

    Ma seule raison de monter un multi-poste à la maison c'est quand je veux jouer à deux. Au départ j'ai essayé de bricoler une solution avec un seul serveur X, du multi pointer et en lançant deux instances du jeu dans la même session, cela marche très bien pour les claviers, mais les souris sont inversées de temps à autre, les fenêtres s'inversent les focus et ainsi de suite.

    J'aimerais vraiment pouvoir affecter un clavier / souris au focus d'une application seulement.

  • [^] # Re: Python

    Posté par  (site web personnel) . En réponse à la dépêche NixOS, collection printemps‐été 17. Évalué à 2. Dernière modification le 10 avril 2017 à 08:55.

    Tu as les packages python qui sont disponibles avec chacun leur hash

    $ nox numpy
    ...
    29 python3.5-numexpr-2.6.1 (nixpkgs.python35Packages.numexpr)
        Fast numerical array expression evaluator for NumPy
    30 python3.5-numpy-1.11.3 (nixpkgs.python35Packages.numpy)
        Scientific tools for Python
    31 python3.5-numpy-1.10.4 (nixpkgs.python35Packages.numpy_1_10)
        Scientific tools for Python
    32 python3.5-numpydoc-0.6.0 (nixpkgs.python35Packages.numpydoc)
        Sphinx extension to support docstrings in Numpy format
    ...
    

    Mais rien ne t’empêche d'utiliser un environnement virtuel au sein de nix. Je fais cela par exemple en Haskell car stack (l'outil d'env virtuel que j'utilise) apporte aussi d'autres choses intéressantes. Comme cela nix gère tes librairies externes, et ton env virtuel gère les lib du langage et le fait "peut-être" mieux.

  • # La même, mais en different

    Posté par  (site web personnel) . En réponse au journal Comment j’ai abandonné Debian.... Évalué à 10.

    2000, dans mes 30 m carrée de campagne (le salon de mes parents, j'avais 14 ans ;) j'ai installé une debian sur l'ordinateur familiale. Je n'ai jamais réussi à finir l'installation, alors j'ai installé une gentoo : que de souvenirs, la compilation de openoffice et xorg pendant 24 heures. J'ai gardé cette gentoo sur cet ordinateur puis les suivants jusqu'en 2009 où je suis passé à arch, presque aussi souple, mais largement moins contraignant.

    Mi 2016 je découvre nix, la solution à tous mes problèmes. Nix c'est un gestionnaire de package qui peut s'installer sur n'importe quelle distribution, je l'ai fais sur arch au départ. Il permet d'installer en parallèle différentes version de packages dans des environnements indépendants. C'est très pratique pour développer quand ton produit dépend de llvm 3.5, que ton temps libre avec haskell dépend de LLVM 3.7 et que ta distribution est packagée autour de de llvm 3.9. Sous arch, c'est soit tu build ton produit, mais t'as pas les outils llvm récent, soit tu as des outils récent, mais tu ne build pas le produit et quoi qu'il arrive tu ne fais pas de haskell ;) Et puis mes collègues sont sous ubuntu, suse, ubuntu (une autre), nos makefile de build devenant dramatiquement complexes … Avec nix, il suffit d'une petite description de dépendance et tu build de façon identique entre plusieurs machines.

    Un petit exemple :

    λ skolem ~ → nix-shell -p gcc49                                                                        ~
    
    [nix-shell:~]$ cc --version
    gcc (GCC) 4.9.4
    
    λ skolem ~ → nix-shell -p gcc5                                                                         ~
    
    [nix-shell:~]$ cc --version
    gcc (GCC) 5.4.0
    
    λ skolem ~ → nix-shell -p gcc6                                                                         ~
    
    [nix-shell:~]$ cc --version
    gcc (GCC) 6.3.0
    

    Pour la création de paquet, c'est assez trivial (une fois que t'as compris les blagues), c'est globalement un fichier .nix qui contient la nom, et les commandes pour build. J'ai convertis mes packets AUR d'archlinux vers nix en quelques minutes (Et j'en avais une bonne cinquantaine).

    Tout ça c'est juste nix, le gestionnaire de paquet (qui s'install aussi sur n'importe quelle distribution, macOS et plus ou moins windows), mais qu'en est il de nixos ?

    J'ai essayé et c'est la distrib de toutes mes machines depuis 2 mois.

    La documentation est mauvaise, voir inexistante et on rencontre certains problèmes étonnant, je n'arrive toujours pas à faire tourner gdm ou je perd le réseau à chaque mise à jour système, mais au final j'apprécie la configuration centralisée.

    On prend vite l'habitude de travailler différemment. Je n'ai presque plus de paquets installée au niveau système, j'ai pleins de profile différents en fonction de ce que je veux faire à un instant t, jouer (un profile avec wine / steam / … ), bosser (un profile avec mes outils de dev et les dépendances spécifique à mon travail), m'amuser (un profile avec haskell). Ma femme a sa session et l'administre comme elle veux en installant ses propres paquets.

    En résumé je suis super satisfait de nix (le gestionnaire de paquet). Pour Nixos, je lui laisse une chance plus importante, mais j'avoue que pour mes besoin ce n'est pas forcement une tuerie, je ne suis pas administrateur système, je réinstalle peu de machine. Mais c'est vrai que c'est tout de même confortable, si je change de machine, j'ai juste besoin de copier mon /etc/nixos/configuration.nix et de cloner les fichiers de config de mon home, et j'ai la même config. En fibre optique, c'est l'affaire de quelques minutes pour recommencer à travailler, vécu il y a une semaine à la mort prévisible de mon ordinateur portable, le nouveau est arrivé, 10 minutes plus tard je lancais des tests pour le boulot.

  • [^] # Re: Illustration

    Posté par  (site web personnel) . En réponse au journal De l'importance (des tests réguliers) des sauvegardes. Évalué à 5.

    Dans le genre d'histoire de type, sans être root (enfin, un peu quand même). J'ai installé mon premier linux (une gentoo) sur un disque IDE esclave, windows 2000 était sur le disque IDE maître. Au bout de 2 ans, j'ai décidé que gentoo méritait d’être sur le disque maître, j'ai donc inversé les branchement IDE, changé les entrée de montage dans /etc/fstab et c'était bon.

    Je boot ensuite windows en me demandant comment il survivrait au changement de configuration des disques. Après quelques seconde de grattage de disque, windows termine en erreur. Je redémarre sous linux, ba y avait plus de linux…. Je n'ai pas la moindre idée de ce qu'il a fait, mais il a trouvé le moyen d'écrire (sans doute du swap) sur le disque maître qui était avant à lui et qui était maintenant réservé pour gentoo.

  • [^] # Re: Illustration

    Posté par  (site web personnel) . En réponse au journal De l'importance (des tests réguliers) des sauvegardes. Évalué à 1.

    Saleté de containers collants ;)