阅读开头的F# -,我专注于以下段落:
来自OCaml背景的程序员在使用F#中的异常时应该小心。由于CLR的体系结构,抛出异常非常昂贵--比在OCaml中要昂贵得多。如果您抛出大量异常,请仔细分析代码,以确定性能成本是否值得。如果成本太高,请适当修改代码。
为什么,由于CLR,如果F#比在OCaml中抛出异常要花费更多?在这种情况下,正确修改代码的最佳方法是什么?
发布于 2012-06-09 22:25:55
Reed已经解释了为什么.NET异常的行为与OCaml异常不同。通常,.NET异常只适用于特殊情况,并且是为此目的而设计的。OCaml具有更轻量级的模型,因此它们也用于实现一些控制流模式。
为了给出一个具体的例子,在OCaml中,您可以使用异常来实现中断循环。例如,假设您有一个函数test,它测试一个数字是否是我们想要的数字。下面对数字进行1到100的迭代,并返回第一个匹配数字:
// 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 n在for循环中使用基本相同的IL代码):
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异常进行控制流的代码的最佳方法。在这种情况下,您可以写:
// 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次代码所需的时间是:
exceptions 2.400sec
recursion 0.013sec
Seq.tryFind 0.240sec这是非常简单的示例,因此我认为使用Seq的解决方案一般不会比使用递归编写的等效代码慢10倍。减速可能是由于分配了额外的数据结构(表示序列的对象、闭包、.)另外,由于附加的间接(代码需要大量的虚拟方法调用,而不仅仅是普通的数字操作和跳转)。但是,异常的代价甚至更高,并且不会使代码变得更短或更易读。
发布于 2012-06-09 21:56:24
CLR中的异常非常丰富,并提供了很多细节。Rico在例外情况的费用上发布了一篇(旧的,但仍然相关的)文章,其中详细介绍了其中的一些内容。
因此,在CLR中抛出异常的相对成本比其他一些环境(包括OCaml )要高。
在这种情况下,正确修改代码的最佳方法是什么?
如果您期望在正常的、非异常的情况下引发异常,则可以重新考虑算法和API,以完全避免异常。例如,尝试提供一个可供选择的API,可以在引发异常之前对环境进行测试。
发布于 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。
https://stackoverflow.com/questions/10964851
复制相似问题