OCaml异常表现

ano*_*nol 12 performance f# ocaml

我经常读到异常有点慢,如果性能是一个问题应该避免(例如,在Java,F#等).这是否适用于常见的OCaml函数,例如Hashtbl.find,返回未找到元素的异常?

特别是,如果我希望我的应用程序有效,我应该总是使用,例如,Hashtable.mem在调用之前测试元素成员资格Hashtbl.find吗?或者mem功能的额外比较是否会对性能产生负面影响?

gas*_*che 20

OCaml异常处理可以非常快速地引发和捕获异常 - 请参阅此SO线程以获取有关其实现方式的内部详细信息.我没有注意精确地对它进行基准测试,但我的随机猜测是它是间接函数调用的大概.

众所周知,与语言的其他部分成比例,OCaml异常明显更快,例如F#的异常 - 这会导致人们将代码从OCaml移植到F#的性能问题.在OCaml中,异常不会导致性能问题.

Hashtbl.mem之前的调用Hashtbl.find可能比捕获异常要慢.惯用的风格往往是try Hashtbl.find .. with Not_found -> ....

也就是说,OCaml社区中有一个明智的运动,使用更明确的错误处理方式,使用option类型而不是异常.理由不是基于性能,而是基于类型检查器然后阻止您忘记处理错误情况的事实.我更喜欢在设计一个全新的API时.使用引发异常的第三方函数时,请确保立即捕获所有可能的异常; 否则就是一种设计气味,应该非常合理.

由于它们的便利性,OCaml异常通常也被用作纯粹的控制流机制(并不表示罕见的故障情况).你会遇到如下代码:

try
  for i = 0 to .... do
    if .. then raise Exit
  done; false
with Exit -> true
Run Code Online (Sandbox Code Playgroud)

最后,我觉得你可能会采取一种糟糕的方法来实现选择.询问关于表演的一般微观问题通常不是要走的路.首先考虑正确性和可读性.性能问题通常应该在稍后进行,并且仅在可测量/可分析的情况下.

  • 我发现异常偶尔会有所帮助并提高代码的可读性(与其他人需要的管道相比,尤其是当monadic样式的库支持尚未完全存在时).我不是在谈论跨函数或跨模块异常引发和捕获的一般情况,而是特定的习惯用法,其中catch站点是静态确定的并且易于推理,例如我在上面演示的示例.(就像`goto`通常是一个坏主意,但在C代码中本地使用时仍然有用,例如,对于某些样式的错误处理.) (2认同)

lam*_*wer 5

首先回答Hashtbl.mem之前的具体问题Hashtbl.find:不要这样做,因为检查表中元素是否存在必须不要两次; 虽然两种策略的成本都是O(1),但Hashtbl.mem首先调用会计算哈希值和查找两次 - 这可能比获取异常需要更长的时间.

作为一般建议,只有在异常概率较低时才创建引发异常的函数.类型系统不考虑异常,因此更强大的实现将具有'a option返回值而不是'a可能的异常.该ocaml的核心库使用的后缀_exn以明确的功能可能会抛出异常,但通常更喜欢非异常抛出的.

所以对于散列表你应该(在一个完美的世界中)有两个函数:

Hashtbl.find_exn : 'a t -> key -> 'a (* throws Not_found *)

Hashtbl.find : 'a t -> key -> 'a option

如有疑问,请使用第二个.如果它不是纯粹的速度,你可以在原始的哈希表周围编写一个简单的包装器:

find_safe h k = try Some (Hashtbl.find h k) with Not_found -> None