IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Caml Discussion :

Bonne pratique du type Option


Sujet :

Caml

  1. #21
    Membre éprouvé
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    832
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 832
    Points : 1 104
    Points
    1 104
    Par défaut
    Le raise tout comme le try sont relativement coûteux, puisqu'ils demandent une sauvegarde et une restauration de l'environnement d'exécution.
    Il me semble qu'on peut implémenter les exceptions en se contenant de stocker un pointeur vers le dernier "exception handler" placé sur la pile, et se contenter d'y revenir. Ça fait un coût léger pour l'installation de l'exception handler (il faut mettre les informations sur la pile, comme quand on appelle une fonction), et pour le lancement de l'exception c'est équivalent à un appel terminal.

    Ce qui est exceptionnel ou non dépend des applications. Dans de nombreux usages des tables de hachage tu peux garantir que tous les accès se feront sur des éléments existants.

    J'aime bien aussi utiliser les type option pour les erreurs, mais je trouve que tu es trop catégorique contre les exceptions : ce n'est pas 1000 fois plus laid (quand les exceptions sont bien documentées, surtout à l'interface du module), et ce n'est pas beaucoup plus lent.

    Si tu as des benchmarks concrets qui montrent une différence sensible de vitesse, je suis intéressé. Je sais qu'on peut écrire des micro-benchmarks en faveur de l'une ou l'autre solution (je soupçonne le fait que les types options sont légèrement plus efficaces quand le retour "exceptionnel" est fait directement par la fonction appelée, et moins efficaces quand on traverse plusieurs appels imbriqués qui utilisent tous le résultat comme une valeur), mais je pense que tu as tort et que sur un code réaliste qui ne fait pas que ça, les exceptions ne sont pas sensiblement plus lentes.

    Pour moi l'avantage et l'inconvénient des monades d'exception (type option, etc.) n'est pas du tout lié aux questions de performances : c'est le fait qu'elles fassent apparaître les erreurs dans le type de retour de la fonction et qu'elles forcent l'appelant à toujours gérer le cas s'il veut une valeur. C'est souvent une bonne chose, mais parfois désagréable. Je crois qu'il n'y a pas une solution qui soit parfaite dans tous les cas, et qu'il faut accepter que les gens fassent des choix différents.

  2. #22
    Membre éprouvé
    Avatar de Cacophrene
    Homme Profil pro
    Biologiste
    Inscrit en
    Janvier 2009
    Messages
    535
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Biologiste

    Informations forums :
    Inscription : Janvier 2009
    Messages : 535
    Points : 1 125
    Points
    1 125
    Par défaut
    Bonsoir le forum,

    Sur un code non critique le choix du type option à la place d'une exception, ou le contraire, est sans aucune conséquence sur les performances globales de l'application. À mon avis, un accès disque, à lui seul, fera perdre au centuple ce qu'on peut gagner en utilisant l'un plutôt que l'autre !

    Mais en laissant de côté le pragmatisme, j'avoue que je préférerais disposer de fonctions interruptibles comme on en trouve dans LablGTK pour les callback dans la gestion des événements. Dans ce sens, je rejoins un peu IOCWT.

    Cordialement,
    Cacophrène

  3. #23
    Membre éprouvé
    Avatar de InOCamlWeTrust
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    1 036
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 1 036
    Points : 1 284
    Points
    1 284
    Par défaut
    Citation Envoyé par bluestorm Voir le message
    Il me semble qu'on peut implémenter les exceptions en se contenant de stocker un pointeur vers le dernier "exception handler" placé sur la pile, et se contenter d'y revenir. Ça fait un coût léger pour l'installation de l'exception handler (il faut mettre les informations sur la pile, comme quand on appelle une fonction), et pour le lancement de l'exception c'est équivalent à un appel terminal.

    Ce qui est exceptionnel ou non dépend des applications. Dans de nombreux usages des tables de hachage tu peux garantir que tous les accès se feront sur des éléments existants.

    J'aime bien aussi utiliser les type option pour les erreurs, mais je trouve que tu es trop catégorique contre les exceptions : ce n'est pas 1000 fois plus laid (quand les exceptions sont bien documentées, surtout à l'interface du module), et ce n'est pas beaucoup plus lent.

    Si tu as des benchmarks concrets qui montrent une différence sensible de vitesse, je suis intéressé. Je sais qu'on peut écrire des micro-benchmarks en faveur de l'une ou l'autre solution (je soupçonne le fait que les types options sont légèrement plus efficaces quand le retour "exceptionnel" est fait directement par la fonction appelée, et moins efficaces quand on traverse plusieurs appels imbriqués qui utilisent tous le résultat comme une valeur), mais je pense que tu as tort et que sur un code réaliste qui ne fait pas que ça, les exceptions ne sont pas sensiblement plus lentes.

    Pour moi l'avantage et l'inconvénient des monades d'exception (type option, etc.) n'est pas du tout lié aux questions de performances : c'est le fait qu'elles fassent apparaître les erreurs dans le type de retour de la fonction et qu'elles forcent l'appelant à toujours gérer le cas s'il veut une valeur. C'est souvent une bonne chose, mais parfois désagréable. Je crois qu'il n'y a pas une solution qui soit parfaite dans tous les cas, et qu'il faut accepter que les gens fassent des choix différents.
    Loin de moi l'idée de remplacer les exceptions par des options, et tu as raison, dans l'ensemble. Par contre, dans le cas de Map, le find est presque un bug. Un cas concret est un compilateur. Que fait-il lorsqu'il rencontre un identificateur ? Une fois sur deux, il n'est pas encore inscrit dans la table des symboles.

    En ce qui concerne les exception handler, certes, le plus difficile et le plus coûteux n'est pas la gestion de la pile, mais bien le fait de sauvegarder l'environnement d'exécution.

    Pour ce qui est des performances, je confirme que la gestion des exceptions est beaucoup plus lourde. Je ne peux pas te donner de benchmark, car ne programmant plus du tout depuis quelque temps, mais un exemple simple serait un Map avec environ 10 000 000 d'éléments et un find-exception cherchant environ 10 000 000 de fois un même élément ne se trouvant pas dans le Map, et un find-option, programmé à partir du Map de la librairie standard, effectuant la même tâche avec le même élément. La différence devrait être assez nette.

  4. #24
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    309
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 309
    Points : 933
    Points
    933
    Par défaut
    Peut être qu'au lieu de dire "nan mais attends, ma méthode, c'est trop grave la mieux", on pourrait se demander ce qu'on compare ? Et peut être même qu'on pourrait écrire des mico-bench qui veulent rien dire mais qui donnent quand même une idée de ce qu'on fait ? Genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    exception Fail
     
    let calcul_succ_tr_opt i =
      let rec aux n = 
        if n = 0 then Some 0 else aux (n - 1)
      in
      match aux i with
      | Some m -> m
      | None -> 0
     
    let calcul_succ_tr_exc i =
      let rec aux n = 
        if n = 0 then 0 else aux (n - 1)
      in
      try
        aux i
      with 
      | Fail -> 0
     
    let calcul_succ_not_opt i =
      let rec aux n =
        if n = 0 then
          Some 0
        else
          match aux (n - 1) with
          | Some m -> Some (m + 1)
          | None -> None
      in
      match aux i with
      | Some m -> m
      | None -> 0
     
    let calcul_succ_not_exc i =
      let rec aux n =
        if n = 0 then
          0
        else
          (aux (n - 1)) + 1
      in
      try 
        aux i
      with
      |Fail -> 0
     
    let calcul_fail_tr_opt i =
      let rec aux n =
        if n = 0 then 
          None
        else aux (n - 1)
      in
      match aux i with
      | Some m -> m
      | None -> 0
     
    let calcul_fail_tr_exc i =
      let rec aux n = 
        if n = 0 then raise Fail else aux (n - 1)
      in
      try
        aux i
      with 
      | Fail -> 0
     
    let calcul_fail_not_opt i =
      let rec aux n =
        if n = 0 then
          None
        else
          match aux (n - 1) with
          | Some m -> Some (m + 1)
          | None -> None
      in
      match aux i with
      | Some m -> m
      | None -> 0
     
    let calcul_fail_not_exc i =
      let rec aux n =
        if n = 0 then
          raise Fail
        else
          (aux (n - 1)) + 1
      in
      try 
        aux i
      with
      |Fail -> 0
     
    let rep f l =
      for i = 0 to 10000000 do
        f l
      done
     
    let _ =
      let n = int_of_string Sys.argv.(1) in
      match Sys.argv.(2) with
      | "sto" ->
          rep calcul_succ_tr_opt n
      | "ste" ->
          rep calcul_succ_tr_exc n
      | "sno" ->
          rep calcul_succ_not_opt n
      | "sne" ->
          rep calcul_succ_not_exc n
      | "fto" ->
          rep calcul_fail_tr_opt n
      | "fte" ->
          rep calcul_fail_tr_exc n
      | "fno" ->
          rep calcul_fail_not_opt n
      | "fne" ->
          rep calcul_fail_not_exc n
      | _ -> assert false
    Et là qu'est ce qu'on constate ?

    Que sur un code tailrec (donc qui n'ouvre pas le type option), le type option est toujours meilleur (qu'il échoue ou non, et quelque soit le niveau d'imbrication)

    Que sur un code non tailrec, à faible niveau d'imbrication, c'est le type option qui gagne, mais qu'à grand nombre d'imbrication, c'est l'exception qui gagne (avec ou sans echec)

    Oui, la pose d'un gestionnaire d'exception à un coût. Oui l'ouverture d'un type option à un coût. Oui parsemer son code de "match" ou de "bind_option", ça le rend moins clair. Oui sauter à un point distant du code par un lancement d'exception, ça le rend moins clair.

    Donc : si c'est la recherche de performance qui est l'objectif, la bonne solution est : ça dépend.

    Si c'est la clareté du code qui est l'objectif, la bonne solution est : ça dépend.


    En espérant avoir fait progresser le débat :-D

  5. #25
    Membre éprouvé
    Avatar de Cacophrene
    Homme Profil pro
    Biologiste
    Inscrit en
    Janvier 2009
    Messages
    535
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Biologiste

    Informations forums :
    Inscription : Janvier 2009
    Messages : 535
    Points : 1 125
    Points
    1 125
    Par défaut
    Bonjour,

    Ce qui compte, avant tout, c'est que le code fasse tout ce qu'on lui demande et rien d'autre. À ce titre, l'intérêt d'un code simple et clair semble évident : c'est un code qui se révèle facile à corriger et à améliorer. Je ne vois alors aucune raison de préférer le type option ou les exceptions car les deux techniques sont claires et intelligibles pour peu qu'elles soient utilisées à bon escient. Ne pas oublier non plus qu'il est possible d'apporter des informations complémentaires à l'aide des commentaires lorsque tel ou tel point du programme peut être obscur à la seule lecture du code. J'aime bien cette phrase pleine d'humour lue sur un forum anglophone, et qui résume bien la situation : I'm not interested in knowing how fast is your program at finding the wrong solution.

    Dans un second temps, lorsque le programme répond à nos attentes (qualitativement), on peut se placer sur un plan quantitatif et chercher à accroître ses performances. Dans ce cas, il faut tester son code sur des données qui ressemblent à ce que le programme manipulera en routine, pour ne pas optimiser à tort et à travers. Les micro-bench sont alors intéressants pour les optimisations locales (avec prudence malgré tout), mais une vision globale des performances requiert un test grandeur nature, plus long, plus difficile à mettre en œuvre, mais aussi plus réaliste.

    Le reste est affaire de style et/ou de culture (par ex. un code monadisant ne se retrouve pas chez tout le monde) et nous pourrions en débattre pendant des jours sans parvenir à un consensus.

    Cordialement,
    Cacophrène

  6. #26
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 147
    Points : 102
    Points
    102
    Par défaut
    Merci tout le monde.

    Je ne pensais pas que mon sujet allait autant être sujet à débat mais je vous remercie pour vos remarques qui en intéresseront certainement d'autres que moi.

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Bonnes pratiques de protections individuelles
    Par Community Management dans le forum Sécurité
    Réponses: 23
    Dernier message: 11/06/2024, 11h23
  2. Réponses: 7
    Dernier message: 02/11/2005, 15h30
  3. [Bonne pratique]Stratégie d'allocation
    Par jowo dans le forum C
    Réponses: 1
    Dernier message: 05/10/2005, 14h47
  4. [FOREIGN K] Valeur de champ = nom de table. Bonne pratique ?
    Par Seb des Monts dans le forum Langage SQL
    Réponses: 9
    Dernier message: 17/05/2005, 10h56

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo