Crystal, un langage proche de Ruby, en version 0.16

Posté par  (site web personnel) . Édité par Benoît Sibaud. Modéré par Benoît Sibaud. Licence CC By‑SA.
Étiquettes :
40
8
mai
2016
Ruby

Crystal est un langage de programmation, encore jeune. Il s'inspire de Ruby pour la syntaxe mais vise des performances proches du C. La version 0.16 vient de sortir, avec un nouvel algorithme pour l'inférence de types. À noter, le compilateur de Crystal est écrit en Crystal.

Voici à quoi ressemble un serveur HTTP basique écrit en Crystal :

# A very basic HTTP server
require "http/server"

server = HTTP::Server.new(8080) do |context|
  context.response.content_type = "text/plain"
  context.response.print "Hello world, got #{context.request.path}!"
end

puts "Listening on http://0.0.0.0:8080"
server.listen

Une syntaxe proche de Ruby

Comme on peut le voir dans l'exemple ci-dessus, la syntaxe est très proche de celle de Ruby. C'est un des objectifs du langage. Ceci dit, la compatibilité avec Ruby n'en fait pas partie.

On retrouve ainsi des classes similaires à celles de Ruby :

class Person
  def initialize(name : String)
    @name = name
    @age = 0
  end

  def name
    @name
  end

  def age
    @age
  end
end

Ou la notion de blocks :

def twice
  yield
  yield
end

twice do
  puts "Hello!"
end

Typage statique

Mais il existe également des différences avec Ruby. Par exemple, Crystal a un typage statique et des génériques :

class MyBox(T)
  def initialize(@value : T)
  end

  def value
    @value
  end
end

int_box = MyBox(Int32).new(1)
int_box.value # => 1 (Int32)

string_box = MyBox(String).new("hello")
string_box.value # => "hello" (String)

En général, l'inférence de types permet de ne pas spécifier les types des variables et méthodes, mais vous pouvez de manière optionnelle le faire :

def add(x : Number, y : Number)
  x + y
end

Bindings avec le langage C

Crystal permet de déclarer des bindings avec le langage C. Et, fait notable, ces bindings peuvent être déclarés en Crystal. Voici un exemple pris de la bibliothèque standard pour s'interfacer avec la libyaml :

@[Link("yaml")]
lib LibYAML
  alias Int = LibC::Int

  PARSER_SIZE = 480
  type Parser = Void*

  struct VersionDirective
    major : Int
    minor : Int
  end

  # ...

  fun yaml_parser_initialize(parser : Parser*) : Int
end

Génération et évaluation de code au moment de la compilation

Le meta-programming de Ruby est très puissant mais aussi très coûteux en performances. Crystal a donc choisi une autre voie : les macros. Celles-ci permettent d'éviter d'avoir à écrire beaucoup de code répétitif sans impacter de manière notables les performances.

Une macro reçoit un noeud AST et va générer du code lors de la compilation.

macro define_method(name, content)
  def {{name}}
    {{content}}
  end
end

# Va générer:
#
#     def foo
#       1
#     end
define_method foo, 1

foo #=> 1

Concurrence et parallélisme

Crystal permet actuellement de construire des programmes concurrents avec des primitives comme les fibers et channels, mais la jeunesse du langage se fait sentir sur ces aspects.

On peut séparer des tâches à effectuer dans des fibers. Chaque fiber a un contexte propre et est comparable à une goroutine en Go par exemple. Les fibers sont coopératives et ne vont passer la main à une autre fiber que de manière explicite (par opposition aux threads, qui sont pré-emptifs).

Il existe également une fiber un peu particulière, l'Event Loop, qui gère tous les I/O.

Enfin, les fibers communiquent entre elles via des channels.

require "socket"

channel = Channel(String).new

spawn do
  server = TCPServer.new("0.0.0.0", 8080)
  socket = server.accept
  while line = socket.gets
    channel.send(line)
  end
end

spawn do
  while line = gets
    channel.send(line)
  end
end

3.times do
  puts channel.receive
end

Ainsi, Crystal permet déjà de structurer son code pour gérer la concurrence, mais est limité de la même façon que le JavaScript pour le parallélisme : une seule fibre peut s'exécuter à un moment donné.

Compilation vers du code natif efficace

Crystal profite de LLVM pour compiler vers du code natif. Les performances sont au rendez-vous. Par exemple, dans ce benchmark, Crystal se retrouve entre C/C++ et Go/Node (et donc loin devant Ruby).

Aller plus loin

  • # Destructeurs

    Posté par  . Évalué à 3.

    Il s'inspire de Ruby pour la syntaxe

    Ce n’est pas un avantage à mon goût…

    Par contre, il a des destructeurs. J’espère donc qu’il supplantera Ruby.

    Cela dit, la mention d’un garbage collector sans précision sur son fonctionnement est un peu inquiétante, pas seulement pour le fonctionnement des destructeurs. Enfin peut-être celui-ci (voire celui de Ruby) fonctionne-t-il mieux que ce que j’ai eu l’occasion de voir avec d’autres langages (pas de libération de mémoire quand on en manque, lancement éventuel du garbage collector… au moment où l’en aurait besoin de performances).

    « Le fascisme c’est la gangrène, à Santiago comme à Paris. » — Renaud, Hexagone

    • [^] # Re: Destructeurs

      Posté par  (site web personnel) . Évalué à 8.

      Le Garbage Collector utilisé est celui de Boehm-Demers-Weiser.

    • [^] # Re: Destructeurs

      Posté par  (site web personnel) . Évalué à 7.

      Cela me rappelle un pattern tout con, que je n'arrivais pas à faire en ocaml. Je lisais une grande série de petit fichiers. En C, le réflexe est d'utiliser et réutiliser toujours le même buffer de taille "suffisante". Or c'est impossible en Ocaml car les string ne sont pas mutable. Les buffers le sont, mais il n'existe pas vraiment de fonction de parsing utilisant des buffers, à la place des string. Donc, il fallait paramétrer le gc pour ne pas qu'il se lance, résultat : 30% de perf en plus, mais explosion de l'usage de la mémoire.

      "La première sécurité est la liberté"

      • [^] # Re: Destructeurs

        Posté par  . Évalué à 8.

        Je suis un peu surpris par ton problème.

        D'une part, les strings ont pendant très longtemps été modifiables en OCaml—elles ne sont devenues immutables par défaut qu'en Août 2014, soit sans doute après ton problème, et ça reste une option qu'on peut désactiver. Même avec des strings immutables tu peux toujours utiliser le type Bytes des chaînes modifiables et convertir vers string pour passer à ta fonction de parsing.

        D'autre part, je ne sais pas de quelle fonction de parsing tu parles, mais il me semble que pas mal de lexers/parsers peuvent lire dans un Buffer—peuvent en fait être paramétrés par la fonction qui lit l'entrée. Par exemple le parseur OCaml lit l'entrée interactive du toplevel sans problème.

        • [^] # Re: Destructeurs

          Posté par  (site web personnel) . Évalué à 2.

          " et convertir vers string pour passer à ta fonction de parsing."

          Le problème est là, tu alloues plein de mémoire à ce moment-là. Ce ne sont pas des "slices".

          "La première sécurité est la liberté"

          • [^] # Re: Destructeurs

            Posté par  . Évalué à 3.

            Non, je ne crois pas qu'il soit nécessaire de copier de la mémoire à ce moment-là. Il me faudrait une définition plus précise du problème pour pouvoir en dire plus.

            • [^] # Re: Destructeurs

              Posté par  (site web personnel) . Évalué à 2.

              Je vais retrouver le pattern, si tu veux. C'est un code un peu ancien.

              "La première sécurité est la liberté"

            • [^] # Re: Destructeurs

              Posté par  (site web personnel) . Évalué à 2.

              Je ne retrouve plus le problème précisément. Le code est là :

              https://github.com/nicolasboulay/index2share/blob/master/src/meta.ml

              La fonction read peut être appelé des dizaines de milliers de fois. Je lis le fichier et je le convertis dans une petite structure de donné.

              "La première sécurité est la liberté"

              • [^] # Re: Destructeurs

                Posté par  . Évalué à 2.

                Je pense que tu pourrais faire tout ça avec un très petit nombre d'allocations. D'abord, lire tout le fichier dans un seul grand buffer (tu peux utiliser Buffer si tu ne connais pas la taille à l'avance, ou sinon Bytes). Ensuite, décoder ligne par ligne au lieu d'utiliser input_line, par exemple en utilisant un lexeur. Au final tu as besoin d'allouer au moins une liste pour la sortie, et une chaîne par ligne de sortie (le résultat de decode) si c'est ce que le code utilisateur attend.

                Je pourrais écrire ça si ça rendait service ou permettait de comparer vraiment, à mon avis ce ne serait pas tellement plus long que le code que tu as écrit ici; mais si tu n'as plus l'usage de ce code ce n'est pas forcément la peine. (Si tu en as l'usage, avoir un peu plus d'info sur le format (est-ce que les tailles correspondent au taille des lignes avant ou après pré-processing, ou à autre chose?), et un fichier d'entrée à utiliser pour des tests serait utile.)

                • [^] # Re: Destructeurs

                  Posté par  (site web personnel) . Évalué à 2.

                  C'est gentil, j'aurais été curieux de voir le résultat. Le logiciel est en version stable, mais n'a pas d'utilisateur :) Le code est plutôt très rapide (optimisation des appels noyaux), il scanne en profondeur les répertoires de façon très efficace.

                  J'ai voulu faire un système d'échange simple de la main à la main avec un système de fichier qui en représente de plus gros. L'interface a une seul action, mais peu de monde comprend le fonctionnement (cf la doc). J'avais posté ici, sans grand succès.

                  Si un jour, je suis motivé, je couperais le programme en 2, avec un binaire pour générer les petits fichiers, et un binaire pour lancer la copie. Cela pourra être utile si on veut réorganiser une arborescence complète, sans attendre la fin de chaque copie (on fait l'arborescence à la main avec les petits fichiers, puis une seul commande les remplacerait avec les vrais).

                  Si je me rappelles bien, j'ai un répertoire demo avec un script qui lance le binaire.

                  "La première sécurité est la liberté"

                  • [^] # Re: Destructeurs

                    Posté par  . Évalué à 4.

                    Si ça peut t'aider, j'ai rapidement deux remarques :

                    • quand on utilise toujours le même tampon, il vaut mieux utiliser Buffer.reset que Buffer.clean (voir la doc du module Buffer)

                    • tu parcours trop de fois le contenu de ton fichier : une fois pour le lire et le mettre dans une liste (via read_ic), une deuxième fois pour renverser la liste (toujours dans read_ic), et une troisième pour traiter chaque ligne avec decode (dans fast_read). Il vaudrait mieux le traiter en flux : voir le tutoriel sur les flux

                    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                  • [^] # Re: Destructeurs

                    Posté par  . Évalué à 3. Dernière modification le 11 mai 2016 à 15:07.

                    Autre amélioration possible pour ton code : utiliser une structure d'ensemble plutôt qu'une liste pour ton type t.path. Les ensembles sont codés par des AVL et offrent une complexité logarithmique en temps là où avec tes listes tu as une complexité linéaire.

                    Ce qui donnerait pour le début de ton code :

                    module SS = Set.Make(String)
                    type t = { size : Int64.t ; path : SS.t}

                    cela te permettra, entre autre, de supprimer la fonction uniq et de coder merge_update ainsi :

                    let merge_update old new_ = { new_ with path = SS.union old.path new_.path }

                    tu n'auras plus besoin de générer une liste puis de la renverser, ou d'autres code dans le genre : la complexité en temps de tes algos sera grandement réduite.

                    Documentation du module Set et tutoriel succinct.

                    Tu vois : pas besoin de contourner le GC pour avoir du code efficace. ;-)

                    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                    • [^] # Re: Destructeurs

                      Posté par  (site web personnel) . Évalué à 3.

                      Il faut voir. Les listes sont ridicules dans 99% des cas. Il y a 1 ou 2 éléments. Pour des tailles inférieurs à 100, c'est très rares d'avoir un container qui bas une liste. Les surcouts statiques sont souvent très couteux (la constante derrière, le O(1))

                      "La première sécurité est la liberté"

                      • [^] # Re: Destructeurs

                        Posté par  . Évalué à 2.

                        Effectivement, si les listes sont de petites tailles ça ne vaut certainement pas le coup. Mais tes fichiers meta ne sont-ils pas un index de fichiers ? Une arborescence peut contenir plein de fichiers, ou alors tu as un index qui ne liste que les fichiers du répertoire et en général les répertoires ne contiennent pas assez de fichiers pour rendre intéressant le recours à la structure d'ensemble, c'est ça ?

                        Sinon pour faire du profilage d'utilisation mémoire à l'exécution tu as l'outil ocp-memprof développé par OCaml Pro qui permet d'analyser les fuites mémoires et de patcher le code coupable. :-) Pour développer ce genre d'outil il faut contourner le GC et même utiliser le module Obj, mais ce n'est pas le plus courant et il faut savoir ce que l'on fait.

                        Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                        • [^] # Re: Destructeurs

                          Posté par  (site web personnel) . Évalué à 2.

                          L'outil génère un "metafichier" par fichier dans le répertoire ./list/ ce fichier contient la taille et un path vers le fichier réel. Si un fichier meta n'est pas dans ./list/ le binaire va tenter de le replacer par son fichier d'origine et va ajouter son path dans le fichier meta de ./list/.

                          "La première sécurité est la liberté"

        • [^] # Re: Destructeurs

          Posté par  . Évalué à 3.

          Il faut toujours passer l'option -safe-string pour forcer la séparation entre les types string (immutable) et bytes (mutable), ce n'est pas encore l'option par défaut (pour ne pas casser, je suppose, le code existant qui utilise toujours le type string de façon mutable).

          Il y a même des fonctions de conversions non sûres entre les deux types ce qui empêche Flambda de faire de la propagation de constante sur le type string.

          Cela étant, je ne vois pas non plus ce qui empêche d'appliquer la méthode du C en OCaml pour le problème décrit par Nicolas. Il faudrait plus d'informations.

          Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

    • [^] # Re: Destructeurs

      Posté par  . Évalué à 5.

      Par contre, il a des destructeurs. J’espère donc qu’il supplantera Ruby.

      Quand tu as un gc, ce n'est pas génial AMHA (même si à un moment donné le gc fait ce que tu veux ça veut dire que tu dépend du comportement de ton gc, qui peut changer au cours du temps). À moins que le langage ne te donne la garantie que le destructeur va être appelé à un moment donné. Mais globalement, si tu veux reproduire du RAII avec des gc le mieux est d'avoir une structure de contrôle comme le with de python ou le try-with-resource de java (tu construit ton objet qui encapsule ta ressource au début du bloc et il appelle la méthode __exit__ ou close en sortant du bloc.

      En python je sais pas, mais en java tu peux vérifier que tous les objets closables sont bien construit dans de tels blocs.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Destructeurs

      Posté par  (site web personnel) . Évalué à 2. Dernière modification le 09 mai 2016 à 13:31.

      Cela dit, la mention d’un garbage collector sans précision sur son fonctionnement est un peu inquiétante, pas seulement pour le fonctionnement des destructeurs. Enfin peut-être celui-ci (voire celui de Ruby) fonctionne-t-il mieux que ce que j’ai eu l’occasion de voir avec d’autres langages (pas de libération de mémoire quand on en manque, lancement éventuel du garbage collector… au moment où l’en aurait besoin de performances).

      Sans parler que c'est aussi s'imposer une limite concernant les performances…. Ce qui peut être un problème pour un langage qui se veut "avec des performances proches du C".

      Un langage avec GC fait le choix de troquer de l'usabilité contre le déterminisme et la gestion fine de la mémoire. Et ça a un impact en terme de performance qui d'ailleurs s'illustre assez bien sur le Computer Language Benchmarks Game.

      Dans le meilleur des cas, Crystal pourra rivaliser avec Go ou Java… mais pas avec C, C++ ou Rust.

      • [^] # Re: Destructeurs

        Posté par  (site web personnel) . Évalué à 5.

        Ça se discute : Java a des performances proches du C. Ocaml est également connu pour ses performances proches du C et utilise aussi un GC.

        • [^] # Re: Destructeurs

          Posté par  . Évalué à 5.

          ah, j'adore ces trolls du lundi ! Oui et il n'y a aucun probleme de mémoire avec le java.

          • [^] # Re: Destructeurs

            Posté par  . Évalué à 8.

            Le gc pose des problèmes dans des cas précis.
            D'un point de vu performance de calcul du code généré par HotSpot ou gcc la différence n'est pas aussi grande que ce que tu semble croire.

            Les points (parmi d'autres) qu'il faut regarder quand tu code en java pour la performance c'est :

            • le démarrage de la JVM (si ton programme fait des arrêts/démarrages fréquents t'es morts, il faut avoir un mode deamon/client)
            • il faut réfléchir à ta manière d'utiliser ta mémoire (sur le traitement des chaînes de caractère par exemple)

            Pour les cas les plus complexes, tu peux créer des objets hors de la pile et donc non gérés par le garbage collector.

            La performance en Java n'est pas un concept et le monde du big data te donnera pleins d'exemples.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: Destructeurs

              Posté par  . Évalué à -5.

              Le gc pose des problèmes dans des cas précis.

              Exemple de cas précis: quand on s'en sert.

              La performance en Java n'est pas un concept et le monde du big data te donnera pleins d'exemples.

              Tout à fait. Mais elle reste différente de celle de c/c++.

              • [^] # Re: Destructeurs

                Posté par  . Évalué à 6.

                Mais elle reste différente de celle de c/c++.

                Il y a l'overhead de la JVM (le modèle mémoire et le garbage), le code en lui même après la chauffe de la JVM est très optimisé

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                • [^] # Re: Destructeurs

                  Posté par  . Évalué à -1.

                  le code en lui même après la chauffe de la JVM est très optimisé

                  Ah, le hotspot supprime l'overhead du GC peut-être ?

                  Le hotspot qui permet de faire quasiment le même calcul en une microseconde que du code natif C++ c'est vrai.
                  Sauf quand au milieu de la microseconde tu prends plusieurs millisecondes de pause GC.
                  Oui, au moins un facteur mille.

                  Bref ça dépend ce que tu appelles performance.
                  Java est très bien, sauf quand t'as besoin de mieux.

        • [^] # Re: Destructeurs

          Posté par  (site web personnel) . Évalué à 3. Dernière modification le 09 mai 2016 à 12:44.

          Ça se discute : Java a des performances proches du C. Ocaml est également connu pour ses performances proches du C et utilise aussi un GC.

          Dépend de ta définition de "proche".

          Si tu prends les benchs du shoutaut que j'ai mis en lien plus haut et que je considère comme une réference en terme d'optimisation. Java est généralement entre 10% et 20% plus lent que C/C++. OCaml est encore plus loin derrière.

          L'écart tend même à se creuser sur dés que l'on commence à travailler avec un grand nombre d'objets, à parler multi-threading ou NUMA. Il y a une plétoire de publications à ce sujet ( genre là, ou ).

          Si un bon nombre de DB NoSQL en Java comme Druid ont fait le choix de ré-implémenter leur propre mémory allocator, c'est pas juste pour décorer et histoire d'être unsafe.

          Allez, juste pour le plaisir du troll et parce que c'est Lundi. Une petite dédicace venant de la FAQ de cassandra:

          "In most cases, the capability of Java to gracefully handle garbage collection above 8GB quickly diminishes."

          .

          • [^] # Re: Destructeurs

            Posté par  (site web personnel) . Évalué à 10.

            Effectivement, ça dépend de la définition de "proche". Pour moi, 10 à 20 % plus lent que le C/C++, ça reste proche.

            Tous les langages dynamiques (JavaScript, Ruby, Python, PHP, etc.) sont 10 à 100 fois plus lent que le C. Go et Scala sont 2 à 3 fois plus lent que le C.

            Comme ça, je dirais qu'un langage a des performances proche du C si on parle de son écart de performances en pourcentage et non en nombre de fois plus lent.

          • [^] # Re: Destructeurs

            Posté par  (site web personnel) . Évalué à 6.

            Allez, juste pour le plaisir du troll et parce que c'est Lundi. Une petite dédicace venant de la FAQ de cassandra:

            "In most cases, the capability of Java to gracefully handle garbage collection above 8GB quickly diminishes."

            Puisque l'on parle de JVM et de gc, c'est curieux que ton lien ne parle pas également du "compressed Oops trick".
            C'est un petit peu hors-sujet par rapport à la dépêche mais comme je trouve cela intéressant, je partage quand même. Pour résumer, il est en général déconseillé d'allouer plus de 32G à la JVM sous peine de souffrir d'une baisse drastique de performance (régression avec une heap de 50G par rapport à une heap de 30G par exemple).
            Cela est dû au fait que la JVM utilise un trick: les pointeurs de référence ("Ordinary object pointers") sont de 32-bit jusqu'à cette "limite" de 32G. Comme ces pointeurs réfèrent des objets alignés à l'octet, ils référencent des offsets plutôt que des octets directement. Cela permet donc de référencer ~4mia d'objets plutôt que de byte. On arrive donc à référencer près de 32G au lieu de 4G avec des pointeurs de 32-bit. En revanche, passé cette limite, les pointeurs passent à une taille de 64-bit au lieu de 32-bit afin de pouvoir référencer l'entier de la heap allouée à la JVM. Comme la taille des pointeurs double, plus de ressources CPU/mémoire sont sollicitées d'où une régression de performance et dans les fait, on "perd" de la mémoire effective. Bien entendu, passé une certaine taille d'allocation de mémoire à la JVM, on les "récupère".

            • [^] # Re: Destructeurs

              Posté par  . Évalué à 7.

              Que ce soit pour l'un ou pour l'autre, j'ai pas encore rencontré de cas où il serait plus intéressant d'augmenter la taille d'une JVM plutôt que de les multiplier. C'est rare (de mon expérience) d'avoir besoin de partager autant de données entre tes threads. C'est quelque chose qu'on essaie d'éviter autant que possible quelque soit le langage.

              Pour le cas particulier de Cassandra, je ne vois pas de raison de dimensionner des JVM si grosses. L'architecture de Cassandra t'apporte vraiment quand tu multiplie le nombre de tes nœuds (meilleur résistance aux pannes, possibilité de répartir les requêtes sur tes nœuds,…). À moins d'avoir des lignes qui font plusieurs Gio (ce qui n'est pas possible avec Cassandra et qui n'est pas une bonne idée globalement), je ne vois pas de raison d'avoir des nœuds énormes.

              Pour moi il y un parallèle entre l'utilisation de très grosses machines et la multiplication des petites machines pour offrir un même service (scaler horizontalement plutôt que verticalement).

              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Destructeurs

            Posté par  . Évalué à 4.

            Tu parles d'OpenJDK, mais Java c'est aussi d'autres VMs (propriétaires hélas) comme Azul Zing qui gérerait soit disant des centaines de gigaoctets de mémoire.
            Bon j'ai pas les sous pour vérifier… :(

            Par ailleurs ils peuvent aussi éliminer les démarrages lents paraît-il grâce à des profils d'exécution. Invérifiable encore une fois.

      • [^] # Re: Destructeurs

        Posté par  . Évalué à 10.

        Moi je remarque surtout que des langages massivement plus lents que ça sont lourdement utilisés en production (Javascript, Ruby, PHP, Python, etc.), et que les programmes sans gestion automatique de la mémoire sont truffés de faille de sécurité béantes qui nous poussent être mettre à jour nos systèmes à la va-vite chaque semaine. (Oui, on peut faire du code plus sûr en C++ ou Rust (ou même en C macroté) en utilisant du refcounting, mais ça a aussi un coût en performance pas forcément négligeable.)

        Il paraît qu'il reste des domaines applicatifs où les temps de pause des GCs incrémentaux sont inacceptables (enfin bon Azul a montré qu'avec assez d'argent investi dans le problème on peut aller très loin), et tant pis pour eux. Mais sinon tirer en permanence la sonnette d'alarme sur les performances du GC c'est un peu dangereux à mon avis, quand ça pousse les gens à faire des choix techniques qui leurs coûtent au final beaucoup plus cher, en terme de débug ou de failles de sécurité.

        (On va dire que Rust avec ses lifetime c'est magnifique etc. Je pense qu'on n'a pas encore l'expérience pour voir si le code Rust est en pratique plutôt sûr, ou aussi un nid à failles comme C. Le langage fait des bons choix dans la direction de la sûreté, mais une communauté trop portée sur le "unsafe" pourrait faire pencher la balance de l'autre côté, et dans une certaine mesure c'est en train d'arriver. C'est lié aussi au fait que garder statiquement des garanties fortes sur l'ownership c'est en fait assez difficile en dehors des cas simples, quand il y a du partage plus riche, ce qui pousse les gens à faire soit du reference counting (mais les performances, etc.) soit du unsafe.)

        • [^] # Re: Destructeurs

          Posté par  (site web personnel) . Évalué à 6. Dernière modification le 09 mai 2016 à 21:36.

          Moi je remarque surtout que des langages massivement plus lents que ça sont lourdement utilisés en production (Javascript, Ruby, PHP, Python, etc.),

          Python n'utilise pas de garbage collector mais un compteur de référence, tout comme Swift et PHP. PHP a une fonction "GC", mais que de nom, c'est en réalité un simple détecteur de référence cyclique.

          Ceci dit, je suis entièrement d'accord avec toi. Les languages à GC sont populaires, ils tendent à rendre le memory model des applications grand publique plus simple et plus safe et, j'ai envie de dire, c'est tant mieux !

          Ce qui tend par contre à m'énerver, c'est cette légende populaire (largement véhiculé par le monde académique d'ailleurs) qui prétend que les garbages collectors ont un coût en terme de performance nulle, qu'ils sont une solution parfaite et sans effet de bords… et qu'ils vont tôt ou tard conquérir le monde et remplacer tous les langages à memory management fine grain, C inclu.

          Je ne compte même plus le nombre de petit boutonneux sortis de l'école qui m'ont débalé ce genre d’âneries.

          Non, tous les langages qui supporte un memory management sans GC ( C, C++, Rust, Ada, Swift, Python ) n'ont pas été inventé par des imbéciles. Il y a des raisons à un memory management manuel ou à un ref counter: un GC a un coût, en terme de performance, en terme de ressources ( 2x à 8x la quantité de mémoire ), en terme de déterminisme, en terme de latence.

          Si ce coût est acceptable pour des services de petite et moyenne taille, il l'est beaucoup moins quand on parle de jeux videos, de 3D, d'application système lourde, de calcul scientifique ou simplement de systèmes embarqués.

          Je pense qu'il serait bon d’arrêter l’hypocrisie et d'enseigner la programmation un poil plus pragmatiquement. Spécialement en ce qui concerne certains aspects comme le memory management ou le multi-theading.

          • [^] # Re: Destructeurs

            Posté par  (site web personnel) . Évalué à 6.

            Python n'utilise pas de garbage collector mais un compteur de référence, tout comme Swift et PHP.

            C'est un avis pour le moins surprenant. Le compteur de référence n'est qu'un élément. Python a bien des pauses pendant la libération des anciens objets qui ne sont plus utilisés. Peux-tu détailler pourquoi tu ne considères pas ça comme un Garbage Collector ?

            Si ce coût est acceptable pour des services de petite et moyenne taille, il l'est beaucoup moins quand on parle de jeux videos, de 3D, d'application système lourde, de calcul scientifique ou simplement de systèmes embarqués.

            A priori, Julia s'en sort bien pour le calcul scientifique. Et Erlang a plus que fait ses preuves pour les systèmes embarqués.

            • [^] # Re: Destructeurs

              Posté par  . Évalué à 5.

              C'est un avis pour le moins surprenant. Le compteur de référence n'est qu'un élément. Python a bien des pauses pendant la libération des anciens objets qui ne sont plus utilisés. Peux-tu détailler pourquoi tu ne considères pas ça comme un Garbage Collector ?

              Ce n'est pas un garbage collector genre mark/sweep ou compactant : il se contente de collecter les cycles, donc ce qui n'est pas libéré par le comptage de référence. Donc en gros il ne va pas faire d'analyse du graphe d'objets et des piles, contrairement à des GCs comme on l'entend classiquement (Java, Go…) (je parle de cPython uniquement).

              A priori, Julia s'en sort bien pour le calcul scientifique. Et Erlang a plus que fait ses preuves pour les systèmes embarqués.

              Ca dépend vraiment des contraintes : si par exemple tu vises des latences inférieures à la ms ça va être très dur avec un GC dès que la taille du tas devient conséquente, même si Azul par exemple a vraiment révolutionné le domaine.
              Mais après c'est vrai que beaucoup de gens tapent sur les GCs sans vraiment savoir de quoi ils parlent, et c'est frustrant en 2016 d'avoir des failles dues à buffer/stack overflows plusieurs fois par semaine…

              • [^] # Re: Destructeurs

                Posté par  (site web personnel) . Évalué à 9.

                Ca dépend vraiment des contraintes : si par exemple tu vises des latences inférieures à la ms ça va être très dur avec un GC dès que la taille du tas devient conséquente

                Il suffit de ne pas avoir un seul tas avec une taille conséquente mais de répartir les données. C'est ce qu'Erlang fait avec ses processus (qui sont différents des processus de l'OS) ou Go avec ses goroutines. Pour Erlang, ça permet de lancer le GC sur un processus Erlang sans bloquer les autres processus et sur un espace mémoire très petit, donc ça va vite.

                Erlang est utilisé pour des switchs et pour l'infrastructure des réseaux mobile (GPRS, 3G, LTE). Autant dire, Les latences inférieurs à la ms ne font lui pas peur.

                • [^] # Re: Destructeurs

                  Posté par  . Évalué à 2.

                  Sauf que tous les workloads ne sont pas partitionables, que l'utilisation de fibres/goroutines/coroutines/user-space tasks/quel que soit le nom qu'on leur donne implique des echanges de messages et donc un cout en copie, synchronization et context switch (meme en user-space).
                  Il y a une raison pour laquelle des boites comme Azul existent…

                  Un autre argument contre les garbage collectors donne par Linus est la perte de localite de reference: lorsque le GC decide de faire une collection, tu te retrouves avec un cache pollue par les objets collectes.

                  Donc non, un heap par task ne resoud pas tous les problemes.

                  • [^] # Re: Destructeurs

                    Posté par  (site web personnel) . Évalué à 4.

                    Je n'ai pas dit que ça doit résoudre tous les problèmes. Mais je pense qu'il n'y a pas d'incompatibilité en général entre l'utilisation d'un Garbage Collector et l'embarqué. Il faut juste un GC qui convient. Celui d'Erlang qui convient dans pas mal de cas. Azul est une autre solution.

                    Sauf que tous les workloads ne sont pas partitionables

                    Les workloads qui ne sont pas partitionnables, ça reste une cas très spécifique.

                    Un autre argument contre les garbage collectors donne par Linus est la perte de localite de reference: lorsque le GC decide de faire une collection, tu te retrouves avec un cache pollue par les objets collectes.

                    Je ne vois pas trop ce que cet argument vient faire ici. Oui, la fragmentation de la mémoire est un problème, pour les GC comme pour la gestion de la mémoire manuelle. En l'occurrence, l'utilisation de fibres/goroutines/processus Erlang aide justement pas mal.

                    D'une part, quand un processus Erlang finit, sa stack et sa heap sont entièrement supprimées. Et c'est de cette façon qu'une bonne partie de la mémoire est libérée. D'autre part, le GC est générationnel : il recopie les objets récents encore utilisés de l'espace réservé aux objets nouvellement créés vers un autre espace. Ainsi, il y a peu de "trous" et on évite une partie de la fragmentation de la mémoire.

                    • [^] # Re: Destructeurs

                      Posté par  . Évalué à 3.

                      Je ne vois pas trop ce que cet argument vient faire ici. Oui, la fragmentation de la mémoire est un problème, pour les GC comme pour la gestion de la mémoire manuelle.

                      Je n'ai jamais parle de fragmentation memoire - qui est d'ailleurs un avantage pour les GCs, qui sont de nos jours compactants - mais de pollution de caches processeurs par l'executiondu GC, qui va donc provoquer l'eviction de code/donnes qui sont actuellement utilisees pour toucher des objets inutilises un peu partout en memoire (qui est par ailleurs tres peu cache-friendly).

                      Mais je pense qu'il n'y a pas d'incompatibilité en général entre l'utilisation d'un Garbage Collector et l'embarqué.

                      Moi non plus, j'aime les GCs, je voulaisjuste souligner qu'il y a de bonnes raisons de s'en passer dans certains contextes.

              • [^] # Re: Destructeurs

                Posté par  . Évalué à 3.

                il se contente de collecter les cycles, donc ce qui n'est pas libéré par le comptage de référence. Donc en gros il ne va pas faire d'analyse du graphe d'objets et des piles, contrairement à des GCs comme on l'entend classiquement (Java, Go…) (je parle de cPython uniquement).

                Ben… il parcourt quand même le graphe d'objets pour détecter les cycles morts (la traversée du graphe se faisant via tp_traverse).

                Les deux gros avantages sont :

                • comme cela ne sert que pour les cycles morts, on peut faire passer le GC beaucoup moins fréquemment puisque la majorité des références sont normalement relâchées par comptage de référence. Ceci dit, quand la génération la plus âgée est parcourue, ça peut prendre du temps.

                • certains objets, qui ne peuvent par construction appartenir à un cycle de référence, sont dispensés de ce parcours (par exemple les entiers, chaînes de caractères…)

                • [^] # Re: Destructeurs

                  Posté par  . Évalué à 2.

                  Ben… il parcourt quand même le graphe d'objets pour détecter les cycles morts (la traversée du graphe se faisant via tp_traverse).

                  En effet, j'ai oublié le graphe "complet", merci de la précision.

                  Ceci dit, quand la génération la plus âgée est parcourue, ça peut prendre du temps.

                  Oui, dans mon souvenir le parcours n'est pas incrémental : il y a déjà eu des rapports de bugs concernant les pauses ?

                  • [^] # Re: Destructeurs

                    Posté par  . Évalué à 2.

                    Oui, dans mon souvenir le parcours n'est pas incrémental : il y a déjà eu des rapports de bugs concernant les pauses ?

                    Non, mais on a déjà vu par le passé des gens disant qu'ils désactivaient le GC pour raison de performances. Je crois que CCP (éditeur de Eve Online) le fait.

          • [^] # Re: Destructeurs

            Posté par  (site web personnel) . Évalué à 3.

            Ce qui tend par contre à m'énerver, c'est cette légende populaire (largement véhiculé par le monde académique d'ailleurs) qui prétend que les garbages collectors ont un coût en terme de performance nulle, qu'ils sont une solution parfaite et sans effet de bords… et qu'ils vont tôt ou tard conquérir le monde et remplacer tous les langages à memory management fine grain, C inclu.

            J'aimerais bien savoir quel "monde académique" tu fréquentes.

            • [^] # Re: Destructeurs

              Posté par  (site web personnel) . Évalué à -3.

              J'aimerais bien savoir quel "monde académique" tu fréquentes.

              Le monde académique bien Français, bien universitaire

          • [^] # Re: Destructeurs

            Posté par  (site web personnel) . Évalué à 10.

            Python n'utilise pas de garbage collector mais un compteur de référence

            D'une part un compteur de référence est une forme de garbage collector (*), d'autre part Python a un « vrai » GC pour gérer les cycles, qui peut être contrôlé avec le module gc :

            import gc
            # Empêcher le GC de se déclencher automatiquement :
            gc.disable()
            # Déclencher manuellement un cycle de collecte :
            gc.collect()
            # Ré-activer le GC :
            gc.enable()

            (*) Un garbage collector est un mécanisme qui libère automatiquement la mémoire quand elle n'est plus utilisée, selon cette définition un compteur de références est un garbage collector.

            un GC a un coût, en terme de performance,

            Inexact. En fonction de la façon dont ton application alloue et désalloue la mémoire, un GC peut s'avérer plus performant qu'une gestion manuelle. En particulier si tu as des structures profondes (arbres ou listes) que tu modifies peu au cours de leur vie (i.e tu les alloues, tu les utilises sans les modifier, puis tu les désalloues). Dans ce cas, un bon GC pourra tout désallouer d'un coup alors qu'une gestion manuelle t'obligera à désallouer chaque nœud de ta structure.

            en terme de ressources ( 2x à 8x la quantité de mémoire ),

            Vrai, du moins tant que ton appli n'a pas de fuites mémoire. C'est pour cette raison que les langages à GC ne perceront jamais dans l'embarqué (je parle ici du vrai embarqué, celui où les ressources sont limitées, pas des ordiphones qui sont plus puissants que le PC que j'avais il y a cinq ans).

            en terme de déterminisme, en terme de latence.

            Faux. Il existe des GC temps réel (donc qui garantissent une latence max). Bien sur, on n'a rien sans rien et ces GC sont généralement encore plus gourmands en mémoire que les autres. Note que de toutes façons ceux qui développent des systèmes vraiment critiques en termes de performances (jeux, par ex.) évitent les allocations dans la partie critique (aussi bien avec un GC, qu'avec malloc) car ni l'un ni l'autre n'ont de performances garanties.

            Les GC ne sont pas la panacée mais ils ont leur utilité, en particulier :

            • lorsque réduire le temps de développement est plus important que d'économiser quelques cycles CPU ou quelques MB de RAM (un développeur coûte beaucoup plus cher que d'acheter une machine plus puissante ou de répartir la charge entre plusieurs machines),

            • lorsque de toutes façons le système est tellement critique qu'aucune allocation n'est faite dans la boucle principale. Dans ce cas rien n'empêche d'utiliser un GC pour les allocations qui, par définition, auront lieu en dehors de la partie critique.

            • [^] # Re: Destructeurs

              Posté par  (Mastodon) . Évalué à 7.

              Inexact. En fonction de la façon dont ton application alloue et désalloue la mémoire, un GC peut s'avérer plus performant qu'une gestion manuelle. En particulier si tu as des structures profondes (arbres ou listes) que tu modifies peu au cours de leur vie (i.e tu les alloues, tu les utilises sans les modifier, puis tu les désalloues). Dans ce cas, un bon GC pourra tout désallouer d'un coup alors qu'une gestion manuelle t'obligera à désallouer chaque nœud de ta structure.

              C'est le genre d'argumentation qui a le don de m'énerver. Un bon GC est meilleure qu'un mauvais programmeur : ho la bonne surprise. Je vais te la faire à l'envers : un mauvais GC est pire qu'un bon programmeur ! Et on a bien fait avancer le bouzin.

              Dans le cas très particulier que tu cites «structures profondes (arbres ou listes) que tu modifies peu au cours de leur vie (i.e tu les alloues, tu les utilises sans les modifier, puis tu les désalloues)», déjà ce n'est pas la désallocation qui prendra énormément de temps si ce sont des structures à durée de vie longue, donc chipoter sur le temps de désallocation, je ne comprends pas bien.

              Ensuite, rien n'empêche d'utiliser un allocateur spécifique pour ce cas là, genre j'alloue un gros tas de mémoire et je déplace un pointeur pour chaque nœud et quand je veux tout désallouer d'un coup, ben je fais free sur mon gros tas de mémoire. Un programmeur correct sait faire ça assez vite et ça va aussi bien que ton super-GC (voire mieux parce que tous les nœuds seront alloués dans une zone contiguë). Ce genre d'allocateur existe dans beaucoup de bibliothèques.

              • [^] # Re: Destructeurs

                Posté par  . Évalué à 6.

                Ensuite, rien n'empêche d'utiliser un allocateur spécifique pour ce cas là, genre j'alloue un gros tas de mémoire et je déplace un pointeur pour chaque nœud et quand je veux tout désallouer d'un coup, ben je fais free sur mon gros tas de mémoire.

                Pour faire ce genre de choses il te faut une vision assez précise des cas d'utilisation. Pour cet exemple, il faut savoir quelle va être la quantité de mémoire utilisée (ou faire une estimation). Si tu te plante il faut soit déplacer toute tes données soit avoir une logique pour gérer toute de même ta structure de manière éparse. Dans le cas général (oui on va parler de temps réel dur, d'embarqué, etc), le préfère définitivement laisser un gc s'occuper de ça et si je rencontre un problème le paramétrer ou passer le bout de code qui pose problème « Off the heap », plutôt que de faire l'inverse.

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                • [^] # Re: Destructeurs

                  Posté par  (site web personnel) . Évalué à 2.

                  Dans le cas général (oui on va parler de temps réel dur, d'embarqué, etc), le préfère définitivement laisser un gc s'occuper de ça et si je rencontre un problème le paramétrer ou passer le bout de code qui pose problème « Off the heap », plutôt que de faire l'inverse.

                  Dans le cas général, tu laisses la RAII et malloc() gérer ça. Dans le cas où tu utilises un home-made Allocator pour faire des (millions) d'allocations en chaine de petit objets, c'est déja un cas qui tuerait 90% des GC.

                  Pour faire ce genre de choses il te faut une vision assez précise des cas d'utilisation. Pour cet exemple, il faut savoir quelle va être la quantité de mémoire utilisée (ou faire une estimation)

                  Généralement en C/C++, tout bon allocator est une chaine d'allocator. Tu fais une estimation pour une première pool X, si elle se remplit tu alloues une deuxième pool Y, si elle se remplit, tu te rabas sur malloc() ou similaire.

                  Il y a un trés bon talk à la dernière CPPcon sur le sujet si certaines personnes sont interessées.

                  • [^] # Re: Destructeurs

                    Posté par  . Évalué à 2.

                    La différence devient assez floue par contre entre un gc bien paramétré et la RAII/définition de pool de mémoire, non ?

                    Dans les deux cas il faut un minimum réfléchir à la bonne manière de gérer la mémoire pour son cas d'utilisation et pas mal de choses sont automatisés. On peut se pignoler sur la nature des automatismes mais ça a l'air de converger. À tel point qu'on se demande si un gc pourrait pas imaginer adopter des stratégies de RAII intelligemment dans certains cas.

                    • [^] # Re: Destructeurs

                      Posté par  (site web personnel) . Évalué à 2.

                      C'est ce dont je parlais dans un autre commentaire. Si tu prends la définition classique d'un "modèle", tu as un diagramme de classe, avec des "références" et des "liens de contenance" qui gère aussi le "lifetime". Dans un modèle objet classique, tous les objets sont créé dans les racines et se référencent entre eux. Dans un modèle, il y a une hiérarchie et une contenance d'objet explicite. Il y a de l'information pour beaucoup simplifier un GC.

                      Tu peux donc imaginer un système qui alloue toutes la mémoire très tôt, et qui n'alloue plus rien ensuite, en fonction de la taille des entrées.

                      "La première sécurité est la liberté"

              • [^] # Re: Destructeurs

                Posté par  (site web personnel) . Évalué à 1.

                ce n'est pas la désallocation qui prendra énormément de temps si ce sont des structures à durée de vie longue, donc chipoter sur le temps de désallocation, je ne comprends pas bien.

                Je compare ce qui est comparable, donc le temps passé dans la désallocation par rapport au temps passé dans le GC. Le reste du temps de traitement sera le même que tu utilises où non un GC.

              • [^] # Re: Destructeurs

                Posté par  . Évalué à 10.

                Un bon GC est meilleure qu'un mauvais programmeur : ho la bonne surprise. Je vais te la faire à l'envers : un mauvais GC est pire qu'un bon programmeur ! Et on a bien fait avancer le bouzin.

                Ce qui fait avancer le bouzin, c'est qu'il y a peu d'implémentations de GC par rapport au nombre de programmeurs, que les GC sont en général écrits par des gens pointus, et que vu le nombre d'utilisateurs et les ressources de développement qu'ils ont, ils sont souvent correctement débuggés et optimisés.

                Bref, c'est l'avantage classique de mutualiser et déléguer le développement d'infrastructures critiques (gestion mémoire, routines d'E/S, ordonnancement de tâches, etc.) à des équipes hautement spécialisées dont le travail sera réutilisé par un grand nombre de gens. Quelle drôle d'idée !

                Ensuite, rien n'empêche d'utiliser un allocateur spécifique pour ce cas là

                Certes, rien n'empêche de tout réécrire à la main et de passer des jours à fignoler et débugger ce qui prendrait dix minutes dans un langage haut niveau. J'espère que tu appliques la même discipline en ce qui concerne ta libc, ta libm, tes primitives de synchronisation, ton noyau de système d'exploitation, etc.

                (je ne parle pas du jour où il faudra que tes allocateurs spécifiques cohabitent harmonieusement avec des infrastructures tierces, par exemple un détecteur de fuites genre Valgrind—tiens, ça me rappelle une histoire avec OpenSSL)

                Un programmeur correct sait faire ça assez vite

                Mouarf. Soit les bibliothèques et applications C qui ont régulièrement des trous de sécu et des fuites mémoire sont écrites par des clampins (c'est quand même ballot, ça : ils auraient dû demander l'aide des gens comme toi), soit tu surestimes largement la protection contre les défauts qu'apportent des compétences en programmation bas niveau.

                J'ai ma petite idée sur laquelle des deux explications est la bonne.

                • [^] # Re: Destructeurs

                  Posté par  (site web personnel) . Évalué à 1.

                  Mouarf. Soit les bibliothèques et applications C qui ont régulièrement des trous de sécu et des fuites mémoire sont écrites par des clampins (c'est quand même ballot, ça : ils auraient dû demander l'aide des gens comme toi), soit tu surestimes largement la protection contre les défauts qu'apportent des compétences en programmation bas niveau.

                  La "safety" d'un langage et sa gestion mémoire sont deux chose différentes.

                  Tu as des programmes vulnérabilité dans Java et PHP tout aussi souvent que dans des bloats en C, pourtant ces langages sont à mémoire managé.

                  Rust est plus résistant aux buffer overflow que la plupart des langages des script, pourtant sa gestion mémoire est manuel. La différence c'est qu'il a été désigné avec la sécurité en tête comme priorité. Il en va de même pour Swift d'Apple.

                  Ce que tu constates, c'est que le C est unsafe. Et Oui il l'est.
                  Il l'est par design dù avec son arithmétique des pointeurs, pas à cause de sa gestion des allocations / desallocations ou parceque tu peux faire des memory pool.

                  Tu peux d'ailleurs faire un buffer overflow ou créer une faille de sécurité en C sans même faire une seule allocation dynamique.

                  l'arithmétique des pointeurs en C te permet de te promener partout en mémoire, pratiquement sans restriction… forcement ça a un cout en terme de sécurité, même si c'est extrêmement puissant

                  • [^] # Re: Destructeurs

                    Posté par  . Évalué à 6.

                    La "safety" d'un langage et sa gestion mémoire sont deux chose différentes.

                    Je ne parle pas de la sûreté d'un langage, mais de celle des applications écrites d'une certaine manière (avec un GC, ou sans, par exemple). Et au-delà de la sécurité, c'est un problème plus général de qualité logicielle qui se pose.

                    Rust est plus résistant aux buffer overflow que la plupart des langages des script

                    Rust est tellement jeune (et, pour l'instant, peu utilisé) que l'on n'a pas de recul sur les problèmes des applications écrites en Rust.

                    Tu peux d'ailleurs faire un buffer overflow ou créer une faille de sécurité en C sans même faire une seule allocation dynamique.

                    Les problèmes du C ne se limitent certes pas à la gestion manuelle de la mémoire. Mais la gestion manuelle de la mémoire fait cependant partie de ses problèmes, et la complexité de la tâche (sur des applications non-triviales) fait que c'est un facteur important de défauts.

                    • [^] # Re: Destructeurs

                      Posté par  (site web personnel) . Évalué à 3. Dernière modification le 11 mai 2016 à 10:44.

                      Et au-delà de la sécurité, c'est un problème plus général de qualité logicielle qui se pose.

                      La qualité logiciel n'a rien à voir avec le langage ni le memory management. Un mauvais programmeur fera un mauvais programme, quel que soit le langage.

                      Rust est tellement jeune (et, pour l'instant, peu utilisé) que l'on n'a pas de recul sur les problèmes des applications écrites en Rust.

                      La gestion mémoire de Rust n'a rien de jeune. Elle vient des techniques de RAII de C++, Ada our Vala qui ont plus de 10 ans.
                      La seule "nouveauté" de Rust, est de les rendre obligatoire par défaut.

                      Les problèmes du C ne se limitent certes pas à la gestion manuelle de la mémoire. Mais la gestion manuelle de la mémoire fait cependant partie de ses problèmes, et la complexité de la tâche (sur des applications non-triviales) fait que c'est un facteur important de défauts.

                      Encore une fois, c'est un problème associé au design du langage, et pas à l'absence de GC. Le C pure rend trés difficile, voir impossible la mise en place de technique de RAII car il ne gère pas la notion de "scope", contrairement à C++, Rust, Ada, Objective-C, Swift, etc….

                      • [^] # Re: Destructeurs

                        Posté par  . Évalué à 8.

                        Encore une fois, c'est un problème associé au design du langage, et pas à l'absence de GC. Le C pure rend trés difficile, voir impossible la mise en place de technique de RAII car il ne gère pas la notion de "scope", contrairement à C++, Rust, Ada, Objective-C, Swift, etc….

                        Un langage est un tout cohérent sortir des coupables plutôt qu'un autre comme ça n'est pas forcément si clair. La gestion des chaînes de caractères du C, c'est lié à sa gestion de la mémoire, tout comme le fait de faire de l'arithmétique de pointeur,…

                        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                      • [^] # Re: Destructeurs

                        Posté par  (site web personnel) . Évalué à 5.

                        La gestion mémoire de Rust n'a rien de jeune. Elle vient des techniques de RAII de C++, Ada our Vala qui ont plus de 10 ans.
                        La seule "nouveauté" de Rust, est de les rendre obligatoire par défaut.

                        Rust a repris plein de concepts existants/éprouvés dans d'autres langages (C++, Haskell, Erlang, Python…), mais pour le coup sa gestion des références est réellement unique, et va plus loin que du simple RAII habituel (c'est sûrement inspiré du RAII, mais c'est bien plus que du RAII obligatoire). Le borrow checker n'a pas d'équivalent (à ma connaissance) ailleurs ; de plus, intégrer les autres concepts avec cette gestion mémoire, pour obtenir un tout cohérent, est aussi un tour de force en soi.

                        • [^] # Re: Destructeurs

                          Posté par  (site web personnel) . Évalué à 2. Dernière modification le 12 mai 2016 à 22:54.

                          Rust a repris plein de concepts existants/éprouvés dans d'autres langages (C++, Haskell, Erlang, Python…), mais pour le coup sa gestion des références est réellement unique, et va plus loin que du simple RAII habituel (c'est sûrement inspiré du RAII, mais c'est bien plus que du RAII obligatoire)

                          Enfin une vrai remarque construite ! merci sincèrement.

                          C'est vrai que le borrow checker fait de Rust un langage assez unique en soit.

                          Il utilise l'analyse statique au moment de la compilation pour détecter des problèmes qui passerait inaperçu dans une RAII classique "à la C++".

                          C'est une innovation propre à Rust, c'est vrai et c'est une innovation importante en terme de sûreté.

                          Le borrow checker n'a pas d'équivalent (à ma connaissance) ailleurs

                          Il n'en a pas directement c'est vrai.

                          Dans un cas similaire, Il y a un talk d'Herb Shutter à la dernière CppCon qui introduit un système similaire en C++ associé au unique_ptr de C++ (probablement inspiré de Rust au passage )

                          Leur système fait de l'analyse static sur du code C++ et détecte à la compilation les utilisations invalide de pointeur en traçant les utilisations invalide potentiels de unique_ptr aprés un "move".

                          Mais ça reste un plugin d'analyse statique là, et ce n'est probablement pas fiable à 100% où Rust l'a mis directement dans le design du langage.

                          • [^] # Re: Destructeurs

                            Posté par  (site web personnel) . Évalué à 2.

                            probablement inspiré de Rust au passage

                            Oui sûrement. Les 2 communautés s'inspirent mutuellement et c'est très bien comme ça, et très intéressant à suivre.

                        • [^] # Re: Destructeurs

                          Posté par  . Évalué à 8.

                          J'étais en train hier de regarder un article de Phil Wadler, "Linear types can change the world!", qui parle de borrowing et a été écrit en… 1990. Et ce n'était pas le premier article à discuter de ça. L'idée a été implémentée dans Cyclone par exemple ("temporary aliasing of unique pointers"), développé entre 2002 et 2006.

                          C'est bien que Rust rende ces idées "mainstream" maintenant mais c'est toujours un peu triste de voir à quel point les communautés de programmation oublient l'histoire des idées dont elles se nourrissent.

                  • [^] # Re: Destructeurs

                    Posté par  (site web personnel) . Évalué à 4.

                    Rust est plus résistant aux buffer overflow que la plupart des langages des script, pourtant sa gestion mémoire est manuel

                    Je ne suis pas d'accord sur le terme 'manuelle' ; pour moi la gestion mémoire de Rust est automatique, mais faite (en grande partie) à la compilation, et pas à l'exécution (comme avec les langages à GC). Mais comme c'est un peu un OVNI, il n'est pas étonnant que l'abus de langage 'automatique==à l'exécution' se soit installé.

                    • [^] # Re: Destructeurs

                      Posté par  (site web personnel) . Évalué à 1. Dernière modification le 10 mai 2016 à 23:26.

                      Je ne suis pas d'accord sur le terme 'manuelle' ; pour moi la gestion mémoire de Rust est automatique, mais faite (en grande partie) à la compilation, et pas à l'exécution (comme avec les langages à GC)

                      Yep, tout comme les techniques de RAII à la C++11 ( en particulier unique_ptr ) qui définit à la compilation le/les scopes et l'ownership de l'objet.

                      Il est difficile de qualifié ce genre de memory management, ce n'est pas vraiment "manuel" car il y a une forme d'automatisme, mais ce n'est pas non plus complètement automatique, car contrairement un GC traditionnel, le développeur a le contrôle de la durée de vie de l'objet, de sa destruction, voir de sa stratégie d'allocation.

                      C'est justement là tout l’intérêt du model: s'offrir 90% de la facilité d'un système de mémoire "complètement automatisé" tout en gardant la possibilité d'avoir une gestion "fine" quand le besoin s'en fait sentir.

                      Mes excuses si ce que j'ai dit était confus précédemment. Pour moi "manuel" signifie tout ce qui n'est pas sur base de GC et qui donne un control manuel, y compris la RAII.

                • [^] # Re: Destructeurs

                  Posté par  (Mastodon) . Évalué à 6.

                  Ce qui fait avancer le bouzin, c'est qu'il y a peu d'implémentations de GC par rapport au nombre de programmeurs

                  Et il y a peu d'implémentation de malloc par rapport au nombre de programmeurs.

                  Certes, rien n'empêche de tout réécrire à la main et de passer des jours à fignoler et débugger ce qui prendrait dix minutes dans un langage haut niveau.

                  Bel homme de paille. Je n'ai pas dit de tout réécrire, j'ai même dit explicitement d'utiliser une bibliothèque qui le fait. Donc, ça prendra à peu près 10 minutes aussi à écrire.

                  tu surestimes largement la protection contre les défauts qu'apportent des compétences en programmation bas niveau.

                  Ce que tu appelles «défaut», ou plus bas «problème», c'est-à-dire la gestion manuelle de la mémoire, d'autre appelle ça un avantage et une force. C'est une question de point de vue, et tu as une manière très condescendante d'expliquer qu'on est tous des gros débiles à vouloir gérer manuellement la mémoire et à vouloir la spécialiser quand c'est nécessaire. C'est ce que font plein de gens tous les jours dans tous un tas de domaines où un GC est hors de question.

                  • [^] # Re: Destructeurs

                    Posté par  . Évalué à 8.

                    Ce que tu appelles «défaut», ou plus bas «problème», c'est-à-dire la gestion manuelle de la mémoire,

                    Non, ce que j'appelle défaut, c'est un bug, une fuite mémoire, un crash, un trou de sécu, etc.

                    et tu as une manière très condescendante d'expliquer qu'on est tous des gros débiles à vouloir gérer manuellement la mémoire

                    Ma condescendance répond au complexe de supériorité des gens qui pensent que les défauts de gestion mémoire sont forcément dus à l'intervention d'un "mauvais programmeur"…

                    Postuler des turpitudes individuelles comme cause de tous les problèmes empêche de prendre conscience des effets de système. La difficulté de la gestion manuelle de la mémoire dans une application complexe, par exemple, est un effet de système inévitable (car les logiciels ne reviendront pas à la complexité des années 60, au contraire). L'historique complexe de l'évolution des projets sur de longues périodes est un autre effet de système (ce qui conduit par exemple à une discordance des conventions internes et d'éventuels malentendus sur icelles).

                    Évidemment, tout reporter sur les "mauvais programmeurs" est d'autant plus facile que la notion est mal définie et chacun peut décider selon ses propres critères qui est un "mauvais programmeur" (ce qui rappelle un célèbre sketch des Inconnus). Et la désignation d'un bouc-émissaire produit un effet de catharsis assez plaisant chez beaucoup d'êtres humains.

                    C'est ce que font plein de gens tous les jours dans tous un tas de domaines où un GC est hors de question.

                    La proportion d'applications où un GC est hors de question est appelée à diminuer. Comme le rappelle un intervenant, un téléphone "intelligent" aujourd'hui est suffisamment puissant pour faire tourner une JVM entière.

                    • [^] # Re: Destructeurs

                      Posté par  (site web personnel, Mastodon) . Évalué à 6.

                      La difficulté de la gestion manuelle de la mémoire dans une application complexe, par exemple, est un effet de système inévitable

                      Certes mais il y a encore beaucoup de projets très complexes qui utilisent une gestion manuelle de la mémoire sans que celle-ci soit forcément complexe en elle-même. Rien n'est inévitable… Après, je ne sais pas ce que tu appelles complexe.

                      un téléphone "intelligent" aujourd'hui est suffisamment puissant pour faire tourner une JVM entière

                      La fameuse JVM entière est, quant à elle, codée dans un langage n'utilisant pas de GC…
                      Comme quoi les deux approches ont leur intérêt.

                    • [^] # Re: Destructeurs

                      Posté par  (Mastodon) . Évalué à 3.

                      Non, ce que j'appelle défaut, c'est un bug, une fuite mémoire, un crash, un trou de sécu, etc.

                      Tu fais une analogie clairement abusive entre gestion manuelle et bug.

                      Ma condescendance répond au complexe de supériorité des gens qui pensent que les défauts de gestion mémoire sont forcément dus à l'intervention d'un "mauvais programmeur"…

                      Tu pars dans un délire paranoïaque. Personne n'a dit que la gestion manuelle de mémoire, c'était le paradis et la facilité et que tous les bugs étaient dû à des mauvais programmeur. Le but de ce fil, c'est juste de dire qu'un GC a un coût en terme de performance, de latence et que ce coût est inacceptable dans beaucoup de domaines.

                      Postuler des turpitudes individuelles comme cause de tous les problèmes empêche de prendre conscience des effets de système.

                      D'un autre côté, accuser l'effet de système permet d'exonérer certains programmeurs des mauvaises pratiques connues depuis des décennies qui mènent à des catastrophes. Parce que la solution au «problème», ce n'est pas de dire que tout le monde passe à un langage à GC, c'est d'appliquer les bonnes pratiques, de trouver des pattern d'allocation qui rendent cette tâche simple et sûre. Croire qu'un GC est la solution ultime à tous les problèmes de gestion mémoire, c'est se tromper lourdement.

                      La proportion d'applications où un GC est hors de question est appelée à diminuer. Comme le rappelle un intervenant, un téléphone "intelligent" aujourd'hui est suffisamment puissant pour faire tourner une JVM entière.

                      Les domaines pour lesquels un GC est inenvisageable à l'heure actuelle ne risque pas de diminuer parce que, pour la plupart, ce sont des domaines où les performances et/ou la latence sont primordiales et donc, on en revient à ce qu'on disait au début.

                      • [^] # Re: Destructeurs

                        Posté par  (site web personnel) . Évalué à 6.

                        "Les domaines pour lesquels un GC est inenvisageable à l'heure actuelle ne risque pas de diminuer parce que, pour la plupart, ce sont des domaines où les performances et/ou la latence sont primordiales et donc, on en revient à ce qu'on disait au début."

                        Typiquement tout ce qui est "safety critical" ne supporte pas l'allocation mémoire, alors un GC… (le SC, c'est pour les avions, les trains, l'industriel, le ferroviaire, la voiture…)

                        Et ce genre de code a tendance à vouloir enfler à très grande vitesse. Il y a une énorme différence entre la taille de code embarqué d'il y a 10 ans, et le code de maintenant.

                        "La première sécurité est la liberté"

                        • [^] # Re: Destructeurs

                          Posté par  (site web personnel) . Évalué à 0.

                          Typiquement tout ce qui est "safety critical" ne supporte pas l'allocation mémoire, alors un GC … (le SC, c'est pour les avions, les trains, l'industriel, le ferroviaire, la voiture…)

                          [troll day]

                          Je vois pas pourquoi.

                          Par exemple, quand une des fusées de SpaceX se vautre lamentablement sur sa barge en tentant d’atterrir, tu pourrais simplement dire "Le GC faisait une pause, promis ça arrivera plus au prochain lancement".

                          [/toll day]

                          • [^] # Re: Destructeurs

                            Posté par  . Évalué à 4.

                            C'est vrai que grâce à un langage sans GC Ariane 4 n'a jamais eu ce genre de problème, n'est-ce pas ?

                            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                            • [^] # Re: Destructeurs

                              Posté par  (site web personnel, Mastodon) . Évalué à 3.

                              C'est vrai que grâce à un langage sans GC Ariane 4 n'a jamais eu ce genre de problème, n'est-ce pas ?

                              En fait, c'est Ariane 5 (cf. Vol_501_d'Ariane_5). Et en plus, ça n'a rien à voir avec une histoire de fuite mémoire mais le problème vient d'un dépassement de capacité sur des variables non protégées.

                              • [^] # Re: Destructeurs

                                Posté par  . Évalué à 2.

                                Je me suis trompé d'Ariane et oui je sais que ça n'a rien avoir, c'est juste répondre à du troll par du troll _^
                                (tu as un détecteur à quand on parle d'ADA ? :)

                                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                                • [^] # Re: Destructeurs

                                  Posté par  (site web personnel, Mastodon) . Évalué à 2.

                                  Je me doutais que ça finirait par en parler. Et puis j'aime bien les news sur les nouveaux langages.
                                  Dès qu'on parle de bug, on parle de l'emblématique bug d'Ariane 5 :)

                                  Bon, je vais pas faire tout un laïus sur le fait que la norme d'Ada autorise l'utilisation d'un GC mais que je n'en ai pas encore vu :)
                                  Globalement, on n'en a pas vraiment besoin vu comment tout ce qui est lié aux pointeurs et à l'allocation dynamique est contraint.
                                  D'ailleurs, j'ai vu parler de pool de mémoire et c'est effectivement un mécanisme qui existe dans le standard.

                                  • [^] # Re: Destructeurs

                                    Posté par  (site web personnel) . Évalué à 1.

                                    Ariane 5, c'est surtout un handler d'exception sur dépassement de capacité d'une variable qui ne servait à rien, qui lance un code d'autotest, le problème. J'imagine que c'est pour cela qu'il y a une chasse au code "mort" depuis.

                                    "La première sécurité est la liberté"

                                    • [^] # Re: Destructeurs

                                      Posté par  (site web personnel, Mastodon) . Évalué à 5. Dernière modification le 12 mai 2016 à 13:34.

                                      Non, au tout départ, c'est bien un problème d'exception matérielle dû à un dépassement de capacité (voir l'explication).
                                      Mais au final, c'est un problème de design puisque la centrale inertielle n'était pas supposée fonctionner après le décollage et encore moins sur Ariane 5 :)
                                      Du coup, on est d'accord, le code mort, c'est mal :D

                          • [^] # Re: Destructeurs

                            Posté par  . Évalué à 2.

                            Le troll sur le SC a déjà eu lieu, en voici le bilan (l'exemple devrait te plaire, le langage cible c'est ADA mais le code est généré automatiquement ;-)

                            Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                            • [^] # Re: Destructeurs

                              Posté par  (site web personnel) . Évalué à 3.

                              Dans le domaine, dans l'embarqué, c'est quand même du C ou du C++, et la bite et le couteau avec des tests unitaire à taux de couverture MCDC. SCADE est vu souvent comme un canon pour écraser une mouche(sauf DO178 niveau A, évidement).

                              "La première sécurité est la liberté"

                              • [^] # Re: Destructeurs

                                Posté par  . Évalué à 4.

                                Si j'étais cynique, je dirais que cela me fait penser à la scène de Fight Club dans laquelle Edward Norton explique son travail à son « amie à usage unique » au début du film : « Une voiture construite par ma société roule aux alentours de 90 km/h, le différentiel arrière se bloque. La voiture se crache est prend feu avec toute la famille à bord. Question : faut-il déclencher un rappel de ce modèle ? Prendre le nombre de véhicules concernés : A; le multiplier par le taux probable de défaillances : B; puis multiplier le résultat par la moyenne des sommes que l'on a été condamné à verser : C. A * B * C = X, si cet X est inférieur au coût d'un rappel : on ne fait rien. » :-/

                                Transposer le principe aux coûts de développements…

                                Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                                • [^] # Re: Destructeurs

                                  Posté par  (site web personnel) . Évalué à 2.

                                  Dans l'automobile, c'est sans doute le cas. Dans l'aéronautique, les contrôleurs ont fait jeter des codes complets pour mauvaises traçabilités (par exemple, le 1er fadec de l'A400M, de mémoire).

                                  "La première sécurité est la liberté"

                                  • [^] # Re: Destructeurs

                                    Posté par  . Évalué à 1.

                                    Il y avait de l'exagération volontaire dans mon propos. Je peux même comprendre, qu'en dehors du niveau A, SCADE puisse apparaître comme un char d'assaut (cela étant, en prenant en compte la nécessité d'avoir des développeurs formés à l'outil, le coût serait-il supérieur pour du niveau B ?).

                                    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                                    • [^] # Re: Destructeurs

                                      Posté par  (site web personnel) . Évalué à 3.

                                      SCADE est un outil top pour n'importe quel code embarqué. Peu de bug, facile à modifier. Et le code généré est rapide (code C "propre").

                                      Mais comme le compilateur est certifié comme peut l'être un code aéronautique, il coute une blinde. Les clients ont du mal à justifier son cout par rapport à un GCC gratuit. Mais on a une boite brésilienne qui a préféré acheter du SCADE, car il ne trouvait de codeur en techno classique pour faire des petits drones. Et en équipe réduite, il refait son code beaucoup plus rapidement.

                                      "La première sécurité est la liberté"

                      • [^] # Re: Destructeurs

                        Posté par  . Évalué à 6.

                        Tu fais une analogie clairement abusive entre gestion manuelle et bug.

                        À chaque fois que l'on peut faire assurer une tâche automatiquement par une infrastructure fiable et éprouvée (un GC, par exemple), on réduit l'intervention manuelle du programmeur et donc on réduit la probabilité de bugs. C'est aussi bête que ça.

                        (ce n'est pas une analogie, c'est un lien de causalité)

                        Les domaines pour lesquels un GC est inenvisageable à l'heure actuelle ne risque pas de diminuer parce que, pour la plupart, ce sont des domaines où les performances et/ou la latence sont primordiales

                        Les domaines où les performances sont suffisamment "primordiales" pour interdire l'utilisation d'un GC fondent comme neige au soleil. Aujourd'hui, de plus en plus de jeux vidéo sont écrits avec un langage à GC (les jeux écrits avec Unity 3D, par exemple). On fait du calcul scientifique en Python. Cassandra, Hadoop sont écrits en Java. Des services critiques tournent sous une VM Erlang. etc.

                        • [^] # Re: Destructeurs

                          Posté par  (site web personnel) . Évalué à 3. Dernière modification le 11 mai 2016 à 23:27.

                          Les domaines où les performances sont suffisamment "primordiales" pour interdire l'utilisation d'un GC fondent comme neige au soleil. Aujourd'hui, de plus en plus de jeux vidéo sont écrits avec un langage à GC (les jeux écrits avec Unity 3D, par exemple). On fait du calcul scientifique en Python. Cassandra, Hadoop sont écrits en Java. Des services critiques tournent sous une VM Erlang. etc.

                          Ahahahah ( Désolé la fatigue probablement )

                          On dirait que tu as pris la liste d'exemple parfaite à ne pas prendre:

                          • Le coeur d'Unity est fait en C++. C# est utilisé pour le scripting des games mechanics. Et même avec ça, c'est un engine qui est connu pour ses problèmes de performances et sa mauvaise gestion du multi-coeur ( avant Unity 5 ). Des jeux comme Kerbal Space program ont énormement souffert de ça d'ailleurs. Sinon tous les autres 3D engines majeurs sont en C++.

                          • "On fait du calcul scientifique en Python". Tous (ou presque) les modules scientifiques et les simulateurs pour python sont codé en C/C++ ( cf numpy, pandas, matplotlib, hdf5, etc, etc ,etc ) avec généralement un backend BLAS / SuperLU / LAPACK. Seul l'interface est en python. Ce qui est tout à fait génial en soit, car ça combine les performances natives de C avec la flexibilité de scripting de python ( Encore mieux avec IPython / Jupither ).

                          • "Cassandra". Récemment, une société a recoder cassandra en C++ avec des performances qui parlent d'elle même je pense.

                          • "Hadoop". Tu devrais faire part de tes théories à MapR, un des principaux supporteur commercial de Hadoop, qui a recoder son propre MapReduce en C++ également.

                          • "Des services critiques tournent sous une VM Erlang". Je vois pas trop le rapport avec le beefsteak là. Personne ici n'a dit que les langages à GC était moins fiable. Et spécialement pas Erlang.

                          • [^] # Re: Destructeurs

                            Posté par  . Évalué à 5.

                            Pour scylla, on va peut-être éviter de prendre pour argent comptant les tests fait par l'éditeur, non ? Le seul test que j'ai vu (mais j'ai pas vraiment cherché je suis juste tombé dessus quand il est sorti) ne donnait pas plus envie que ça
                            http://blog.octo.com/scylladb-contre-cassandra-vers-un-nouveau-mythe/

                            Sachant qu'ils ne sont pas iso fonctionnels. Par exemple il n'y a pas de de vues matérialisées tu va devoir le faire à la main et c'est bien plus lent.

                            Ce sera peut être bien un jour, mais on va attendre quelques mois pour se décider (mais je doute).

                            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                            • [^] # Re: Destructeurs

                              Posté par  . Évalué à 5.

                              De même pour MapR et sa réécriture de HBase, HDFS et kafka. Ce n'est pas parce que ça existe que c'est véritablement plus performant.

                              J'ai l'impression comme avec Scilla, qu'il s'agit de gens qui veulent s'engouffrer en se disant qu'ils vont réécrire le truc à la mode mais en C++ (parce que c'est fooorcément plus performant !).

                              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                            • [^] # Re: Destructeurs

                              Posté par  . Évalué à 2.

                              Comparatif intéressant et sans doute plus impartial. L'article aurait pu s'intituler : « Quand la pratique peine à rejoindre la théorie ». :-)

                              En même temps, il y a peut être de la mauvaise foi non assumée chez une personne qui considère le benchmarkgame de chez Debian comme une « référence en terme d'optimisation » pour comparer les langages, là où pour les auteurs du comparatif ce n'est même pas le cas :

                              Non-motivation: We are profoundly uninterested in claims that these measurements, of a few tiny programs, somehow define the relative performance of programming languages.

                              source

                              Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                              • [^] # Re: Destructeurs

                                Posté par  (site web personnel) . Évalué à -2. Dernière modification le 12 mai 2016 à 10:33.

                                En même temps, il y a peut être de la mauvaise foi non assumée chez une personne qui considère le benchmarkgame de chez Debian comme une « référence en terme d'optimisation » pour comparer les langages, là où pour les auteurs du comparatif ce n'est même pas le cas.

                                Quand on a pas d'argumentation à avancer, on commence les attaques personnelles on dirait.

                                Le "The Computer Language Benchmarks Game" offrent des versions optimisées dans différents langages à des problèmes donnés. Et pour ces problèmes donnés, je le trouve personnellement trés bons et effectivement une "référence en terme d'optimisation". Mais peut-être ai-je tord ?

                                Mais étant donné ton apparente intelligence supérieure, je te propose de prouver d'éclairer ces pauvres plébéiens avec ta science de la preuve formelle et tes connaissances pointus des performances des GC en leur envoyant ta propre implémentation de leur benchs. (évidemment supérieur !)

                                https://benchmarksgame.alioth.debian.org/play.html

                                Je suis certain qu'ils se laisseront convaincre aisément si tu leur montrent des faits au lieu d'un beau discours.

                                • [^] # Re: Destructeurs

                                  Posté par  (site web personnel) . Évalué à 5.

                                  Le "The Computer Language Benchmarks Game" offrent des versions optimisées dans différents langages à des problèmes donnés. Et pour ces problèmes donnés, je le trouve personnellement trés bons et effectivement une "référence en terme d'optimisation". Mais peut-être ai-je tord ?

                                  Ce benchmark a pas mal de défauts :

                                  • Déjà les versions dites « optimisées » ne le sont pas toujours. Dans certains langages, il y a eu effectivement pas mal de temps passé pour optimiser le problème. Dans d'autres langages, c'est juste la première version qui a marché qui est testée, personne n'a cherché à optimiser.

                                  • Les langages n'implémentent pas tous les mêmes problèmes. Certains langages n'ont pas des implémentations pour tous les problèmes ou des versions trop anciennes qui ne compilent plus.

                                  • Les benchmarks sont très faussés vers une utilisation pure du CPU (absence total d'I/O). Et seulement 4 cœurs, ça paraît peu de nos jours (mon PC qui a quelques années en a 8).

                                  • Les règles pour les benchmarks m'ont l'air floues. J'ai l'impression que certains langages profitent de bindings vers un langage plus bas niveau (genre le C++ qui utilisent des trucs en C plutôt que leurs équivalents en C++). D'autres langages ont l'air de précalculer à la compilation (qui n'est pas prise en compte dans les comparaisons), ce qui n'est pas possible pour les langages dynamiques.

                                  • [^] # Re: Destructeurs

                                    Posté par  (site web personnel) . Évalué à 1.

                                    Déjà les versions dites « optimisées » ne le sont pas toujours. Dans certains langages, il y a eu effectivement pas mal de temps passé pour optimiser le problème. Dans d'autres langages, c'est juste la première version qui a marché qui est testée, personne n'a cherché à optimiser.

                                    Chacun est libre de fournir sa propre implémentation si amélioration.

                                    Les langages n'implémentent pas tous les mêmes problèmes.

                                    Oui, mais c'est clairement stipulé dans les résultats.

                                    Les benchmarks sont très faussés vers une utilisation pure du CPU

                                    C'est un cas d'utilisation qui les mets à l'égalité. Mesurer l'I/O c'est principalement mesurer l'OS, ça a très peu d’intérêt pour un bench qui veut évaluer un langage.

                                    Et seulement 4 cœurs, ça paraît peu de nos jours (mon PC qui a quelques années en a 8).

                                    Ils testent à la fois une execution séquentiel et parallèle, 4, 8, 16 ou 32.. le but ici n'est pas de benchmarké le weak scaling mais simplement la gestion grossière du multi-threading et du parallelisme.

                                    Les règles pour les benchmarks m'ont l'air floues. J'ai l'impression que certains langages profitent de bindings vers un langage plus bas niveau (genre le C++ qui utilisent des trucs en C)

                                    Tu as déja fait du C++ une fois dans ta vie pour sortir des énormités pareilles ? Depuis quand un programme C++ a besoin de bindings pour utiliser du C ?

                                    • [^] # Re: Destructeurs

                                      Posté par  (site web personnel) . Évalué à 3.

                                      Mesurer l'I/O c'est principalement mesurer l'OS, ça a très peu d’intérêt pour un bench qui veut évaluer un langage.

                                      Ce n'est pas si vrai que ça. C'est surtout un bench du sous système IO de la lib du langage. Et cela peut être très mal fait.

                                      Il suffit de voir la variété énorme d'appel système sous Linux, qui selon le cas d'usage ne sont pas les plus performant (mmap qui bouffe la mémoire, et ne peut agrandir un fichier; le read/write qui bufferise et donc ajoute une copie, mais permet de diminuer drastiquement la latence dû au noyau; splice() qui permet de partager un buffer noyau interne; la gestion d'un thread par disque augmente aussi les performances; les ios asynchrones masquent les latences mais peuvent être un cauchemar à utiliser)

                                      Disons que cela pourrait être un bon benchmark en soi. Devoir lire une grande quantité de fichier (10 000 ?) de taille moyenne (1 Mo ?) dans une arborescence, et d'écrire autant de fichiers (avec un checksum bidon dedans par exemple).

                                      C'est une charge hautement parallélisable, mais qui est complètement "IO bound".

                                      "La première sécurité est la liberté"

                                      • [^] # Re: Destructeurs

                                        Posté par  (site web personnel) . Évalué à 1.

                                        Ce n'est pas si vrai que ça. C'est surtout un bench du sous système IO de la lib du langage. Et cela peut être très mal fait.

                                        C'est vrai. Mais c'est quelque chose d'assez dure à mesurer, les I/O font toujours parti des aspects les plus dure à benchmarker d'un programme car elles sont très sensibles au effets de bords ( cache du kernel, utilisation de la mémoire, block size, … )

                                        Si tu rajoutes à ça le fait que la plupart des langages te donne à la fois leur propre librairie et un accés direct à l'API POSIX open/read/write de l'OS.

                                        Tu obtiens une situation où ce que tu mesures est bien souvent l'efficacité d'un pattern d'accés plutot que la performance de ta lib en question :)

                                        . Devoir lire une grande quantité de fichier (10 000 ?) de taille moyenne (1 Mo ?) dans une arborescence, et d'écrire autant de fichiers (avec un checksum bidon dedans par exemple).

                                        Ça pourrait être trés interessant en effet. Peut être un benchmark à leur proposer.

                                        La même chose concernant le taux de request par seconde sur un seul et même socket: le classique C10K problem.

                                        • [^] # Re: Destructeurs

                                          Posté par  (site web personnel) . Évalué à 3.

                                          "C'est vrai. Mais c'est quelque chose d'assez dure à mesurer, les I/O font toujours parti des aspects les plus dure à benchmarker d'un programme car elles sont très sensibles au effets de bords ( cache du kernel, utilisation de la mémoire, block size, … ) "

                                          C'est toujours pareil. Tu ne peux mesurer l'effet d'un seul chose, que toute chose étant égal par ailleurs. C'est vrai dans tout bench.

                                          "La même chose concernant le taux de request par seconde sur un seul et même socket: le classique C10K problem."

                                          C'est vieux :) Un "hello world" de 5 ligne en Go, te livre 20 000 req/s.

                                          "La première sécurité est la liberté"

                                          • [^] # Re: Destructeurs

                                            Posté par  (site web personnel) . Évalué à 1. Dernière modification le 13 mai 2016 à 14:56.

                                            C'est vieux :) Un "hello world" de 5 ligne en Go, te livre 20 000 req/s.

                                            Yep :) 10K est plus vraiment d'actualité mais le problème l'est toujours :)

                                            Je suis curieux de voir ce que donnerait les différents les différents langage / model de threading de nos jours sur ça.

                                            • [^] # Re: Destructeurs

                                              Posté par  (site web personnel) . Évalué à 2.

                                              Tu prends un hello world de chacun pour une application web http et tu utilises "ab".

                                              J'avais fais un crash test, que je prenais pour la vitesse maximum absolu pour faire un server de web application.

                                              Test sur mon portable sur un “hello wolrd” avec “ab -n 1000 -c 100”
                                              - 1300 req/s en ocsigen
                                              - 20 000 req/s avec go
                                              - 15 000 req/s en pure apache
                                              - 1900 re/s avec 2 ocsgen + haproxy

                                              "La première sécurité est la liberté"

                                    • [^] # Re: Destructeurs

                                      Posté par  (site web personnel) . Évalué à 5.

                                      Mesurer l'I/O c'est principalement mesurer l'OS, ça a très peu d’intérêt pour un bench qui veut évaluer un langage.

                                      Et pourtant, il y a de très gros écarts sur un benchmark comme celui de Tech Empower.

                                      https://www.techempower.com/benchmarks/#section=data-r12&hw=peak&test=query -> sur cet exemple, le C++ est loin derrière Java et Dart (!!?). Pourtant, il devrait être plus près de l'OS en termes d'appels systèmes et avec la gestion manuelle de la mémoire, il devrait être bien devant selon tes dires. Comme quoi, ce n'est pas si simple.

                                      Tu as déja fait du C++ une fois dans ta vie pour sortir des énormités pareilles ? Depuis quand un programme C++ a besoin de bindings pour utiliser du C ?

                                      Oui, j'ai déjà fait du C++ et non, le C++ n'a pas besoin de bindings pour faire du C. Je ne voulais pas parler uniquement du C++ mais de tous les langages qui utilisent des bibliothèques en C alors qu'ils pourraient faire sans avec le langage en question. Pour certains langages, ça passe par des bindings, pas pour le C++, et j'ai tout mis ensemble sous l’appellation de bindings ves le C.

                                      Ceci dit, mon point reste valide. Les règles du benchmark m'ont l'air floues pour savoir quand on peut utiliser une bibliothèque en C dans un langage autre que le C.

                                      • [^] # Re: Destructeurs

                                        Posté par  (site web personnel) . Évalué à 3.

                                        https://www.techempower.com/benchmarks/#section=data-r12&hw=peak&test=query -> sur cet exemple, le C++ est loin derrière Java et Dart (!!?). Pourtant, il devrait être plus près de l'OS en termes d'appels systèmes et avec la gestion manuelle de la mémoire, il devrait être bien devant selon tes dires. Comme quoi, ce n'est pas si simple.

                                        Sincèrement… Tu critiques le manque d'I/O dans le bench de Debian et son manque de règle.

                                        Et tu links un bench compare des frameworks Webs qui ont des implémentations différentes pour des objectifs différents associé à des Database différentes derrière des serveurs web différents sur la seule métrique du request/sec.

                                        Il y a plus de variable à prendre en compte sur un Bench comme ça que d'ours polaire en Alaska.

                                        Si je vois l’intérêt du bench pour le choix d'un framework Web. Tout ça n'a rien n'a voir avec l' I/O, ni même avec les perfs d'un langage d'ailleurs.

                                • [^] # Re: Destructeurs

                                  Posté par  . Évalué à 5.

                                  C'est si gentiment demandé. :-) Bruno Michel t'as en partie donnée une réponse satisfaisante.

                                  D'abord la citation que je donnais provient du site du benchmarck à la page why measure toy benchmark programms ?. Je la remets pour que ce soit clair, et le graissage n'est pas de moi mais se trouve dans la page original :

                                  Non-motivation: We are profoundly uninterested in claims that these measurements, of a few tiny programs, somehow define the relative performance of programming languages.

                                  Leur objectif, clairement affiché (« we are porofoundly unintersted »), n'est pas de se servir des résultats comme base pour determiner les performances relatives des différents langages. Visiblement, tu ne lui donnes pas le même objectif que ses auteurs.

                                  Ensuite, passons à l'analyse d'un des tests, celui qui semble te préoccuper le plus : la gestion de la mémoire. Pour mettre à l'épreuve les GC, il y a le test binary trees

                                  The work

                                  The work is to create binary trees - composed only of tree nodes all the way down-to depth 0, before any of those nodes are GC'd - using at-minimum the number of allocations of Jeremy Zerfas's C program. Don't optimize away the work.

                                  Le programme de Jeremy Zefra's, écrit en C donc avec gestion manuelle de la mémoire, est celui qui sert de référence et qui réalise le meilleur score du test

                                  Maintenant, comparons ses performances face à deux langages avec GC :

                                  Code source temps charge cpu
                                  C gcc #3 3.26 59% 76% 78% 99%
                                  Haskell GHC 25.62 36% 88% 49% 36%
                                  OCaml #2 25.82 89% 95% 54% 64%
                                  OCaml #5 41.55 1% 100% 1% 1%

                                  Et là, on peut se dire : ces deux langages, avec leur gestion automatique, se prennent une valise ! M'enfin, OCaml a deux codes pour le test et avec un grande différence de temps entre le deux, pourquoi ? Alors, on regarde la charge CPU : le second est mono thread et pas le premier, donc déjà il y a une optimisation faite qui ne change rien au coût du GC et qui consiste dans la parallélisation des calculs. Mais au fait, n'y aurait-il pas d'autres code en C qui ont passé le test ? Et bien si, et voilà le résultat :

                                  Code source temps charge cpu
                                  C gcc #5 21.20 96% 89% 97% 97%

                                  Tiens, on se rapproche déjà plus des performances de OCaml et Haskell ! Mais que ce passe-t-il donc dans ce code C par rapport au super code de la mort qui tue en 3.26 secondes ? Tout simplement il optimise bien moins le parallélisme. Il utilise pthread là où le plus rapide utilise apr-1.0.

                                  Au final, les résultats du test (qui était sensé mettre à l'épreuve les GC) mais surtout en avant des optimisations de parallélisme. On trouvera les différents codes à ces adresses :

                                  On pourra également noté que pour le code OCaml le plus rapide le parallélisme est fait à l'arrache (voir dans les commentaires : « Rough parallelization by Mauricio Fernandez »), que le code Haskell n'a pas le droit (c'est la règle du jeu) de profiter de son évaluation paresseuse (ce qu'aucun développeur ne fera dans du code réel) :

                                  --
                                  -- an artificially strict tree. 
                                  --
                                  -- normally you would ensure the branches are lazy, but this benchmark
                                  -- requires strict allocation.
                                  --
                                  data Tree = Nil | Node !Int !Tree !Tree

                                  et enfin que le code compilé OCaml ne profite pas de dernières optimisations apportées dans le compilateur par Flambda, contrairement au code C qui est compilé avec l'option -O3 de gcc.

                                  Cela étant pour répondre à ta proposition de soumettre mon propre au code au test (qui devrait uniquement consister en une optimisation du parallélisme pour se rapprocher du haut du panier), j'ai autre chose à faire de mon temps que de jouer à « qui à la plus grosse ? ». Je suppose qu'il en est de même pour Fabrice Le Fessant, le fondateur de OCaml Pro, vu que c'est lui qui a soumis le code le plus lent (enfin, il a apporté des modifications sur le code d'un autre) : il sait pertinemment optimiser du calcul parallèle en OCaml, mais il doit avoir d'autres chats à fouetter.

                                  Les benchmarks, c'est bien, encore faut-il en analyser correctement les résultats. ;-)

                                  Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                                  • [^] # Re: Destructeurs

                                    Posté par  (site web personnel) . Évalué à 0. Dernière modification le 12 mai 2016 à 21:27.

                                    Leur objectif, clairement affiché (« we are porofoundly unintersted »), n'est pas de se servir des résultats comme base pour determiner les performances relatives des différents langages.

                                    Je déteste me répéter. Encore une fois, ce qu'ils annoncent c'est que ces benchmarks ne sont pas représentatifs des performances d'un langage dans son ensemble mais pour un problème donné justement défini par le toy benchmark lui même.

                                    Je te conseil sincèrement de te renseigner plus sur le concept de "mini-app" et ce que ça implique dans le domaine du Calcul à haute performance (HPC). Ça t'evitera de sortir ce genre d'anerie à l'avenir.

                                    Ensuite, passons à l'analyse d'un des tests, celui qui semble te préoccuper le plus : la gestion de la mémoire. Pour mettre à l'épreuve les GC, il y a le test binary trees

                                    Toute les mini-app à base binary tree montrent principalement l'impact du trashing du cache du processeur.
                                    Si tu veux un benchmark qui pousse un GC à ses limites dans un context des calculs mathématiques plus intensif. Le mini-app sur sur mandelbrot est beaucoup plus intéressant.

                                    Tiens, on se rapproche déjà plus des performances de OCaml et Haskell ! Mais que ce passe-t-il donc dans ce code C par rapport au super code de la mort qui tue en 3.26 secondes ? Tout simplement il optimise bien moins le parallélisme. Il utilise pthread là où le plus rapide utilise apr-1.0.

                                    Tu ne sais pas lire du code C j'ai juste ?

                                    apr n'a rien à voir avec pthread ni même avec le threading. gcc#3 utilise openMP et gcc#5 pthread.

                                    La différence de performance provient trés probablement du fait que gcc#3 utilise une gestion manuel de la mémoire avec une memory pool ( utilisant apr ).

                                    Ironiquement, tu as selectionner un bench qui valide ce que je disais précédemment sur les memory pool.

                                    vu que c'est lui qui a soumis le code le plus lent (enfin, il a apporté des modifications sur le code d'un autre) : il sait pertinemment optimiser du calcul parallèle en OCaml

                                    Ou peut-être que OCaml a un global interpreter lock qui l'empeche d'utiliser proprement du multi-threading, et que même le meilleur programmeur du monde ne peut pas changer ça ?
                                    C'est d'ailleurs pour cela que le benchmark OCaml#2 fork() / join() et multi-process pour pouvoir compenser.

                                    Les benchmarks, c'est bien, encore faut-il en analyser correctement les résultats. ;-)

                                    Assez ironique en soit quand on réalise que ton analyse est presque fausse du début à la fin.

                                    Ceci dit, je déteste perdre mon temps avec des personnes dont l'argumentation n'étaye sur aucun fait mais juste sur leur propre théologie. Je cesserai donc de poster ici.

                                    Je te conseille par contre de t’intéresser un poil plus au langages à bas niveau et pas seulement au calcul formelle. tu risques de voir le monde un peu plus pragmatiquement :)

                          • [^] # Re: Destructeurs

                            Posté par  (site web personnel) . Évalué à 2.

                            On peut aussi parler de Rust, le langage de Mozilla, qui offre le choix concernant la gestion de mémoire (GC ou pas GC, utilisation de pile, etc…).

                            "La première sécurité est la liberté"

                            • [^] # Re: Destructeurs

                              Posté par  . Évalué à 4.

                              Rust ne permet pas d'utiliser un GC aujourd'hui. C'était dans les cartons au début, mais ils ont abandonné l'idée—en 2013, si je me souviens bien. Depuis pknfelix essaie de travailler là-dessus, que ce soit implémenter un bon GC optionnel ou au moins collaborer avec du code extérieur (non-Rust) utilisant un GC, mais c'est vraiment difficile. Cf. cette série de billets de blog récente.

                          • [^] # Re: Destructeurs

                            Posté par  . Évalué à 1.

                            Le coeur d'Unity est fait en C++. C# est utilisé pour le scripting des games mechanics.

                            Et alors ? C'est justement l'important : les milliers de programmeurs utilisant Unity écrivent en C# et n'ont pas à gérer la mémoire à la main.

                            Seul l'interface est en python.

                            Même réponse. On s'en fout qu'une partie du backend soit écrit en C/C++, lorsqu'on n'a pas soi-même à écrire du C/C++ ni à gérer la mémoire à la main. Les objets Numpy et Pandas sont bel et bien gérés par la même infrastructure que n'importe quel autre objet Python…

                            • [^] # Re: Destructeurs

                              Posté par  (site web personnel) . Évalué à 2. Dernière modification le 12 mai 2016 à 13:40.

                              Même réponse. On s'en fout qu'une partie du backend soit écrit en C/C++, lorsqu'on n'a pas soi-même à écrire du C/C++ ni à gérer la mémoire à la main. Les objets Numpy et Pandas sont bel et bien gérés par la même infrastructure que n'importe quel autre objet Python…

                              Alors ne viens pas prétendre que "Les domaines où les performances sont suffisamment "primordiales" pour interdire l'utilisation d'un GC fondent comme neige au soleil.". C'est faux et hypocrite.

                              C'est juste qu'on tend à utiliser ces implémentations en C/C++ dans des langages de plus haut niveau.

                              • [^] # Re: Destructeurs

                                Posté par  . Évalué à 1.

                                Mouarf. C'est un monument de mauvaise foi, là…

                                Des langages comme Python, Java ou C# progressent là où auparavant on n'utilisait que du C ou du C++ (ou Fortran), il est donc parfaitement justifié de dire que les langages à gestion automatique de la mémoire progressent par rapport à ceux à gestion manuelle de la mémoire.

              • [^] # Re: Destructeurs

                Posté par  (site web personnel) . Évalué à 9.

                C'est le genre d'argumentation qui a le don de m'énerver. Un bon GC est meilleure qu'un mauvais programmeur : ho la bonne surprise. Je vais te la faire à l'envers : un mauvais GC est pire qu'un bon programmeur ! Et on a bien fait avancer le bouzin.

                Tout à fait, et un bon GC, il mark and sweep, mais c'est un bon GC, tandis qu'un mauvais GC… il mark and sweep mais c'est un mauvais GC…

                « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

            • [^] # Re: Destructeurs

              Posté par  (site web personnel) . Évalué à 4. Dernière modification le 10 mai 2016 à 09:16.

              D'une part un compteur de référence est une forme de garbage collector

              On peut jouer avec les mots, mais ce n'est pas un garbage collector classique mark & sweep ou generationel et ça n'en a pas les propriétés. Ça n'a ni les problemes de lifetime associés au GC GC classiques, ni les problemes de surconsommation memoires ( meme si ca en a d'autres ) . Ce qui le rend par ailleurs beaucoup plus facile a interfacer avec du C/C++ que du Java… entre autres…

              C'est plutot bien detaillé ici par ailleurs ( http://effbot.org/pyfaq/how-does-python-manage-memory.htm )

              Inexact. En fonction de la façon dont ton application alloue et désalloue la mémoire, un GC peut s'avérer plus performant qu'une gestion manuelle. En particulier si tu as des structures profondes (arbres ou listes) que tu modifies peu au cours de leur vie (i.e tu les alloues, tu les utilises sans les modifier, puis tu les désalloues). Dans ce cas, un bon GC pourra tout désallouer d'un coup alors qu'une gestion manuelle t'obligera à désallouer chaque nœud de ta structure.

              Pour reprendre ton pattern.

              Non. Une gestion manuelle bien fait sera toujours plus performante et plus facile à profiler. Un GC peut savouer un poil plus performant qu'une gestion manuelle naive dans certains cas précis. Le cas que tu cites se résoud avec une simple Memory Pool local au life time de ton graphe.

              Faux. Il existe des GC temps réel (donc qui garantissent une latence max). Bien sur, on n'a rien sans rien et ces GC sont généralement encore plus gourmands en mémoire que les autres.

              Non. Ça c'est ce au'on nous annonce depuis 10 ans environ. Fait est qu'en pratique, même Android malgrés les sommes colossales investi par Google dans Dalvik, continue d'avoir des problèmes de latence associés á son GC.

              Note que de toutes façons ceux qui développent des systèmes vraiment critiques en termes de performances (jeux, par ex.) évitent les allocations dans la partie critique (aussi bien avec un GC, qu'avec malloc) car ni l'un ni l'autre n'ont de performances garanties.

              Non. Ça c'est la théorie. En pratique tu ne peux pas éviter d'avoir des allocations même dans la partie critique. Ce que en C/C++ tu compenseras par un pool allocator, un stack allocator ou par l'utilisation de jemalloc, tcmalloc ou le tbbmalloc si ton problème vient d'un haut niveau de concurrence. Toutes ces strategies sont communément deployé dans des apps que vous utilisez tous les jours ( jeux video, database, server web, etc, etc )

              • [^] # Re: Destructeurs

                Posté par  . Évalué à 10.

                Non. Une gestion manuelle bien fait sera toujours plus performante et plus facile à profiler. Un GC peut s'avouer un poil plus performant qu'une gestion manuelle naïve dans certains cas précis. Le cas que tu cites se résoud avec une simple Memory Pool local au life time de ton graphe.

                Pour moi c'est un peu comme dire que gcc n'optimise pas ton code aussi bien que si tu avais directement écris le code assembleur que gcc avait sorti. L'humain est tout à fait capable de sérialiser des boucles, réorganiser les instructions et inliner des méthodes ou aller taper des instructions spécifique de la CPU par exemple. Mais bizarrement on ne le fait presque plus (je ne dis pas qu'il ne faut plus le faire, je dis qu'on peut très bien comprendre que les gens ne le font plus).

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

              • [^] # Re: Destructeurs

                Posté par  . Évalué à 4.

                Ça c'est la théorie. En pratique tu ne peux pas éviter d'avoir des allocations même dans la partie critique.

                Une théorie qui ne correspond pas à la pratique, ou que l'on ne peut mettre en pratique c'est du vent : peut être de jolis mots mis les uns à la suite des autres, mais rien de plus (du blalba, en gros). Dit autrement, si ça ne vaut rien en théorie ça ne vaut rien en pratique et réciproquement.

                Cela étant, dans la pratique, s'il y a bien un domaine où le temps c'est de l'argent c'est le trading haute fréquence (THF ou HFT). La latence peut coûter un paquet de pognon, mais les risques de bug encore plus; alors les acteurs de ce secteur font du calcul de risques (et ça, crois moi, ils savent faire ;-) entre le coût, les avantages et les défauts d'utiliser un langage avec GC pour leur back-end.

                Et la société de courtage Jane Street qui gère 8 milliards de dollars et des millions de transactions par jour (2% de l'activité journalière de leur secteur) utilise OCaml pour leur back-end :

                Les sociétés de courtage manqueraient-elles de pragmatisme ?

                Sinon, je n'ai jamais croisé d'universitaire qui m'a soutenu qu'un système avec GC pouvait faire mieux que du C sur le plan de la latence (remarque de bon sens, un GC rajoute toujours de l'overhead).

                Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                • [^] # Re: Destructeurs

                  Posté par  (site web personnel) . Évalué à 3. Dernière modification le 10 mai 2016 à 16:34.

                  Les sociétés de courtage manqueraient-elles de pragmatisme ?

                  Sinon, je n'ai jamais croisé d'universitaire qui m'a soutenu qu'un système avec GC pouvait faire mieux que du C sur le plan de la latence (remarque de bon sens, un GC rajoute toujours de l'overhead).

                  On doit pas connaitre les même société de Fast-Trading alors.
                  Au passage… "Jane Street Capital" est une société de Trading, pas de "Fast Trading".

                  Les vrai sociétés de Fast trading que je connais ( comme Jump Trading aux USA ) vont jusqu'à recoder leur propre driver InfiniBand qu'ils pour gagner quelques nano-secondes sur leur opérations.

                  Toute leur stack logiciel est en C/C++, ils disposent de leur propre supercomputer et sont présent chaque année dans les plus grosses conférences du monde HPC (SC ou ISC) juste pour tenter comment gagner un petit poil de latence sur leur transactions.

                  Ces gens là te rigoleront à la figure dés que tu commenceras à leur parler de Garbage Collector ou d'OCaml. Et sincérement, ils auront raison.

                  • [^] # Re: Destructeurs

                    Posté par  . Évalué à 4.

                    Au passage… "Jane Street Capital" est une société de Trading, pas de "Fast Trading".

                    Il est vrai que cela fait une grande différence, et rend la solution avec GC acceptable.

                    Pour le reste, utiliser C/C++ est une question de bon sens : c'est le seul moyen d'exploiter à 100% les possibilités du matériel, toute approche avec GC bridant la puissance du matériel. À matériel équivalent, un GC n'égalera jamais une gestion manuelle, fine et bien faite.

                    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                    • [^] # Re: Destructeurs

                      Posté par  (site web personnel) . Évalué à 5.

                      Je ne suis pas sûr.

                      Il existe un paradigme que je n'ai quasiment jamais vu utiliser. SCADE s'interdit toute allocation et utilise de gros buffer statique, et cela marche très bien pour 99% du code embarqué.

                      Le problème d'absence d'allocation se pose si la taille des entrées n'est pas connu. C'est le cas dans la norme ARINC 661 qui est un serveur graphique dont les widgets sont connu à l'init déclaré dans un fichier de définition.

                      Je pense que l'allocation à l'init permet de gérer pas mal de cas, ou au moins, à un temps précis, ce qui permet de gérer l'erreur le plus tôt possible. Un système à gestion automatique de mémoire, pourrait calculer les besoins mémoire au démarrage (selon la taille d'un fichier par exemple) et ensuite ne plus faire d'allocation du tout.

                      Une autre vois serait de mieux gérer l'appartenance des objets. Dans un système classique, un objet est créé en vrac et on fait des références dessus. Dans l'ingénierie des modèles, si on définit un diagramme de classe, on a clairement une notion d'appartenance à un arbre, et "des" références. Mais cela veut clairement dire que la destruction de la racine de l'arbre entraine toutes les feuilles. Je ne connais pas non plus de gestion de mémoire automatique qui utilise cette propriété.

                      "La première sécurité est la liberté"

                      • [^] # Re: Destructeurs

                        Posté par  . Évalué à 1.

                        Je ne connaissais pas le principe, l'idée est intéressante ça mérite réflexion. L'idée est de déterminer « statiquement » les besoins en mémoire, non à la compilation, mais le plus tôt possible à l'exécution et ne plus faire d'allocations par la suite ?

                        Ce que tu décris sur la gestion des objets, n'est-ce pas justement le principe du RAII ?

                        Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

                        • [^] # Re: Destructeurs

                          Posté par  (site web personnel) . Évalué à 2.

                          "Je ne connaissais pas le principe, l'idée est intéressante ça mérite réflexion. L'idée est de déterminer « statiquement » les besoins en mémoire, non à la compilation, mais le plus tôt possible à l'exécution et ne plus faire d'allocations par la suite ?"

                          Oui, c'est l'idée. Cela peut s'appliquer souvent. Je crois que haproxy fonctionne ainsi. Tu as un modèle de mémoire qui ne se croit pas infini. Il faut donc en demander au début une certaine quantité, et ne plus en bouger. En plus, cela évite les problèmes de fragmentation.

                          "Ce que tu décris sur la gestion des objets, n'est-ce pas justement le principe du RAII ?"

                          Cela y ressemble un peu. Mais tu peux imaginer des cas supplémentaires qui détruisent une arborescence d'objet en dehors du RAII. Et il faut gérer en plus les pointeurs qui ciblent un objet détruit.

                          "La première sécurité est la liberté"

        • [^] # Re: Destructeurs

          Posté par  . Évalué à 4.

          Moi je remarque surtout que des langages massivement plus lents que ça sont lourdement utilisés en production (Javascript, Ruby, PHP, Python, etc.),

          Et ces langages font souvent appel à du C pour les fonctions consommatrices de ressources. Après, je suis très content d'utiliser ces langages, il ne faut juste pas se voiler la face.

          es programmes sans gestion automatique de la mémoire sont truffés de faille de sécurité béantes qui nous poussent être mettre à jour nos systèmes à la va-vite chaque semaine

          Et on fait aussi des mises à jour pour les programmes en PHP chaque semaine.

          « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

          • [^] # Re: Destructeurs

            Posté par  (site web personnel) . Évalué à 5.

            Et ces langages font souvent appel à du C pour les fonctions consommatrices de ressources. Après, je suis très content d'utiliser ces langages, il ne faut juste pas se voiler la face.

            D'ailleurs, on peut faire du calcul matriciel en Python qui soit aussi performant que le code C équivalent. Ça n'a absolument rien de magique, car les bibliothèques bien écrites (comme numpy) sont codées en C, et le code Python (qui est la seule chose que le développeur voit, et qui sera facile à écrire et concis) ne sert que de ciment au code qui calcul. Du coup, on se moque que le code Python ne soit pas très performant, vu qu'on n'y passe qu'un pouillème du temps de calcul.

            • [^] # Re: Destructeurs

              Posté par  . Évalué à 3.

              Comment ça se passe pour le déploiement ? Tu as une version de numpy pour x86, x86-64, ARM, Linux/Windows/MacOS/*BSD/Android/iOS,… ?

              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

              • [^] # Re: Destructeurs

                Posté par  (site web personnel) . Évalué à 5.

                Tu as trois possibilités pour les paquets Python :

                • avec le système de packaging de ta distrib (apt-get install python-numpy)
                • depuis le dépôt avec les sources et la recompilation qui va avec
                • depuis le dépôt avec des versions précompilées (avec plusieurs choix d'archi)

                par exemple : https://pypi.python.org/pypi/numpy/1.9.3

                • [^] # Re: Destructeurs

                  Posté par  (site web personnel) . Évalué à 2.

                  Et installation avec l'outil ligne de commande pip.

                  Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

                • [^] # Re: Destructeurs

                  Posté par  . Évalué à 3.

                  depuis le dépôt avec les sources et la recompilation qui va avec

                  C'est ça qui m'intéresse. Ça signifie qu'il y a un build de la bibliothèque par plateforme (couple architecture CPU/OS) ? Ton lien montre uniquement 2 architectures et 2 OS, mais je me doute qu'il y a plus (windows et ARM au minimum)… CPAN a un truc pour ça (exemple pour POE et je vois pas les CPU) et lors de l'installation c'est compilé en local (c'est lent et embêtant).

                  Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: Destructeurs

              Posté par  (site web personnel) . Évalué à 3.

              D'après ce que je comprends, le calcul matriciel de numpy utilise une bibliothèque de type BLAS qui est a des chances d'être codée … en Fortran! :) C'est au moins le cas d'OpenBLAS une des principales implémentations, et l'implémentation historique qui a fait long feu.

              • [^] # Re: Destructeurs

                Posté par  . Évalué à 3.

                Bien évidemment. On ajoutera que le noyau et le compilateur Fortran sont codés en C (*), que dans Numpy il y a beaucoup de code C également, et que derrière tout cela il y a certainement aussi des lignes d'assembleur (peut-être même qu'à un moment des gens ont entré des octets à la main directement dans la mémoire—enfin, à l'aide d'un clavier tout de même, quoique).

                Il reste que pour le programmeur utilisant Numpy, le langage exposé est Python, et la sémantique de gestion de la mémoire est aussi celle de Python, ce qui est un peu l'important ici.

                (*) Ok, pour le compilateur Fortran, j'ai la flemme de vérifier ;-)

                • [^] # Re: Destructeurs

                  Posté par  (site web personnel) . Évalué à 2.

                  Pour clarifier, je réponds à cette phrase de ton message:

                  Ça n'a absolument rien de magique, car les bibliothèques bien écrites (comme numpy) sont codées en C

                • [^] # Re: Destructeurs

                  Posté par  (site web personnel, Mastodon) . Évalué à 6.

                  (peut-être même qu'à un moment des gens ont entré des octets à la main directement dans la mémoire—enfin, à l'aide d'un clavier tout de même, quoique).

                  http://la.buvette.org/wikipics/supercoder2.jpg

                  • [^] # Re: Destructeurs

                    Posté par  . Évalué à -6.

                    Je crois que en réalité les claviers étaient beaucoup plus petits que ne le laisse présager ta photographie.

      • [^] # Re: Destructeurs

        Posté par  (site web personnel) . Évalué à 6.

        Sans parler que c'est aussi s'imposer une limite concernant les performances…. Ce qui peut être un problème pour un langage qui se veut "avec des performances proches du C".

        En réalité c'est un problème très subtil, un cas classique où un programme ou la mémoire est gérée de façon manuelle va se comporter plus mal qu'un programme dont la mémoire est gérée automatiquement, est celui d'un programme avec des allocations de longue durée qui va fractionner sa mémoire. Le fractionnement de la mémoire a un impact critique sur la performance lorsqu'il empêche la localisation des données en mémoire, c'est à dire lorsqu'à cause du fractionnement, les données allouées simultanément pour un calcul vont se retrouver dans des pages de cache différentes. Dans un langage où la mémoire est gérée automatiquement, le ramasse-miette peut déplacer les données en mémoire et garantir une bonne localisation tandis que dans un langage comme C, c'est impossible de garantir cette localisation sans utiliser un système spécialisé.

  • # Gestionnaire de package : shards

    Posté par  . Évalué à 2.

    À notez que Crystal est livré avec son gestionnaire de package nommé shards. Il n'y a pas de dépôt centralisé comme avec RubyGem ou encore npm.

  • # Pré-emptif

    Posté par  . Évalué à -7.

    petit question sur le pre-emptif qui est opposé dans la news à un système de fibre que je n'ai pas bien compris.

    Sur un processeur à plusieurs coeur on peu attacher des processus sur chacun des coeurs afin qu'il s'éxecutent indépendamment. Donc si j'ai bien compris la fréquence des dit coeurs n'entre que peu en compte sur la vitesse d'execution.

    Comment traduire l'éxécution dans un système de "fibre", n'est pas plutot la description de l'isolation du flux des données au sein de l'environement d'éxécution de mon application dans ce langage ?

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.