12
votes

Performance OCAML des exceptions

J'ai souvent lu que les exceptions sont quelque peu lentes et doivent être évitées si la performance est un problème (par exemple, en Java, F #, etc.). Est-ce que cela s'applique aux fonctions OCAML commun telles que hashtbl.find , qui revenait des exceptions pour les éléments non trouvés?

En particulier, si je veux que ma demande soit efficace, dois-je toujours tester l'adhésion à l'élément à l'aide, par exemple, hashtable.mem avant d'appeler hashtbl.find ? Ou la comparaison supplémentaire du MEM fonctionne-t-elle négativement des performances négatives?


0 commentaires

3 Réponses :


5
votes

Pour première réponse à la question concrète pour hashtbl.mem avant hashtbl.find : Ne le faites pas comme le chèque de l'existence de l'élément dans la table devra ne pas faire deux fois; tandis que le coût sera O (1) pour les deux stratégies, appeler hashtbl.mem sera d'abord calculer la valeur de hachage et la recherche deux fois - ce qui peut prendre assez de plus longue que récupérer une exception.

En tant que conseil général, créez uniquement des fonctions qui soulèvent des exceptions si la probabilité d'une exception est faible. Les exceptions ne sont pas prises en compte pour le système de type. Des implémentations plus robustes disposeront d'une option d'option à la place de 'A plus une éventuelle exception. La bibliothèque OCAML CORE utilise un suffixe de _exn pour préciser qu'une fonction peut lancer une exception mais préfère généralement les lanceurs non exceptionnels.

Donc pour la haquetable que vous devriez (dans un monde parfait) avoir deux fonctions:

hashtbl.find_exn: 'Une t -> clé ->' a (* lance no_found *)

hashtbl.find: 'une t -> clé ->' une option

En cas de doute, utilisez le second. Si ce n'est pas pour une vitesse pure, vous pouvez écrire un wrapper simple autour de la table de hachage d'origine:

wind_safe h k = essayer certains (hashtbl.find h k) avec no_found -> Aucun


3 commentaires

Votre réponse est assez judicieuse, mais je suis fortement en désaccord avec le "seulement ... soulever des exceptions si la probabilité d'une exception est faible" idée. La probabilité - chose n'est garantie que par la contre-performance (élever des exceptions est souvent une performance fine sage) ni par des considérations de correction (bogues de probabilité faibles étant encore des bugs). Je ne comprends pas d'où cela vient dans le contexte de OCAML.


Vous devez penser à la localité et au code attractant: s'il s'agit d'une erreur grave, vous voudrez peut-être abandonner votre calcul actuel et votre par exemple. informer l'utilisateur. Ceci est très fastidieux si vous souhaitez vous échapper d'une série de fonctions, car vous devez intégrer la manipulation erronée dans chacun d'entre eux - pas seulement un jeu / avec un bloc autour du tout. Si vous vous attendez normalement à quelque chose d'aller "faux", vous traiterez cette erreur plus localement (par exemple filtrer les résultats). Vous l'avez probablement vu que dans OCAML, car la plupart des langues n'ont pas de type d'option ad hoc et de perdre moins de type de type à des exceptions.


Je ne sais pas comment votre commentaire (raisonnable) est lié à l'argument précédent «faible probabilité». Faites-vous un lien implicite entre "probabilité bas" et "erreur que vous ne pouvez pas gérer localement et devrait plutôt signaler à l'utilisateur?". Je conviens qu'il y a quelques situations où l'abandon et les rapports sont utiles, mais je ne vois pas cela comme lié à une mesure "probabilité d'erreur", plutôt une question de domaine problématique (qui a la responsabilité de gérer cela ?). Le point sur le style monadique ayant une question de modularité est (frustrant mais) bien.



21
votes

Traitement des exceptions OCAML Making Fabriser et attraper des exceptions extrêmement rapides - voir Ce type de thread pour les détails internes sur la manière dont il est mis en œuvre. Je n'ai pas pris soin de la comparer précisément, mais ma supposition aléatoire serait que c'est dans le Ballpark d'un appel de fonction indirect.

On sait que les exceptions OCAML sont nettement plus rapides, proportionnellement au reste de la langue. , que des exceptions de f # par exemple - cela a donné lieu à un problème de performance pour les personnes portant leur code d'OCAML à F #. Dans OCAML, des exceptions ne causent pas de problèmes de performance. P>

appelant hashtbl.mem code> avant hashtbl.find code> est susceptible d'être plus lent que de saisir l'exception. Le style idiomatique a tendance à être try haashtbl.find .. with Not_found -> ... code>. P>

Cela dit, il existe un mouvement sensible dans la communauté OCAML pour utiliser plus explicite Style de traitement des erreurs, à l'aide d'une option code> code> types plutôt que des exceptions. La justification n'est pas basée sur des performances, mais sur le fait que le vérificateur de type vous empêche d'oublier de gérer une situation d'erreur. Je préférerais que lors de la conception d'une nouvelle API fraîche. Lorsque vous utilisez une fonction tierce qui soulevait des exceptions, assurez-vous d'attraper immédiatement toutes les exceptions possibles; Faire autrement, c'est une odeur de conception en général et devrait être très fortement justifiée. P>

En raison de leur commodité, les exceptions OCAML sont également souvent utilisées comme mécanisme de contrôle de contrôle pur (et ne pas signaler une condition de défaillance rare). . Vous rencontrerez un code tel que: P>

try
  for i = 0 to .... do
    if .. then raise Exit
  done; false
with Exit -> true


3 commentaires

En fait, l'une des raisons pour lesquelles la "voie standard OCAML" dans ce cas sembla un peu bizarre pour moi, c'est parce que j'ai lu Java efficace, où Joshua Bloch mentionne explicitement un tel idiome appliqué en Java et qu'il devrait être évité. Je sais que Ocaml n'est pas Java (heureusement!), Mais cela semblait un conseil très générique applicable à la plupart des langages de programmation. Donc, le fait que OCAML serait presque «forcerait» que vous le feriez semblait unsa naturel pour moi. Quoi qu'il en soit, je conviens que l'optimisation vient après la correction. J'ai juste tendance à sur-utiliser des listes d'OCAML en raison de leur simplicité et de leur élégance, ce qui m'a conduit à des problèmes quadratiques.


Les exceptions sont une optimisation du contrôle de flux: c'est un opérateur goto non local déguisé. Premièrement, il est important d'obtenir le droit de contrôle du flux. Les exceptions ne vous aident pas ici, et parfois des dommages (si vous ne savez pas que certaines fonctions vous lancent une exception). Lorsque la performance n'est pas suffisante, nous pouvons essayer de l'optimiser en mettant à quelques exceptions près. De plus, je pense que le conseil de performance Java n'aide pas à OCAML à cause de détails de mise en œuvre très différents.


J'ai trouvé des exceptions à parfois être utiles et améliorez la lisibilité du code (par rapport à la plomberie dont on aurait besoin autrement, en particulier lorsque la prise en charge de la bibliothèque au style monadique n'est pas encore là). Je ne parle pas d'un cas général de travaux croisés ou d'une exception à module transversale élever et attraper, mais des idiomes spécifiques où le site de capture est statiquement déterminé et facile à raisonner, tel que l'exemple que j'ai démontré ci-dessus. (Juste comme goto est généralement une mauvaise idée mais peut toujours être utile lorsqu'il est utilisé localement en code C, par exemple. Pour certains styles de manutention d'erreur.)



1
votes

J'ai souvent lu que les exceptions sont quelque peu lentes et doivent être évitées si la performance est un problème (par exemple, en Java, F #, etc.). Est-ce que cela s'applique aux fonctions OCAML commun telles que HASHTBL.FIND, qui renvoie des exceptions pour les éléments non trouvés?

non. Le folklore autour des exceptions étant lent et que les circonstances exceptionnelles dans les langues traditionnelles sont absurdes. Les exceptions ne sont qu'une autre construction de flux de contrôle. Les exceptions d'OCAML sont rapides et couramment utilisées pour des circonstances non exceptionnelles. Enfin, j'ai regardé (il y a quelques années) Les exceptions étaient d'environ 6 fois plus vite dans Ocaml que c ++, environ 100 fois plus vite que Java et environ 600 fois plus vite que.

Les gens évitent parfois des exceptions dans OCAML et non pour des raisons liées à la performance, mais parce qu'ils veulent un flux de contrôle local explicite, forçant généralement l'appelant à correspondant à un type de réussite / échec au lieu de potentiellement non local. propagation d'exception. Ils le font pour améliorer l'exactitude, ce qui est plus important que la performance.


0 commentaires