OCaml异常表现

时间:2012-08-28 13:32:13

标签: performance f# ocaml

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

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

3 个答案:

答案 0 :(得分:20)

OCaml异常处理可以极快地提升和捕获异常 - 有关如何实现的内部详细信息,请参阅this SO thread。我没有注意精确地对它进行基准测试,但我的随机猜测是它是间接函数调用的大概。

众所周知,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

最后,我觉得你可能会采取一种糟糕的方法来实施选择。询问关于表演的一般微观问题通常不是要走的路。首先考虑正确性和可读性。性能问题通常应该在以后才会出现,并且只有在可测量/可评估的情况下才会出现。

答案 1 :(得分:5)

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

作为一般建议,只有在异常概率较低时才创建引发异常的函数。类型系统不考虑异常,因此更强大的实现将具有'a option返回值而不是'a加上可能的异常。 ocaml core 库使用后缀_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

答案 2 :(得分:1)

  

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

没有。关于异常缓慢和主流语言特殊情况的民间传说是无稽之谈。例外只是另一个控制流构造。 OCaml的例外情况很快,通常用于非特殊情况。最后我看(几年前)OCaml中的异常比C ++快6倍,比Java快约100倍,比.NET快600倍。

人们有时会避免OCaml中的异常,而不是出于与性能相关的原因,而是因为他们需要显式的本地控制流,通常强制调用者通过成功/失败联合类型match而不是潜在的非本地异常传播。他们这样做是为了提高正确性,这比绩效更重要。