CLR vs OCaml异常开销 [英] CLR vs OCaml exception overhead

查看:82
本文介绍了CLR vs OCaml异常开销的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

阅读从F#开始-罗伯特·皮克林(Robert Pickering)我专注于以下段落:

Reading Beginning F# - Robert Pickering I focused on the following paragraph:

来自OCaml背景的程序员在使用时应格外小心 在F#中使用异常.由于CLR的架构, 引发异常非常昂贵-相当昂贵 比OCaml中的要好.如果您抛出很多异常,请分析您的代码 仔细决定性能成本是否值得.如果 费用太高,请适当修改代码.

Programmers coming from an OCaml background should be careful when using exceptions in F#. Because of the architecture of the CLR, throwing an exception is pretty expensive—quite a bit more expensive than in OCaml. If you throw a lot of exceptions, profile your code carefully to decide whether the performance costs are worth it. If the costs are too high, revise the code appropriately.

为什么,由于CLR,如果F#引发异常要比OCaml引发异常昂贵?在这种情况下,适当修改代码的最佳方法是什么?

Why, because of CLR, throwing an exception is more expensive if F# than in OCaml? And what is the best way to revise the code appropriately in this case?

推荐答案

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

Reed already explained why .NET exceptions behave differently than OCaml exceptions. In general, .NET exceptions are suitable only for exceptional situations and are designed for that purpose. OCaml has more lightweight model and so they are used to also implement some control flow patterns.

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

To give a concrete example, in OCaml you could use exception to implement breaking of a loop. For example, say you have a function test that tests whether a number is a number we want. The following iterates over numbers from 1 to 100 and returns first matching number:

// 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#中的for循环内使用return n基本上相同的IL代码):

To implement this without exceptions, you have two options. You could write a recursive function that has the same behaviour - if you call find 0 (and it is compiled to essentially the same IL code as using return n inside for loop in C#):

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异常进行控制流的代码的最佳方法.在这种情况下,您可以编写:

The encoding using recursive functions can be a bit lengthly, but you can also use standard functions provided by the F# library. This is often the best way to rewrite code that would use OCaml exceptions for control flow. In this case, you can write:

// 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有很好的教程

If you're not familiar with option types, then take a look at some introductory material. F# wikibook has a good tutorial and MSDN documentation has useful examples too.

使用Seq模块中的适当功能通常会使代码短很多,因此更可取.与直接使用递归相比,它的效率可能略低,但是在大多数情况下,您不必担心.

Using an appropriate function from the Seq module often makes the code a lot shorter, so it is preferrable. It may be slightly less efficient than using recursion directly, but in most of the cases, you don't need to worry about that.

编辑:我对实际效果感到好奇.如果输入是延迟生成的序列seq { 1 .. 100 }而不是列表[ 1 .. 100 ],则使用Seq.tryFind的版本会更有效(由于列表分配的开销).经过这些更改,并且test函数返回第25个元素,在我的计算机上运行代码100000次所需的时间为:

I was curious about the actual performance. The version using Seq.tryFind is more efficient if the input is lazily generated sequence seq { 1 .. 100 } instead of a list [ 1 .. 100 ] (because of the costs of list allocation). With these changes, and test function that returns 25th element, the time needed to run the code 100000 times on my machine is:

exceptions   2.400sec  
recursion    0.013sec  
Seq.tryFind  0.240sec

这是非常琐碎的示例,因此我认为使用Seq的解决方案通常不会比使用递归编写的等效代码慢10倍.速度下降可能是由于分配了其他数据结构(表示序列,闭包等的对象)以及其他间接调用(代码需要大量的虚拟方法调用,而不仅仅是简单的数字运算和跳转).但是,异常的代价更高,并且不会以任何方式使代码更短或更易读...

This is extremely trivial sample, so I think the solution using Seq will not generally run 10 times slower than equivalent code written using recursion. The slowdown is probably due to the allocation of additional data structures (object representing the sequence, closures, ...) and also due to additional indirection (the code needs numerous virtual method calls, instead of just plain numeric operations and jumps). However, exceptions are even more costly and don't make the code shorter or more readable in any way...

这篇关于CLR vs OCaml异常开销的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆