• [^] # Re: On s'en bat le steak

    Posté par . En réponse au journal Typage statique pour Python. Évalué à 1. Dernière modification le 05 juin 2016 à 10:38.

    Je vois un peu mieux le principe du borrow checker, je regarderai peu être plus en détail la façon dont Rust gère les références. Cela étant, au début de Rust il y avait un GC qu'ils ont abandonné, mais l'idée de remettre de la gestion automatique est toujours présente, cela semble être néanmoins compliqué. Il y a deux articles sur le sujet sur le blog d'un des membres de l'équipe du compilateur :

    Sinon quelle différence fais-tu entre un langage qui peut faire de la programmation système et un langage système ? Il y a des personnes qui écrivent des noyaux, sous la forme d'unikernel, en OCaml :

    MirageOS is a library operating system that constructs unikernels for secure, high-performance network applications across a variety of cloud computing and mobile platforms. Code can be developed on a normal OS such as Linux or MacOS X, and then compiled into a fully-standalone, specialised unikernel that runs under the Xen hypervisor.

    MirageOs

    Il y a bien des problèmes de latence liés au GC qui peuvent apparaître (contrairement à la gestion totalement manuelle du C par exemple) mais cela se gère aussi en adaptant son fonctionnement à l'application. Je ne connais pas le fonctionnement du GC de Haskell (mais d'après l'article c'est similaire à celui de OCaml), mais pour celui de OCaml ce chapitre de Real World OCaml en détail bien le fonctionnement.

    Le premier commentaire de l'article que tu cites renvoie sur un benchmark (dont je n'ai pas estimé la pertinence) qui mesure la latence d'un serveur web qui stocke une hashtable (pour le serveur web en OCaml, il utilise d'ailleurs une bibliothèque développée par l'équipe de MirageOS) :

    benchresult

    J'ai eu une polémique récemment sur le coup de la latence du GC de OCaml en rapport à ce benchmark du benchmarkgame de chez Debian. J'avais mal estimé le rôle du GC dans les mauvais résultats : comme pour le cas de ton article, il y a des objets trop gros qui restent vivants trop longtemps. En adaptant la taille de la minor heap on obtient de meilleurs résultats (j'utilise aussi la bibliothèque functory, qui fait une sorte de MapReduce, pour le parallélisme ) :

    open Functory.Cores
    let () = set_number_of_cores 4
    let set_minor_heap size = Gc.set {(Gc.get()) with Gc.minor_heap_size=size}
    type tree = Empty | Node of tree * int * tree
    let rec make i d =
     if d = 0 then Node(Empty, i, Empty)
     else let i2 = 2 * i and d = d - 1 in Node(make (i2 - 1) d, i, make i2 d)
    let rec check = function Empty -> 0 | Node(l, i, r) -> i + check l - check r
    let min_depth = 4
    let max_depth = 
     let n = try int_of_string(Array.get Sys.argv 1) with _ -> 10 in
     max (min_depth + 2) n
    (*
    * c'est ici que j'adapte la taille de la minor heap 
    * ce qui évite de nombreuse copie entre la minor et la major heap
    * et réduit grandement la latence due au GC 
    *)
    let () = set_minor_heap (8 lsl max_depth)
    let stretch_depth = max_depth + 1
    let () =
     let c = check (make 0 stretch_depth) in
     Printf.printf "stretch tree of depth %i\t check: %i\n" stretch_depth c
    let long_lived_tree = make 0 max_depth
    let worker (niter, d) =
     let c = ref 0 in
     for i = 1 to niter do 
     Gc.minor();
     c := !c + check(make i d) + check(make (-i) d)
     done;
     !c
    let () =
     flush stdout;
     let tasks =
     let l = ref [] in
     for i = ((max_depth - min_depth) / 2 + 1) - 1 downto 0 do
     let d = min_depth + i * 2 in
     let niter = 1 lsl (max_depth - d + min_depth) in
     l := ((niter, d), None) :: !l
     done;
     !l
     in
     let res = ref [] in
     let master ((niter, d), _) c =
     res := (2*niter, d, c) :: !res;
     []
     in
     (* il n'y a plus de major collection pendant ce calcul *)
     compute ~worker ~master tasks;
     let log (niter, d, c) =
     let s = Printf.sprintf "%i\t trees of depth %i\t check: %i" niter d c in
     print_endline s;
     in
     List.iter log (List.sort (fun (_, d, _) (_, d', _) -> compare d d') !res);
     Printf.printf "long lived tree of depth %i\t check: %i\n"
     max_depth (check long_lived_tree)

    avec ce code, sur ma machine à 4 cœurs, j'obtiens comme résultat standard avec un time btree 20 :

    real 0m5.411s
    user 0m16.320s
    sys 0m0.492s
    

    là où avec le code C le plus performant je suis dans cet ordre de grandeur :

    real 0m3.721s
    user 0m13.144s
    sys 0m0.204s
    

    c'est pas si mal et bien mieux que le 25.82 s du meilleur code OCaml du test. D'autant que, n'étant pas moi même programmeur (je suis mathématicien et logicien, pas informaticien), il est fort probable qu'un spécialiste du langage puisse encore améliorer cela.

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