首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >CLR与OCaml异常开销

CLR与OCaml异常开销
EN

Stack Overflow用户
提问于 2012-06-09 21:52:32
回答 3查看 874关注 0票数 13

阅读开头的F# -,我专注于以下段落:

来自OCaml背景的程序员在使用F#中的异常时应该小心。由于CLR的体系结构,抛出异常非常昂贵--比在OCaml中要昂贵得多。如果您抛出大量异常,请仔细分析代码,以确定性能成本是否值得。如果成本太高,请适当修改代码。

为什么,由于CLR,如果F#比在OCaml中抛出异常要花费更多?在这种情况下,正确修改代码的最佳方法是什么?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-06-09 22:25:55

Reed已经解释了为什么.NET异常的行为与OCaml异常不同。通常,.NET异常只适用于特殊情况,并且是为此目的而设计的。OCaml具有更轻量级的模型,因此它们也用于实现一些控制流模式。

为了给出一个具体的例子,在OCaml中,您可以使用异常来实现中断循环。例如,假设您有一个函数test,它测试一个数字是否是我们想要的数字。下面对数字进行1到100的迭代,并返回第一个匹配数字:

代码语言:javascript
复制
// Simple exception used to return the result
exception Returned of int

try
  // Iterate over numbers and throw if we find matching number
  for n in 0 .. 100 do
    printfn "Testing: %d" n
    if test n then raise (Returned n)
  -1                 // Return -1 if not found
with Returned r -> r // Return the result here

要无例外地实现这一点,您有两个选项。您可以编写一个具有相同行为的递归函数--如果您调用find 0 (并且它被编译成与在C#中使用return nfor循环中使用基本相同的IL代码):

代码语言:javascript
复制
let rec find n = 
  printfn "Testing: %d" n
  if n > 100 then -1  // Return -1 if not found
  elif test n then n  // Return the first found result 
  else find (n + 1)   // Continue iterating

使用递归函数的编码可能有点长,但也可以使用F#库提供的标准函数。这通常是重写使用OCaml异常进行控制流的代码的最佳方法。在这种情况下,您可以写:

代码语言:javascript
复制
// Find the first value matching the 'test' predicate
let res = seq { 0 .. 100 } |> Seq.tryFind test
// This returns an option type which is 'None' if the value 
// was not found and 'Some(x)' if the value was found.
// You can use pattern matching to return '-1' in the default case:
match res with
| None -> -1
| Some n -> n

如果您不熟悉选项类型,那么请看一些介绍性的材料。F# wikibook有一个很好的教程MSDN文档有一些有用的示例也是。

使用来自Seq模块的适当函数通常会使代码更短,因此它更好。它可能比直接使用递归效率略低,但在大多数情况下,您不需要担心这个问题。

编辑:,我对实际的性能很好奇。如果输入是延迟生成的序列seq { 1 .. 100 }而不是列表[ 1 .. 100 ] (因为列表分配的成本),那么使用seq { 1 .. 100 }的版本会更高效。对于这些更改,以及返回第25个元素的test函数,在我的计算机上运行100000次代码所需的时间是:

代码语言:javascript
复制
exceptions   2.400sec  
recursion    0.013sec  
Seq.tryFind  0.240sec

这是非常简单的示例,因此我认为使用Seq的解决方案一般不会比使用递归编写的等效代码慢10倍。减速可能是由于分配了额外的数据结构(表示序列的对象、闭包、.)另外,由于附加的间接(代码需要大量的虚拟方法调用,而不仅仅是普通的数字操作和跳转)。但是,异常的代价甚至更高,并且不会使代码变得更短或更易读。

票数 7
EN

Stack Overflow用户

发布于 2012-06-09 21:56:24

CLR中的异常非常丰富,并提供了很多细节。Rico在例外情况的费用上发布了一篇(旧的,但仍然相关的)文章,其中详细介绍了其中的一些内容。

因此,在CLR中抛出异常的相对成本比其他一些环境(包括OCaml )要高。

在这种情况下,正确修改代码的最佳方法是什么?

如果您期望在正常的、非异常的情况下引发异常,则可以重新考虑算法和API,以完全避免异常。例如,尝试提供一个可供选择的API,可以在引发异常之前对环境进行测试。

票数 12
EN

Stack Overflow用户

发布于 2012-06-10 18:35:44

为什么,由于CLR,如果F#比在OCaml中抛出异常要花费更多?

OCaml为使用异常作为控制流进行了很大的优化。相反,.NET中的异常根本没有经过优化。

请注意,性能差异是巨大的。OCaml中的异常比F#中的异常快约600×10。根据我的基准测试,即使C++在这方面也比OCaml慢6×10。

尽管据称.NET异常提供了更多的(OCaml提供堆栈跟踪,您还想要什么?)我想不出他们为什么要像现在这样慢慢来。

在这种情况下,正确修改代码的最佳方法是什么?

在F#中,您应该编写“完全”函数。这意味着您的函数应该返回一个联合类型的值,指示结果的类型,例如正常或异常。

特别是,对find的调用应该替换为对tryFind的调用,该调用返回option类型的值,如果集合中没有关键元素,则返回的值是Some值或None

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10964851

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档