使用单一案例区分联合类型是否会影响绩效?

Tri*_*Gao 10 f# variant

为每个原始值都有一个包装器很好,所以没有办法滥用它.我怀疑这种便利是有代价的.性能下降了吗?如果性能受到关注,我是否应该使用裸原始值?

Jac*_* P. 13

是的,当使用单例联合类型来包装原始值时,性能会下降.联合案例被编译成类,因此您将支付分配(以及稍后收集)类的价格,并且每次获取union案例中保存的值时,您还将有一个额外的间接.

根据您的应用程序的具体情况,以及您将产生这些额外开销的频率,如果它使您的代码更安全和更模块化,它仍然值得做.

我在F#中编写了很多对性能敏感的代码,我个人倾向于尽可能使用F#度量单位类型来"标记"原始类型(例如,int).这可以防止它们被滥用(感谢F#编译器的类型检查器),但也避免了任何额外的运行时开销,因为在编译代码时会删除度量类型.如果你想要一些这方面的例子,我在fsharp-tools项目中广泛使用了这种设计模式.

  • @bonomo你可以,但它有点hacky.您还应该记住,由于度量单位类型已被删除,因此它们不会在运行时强制执行; 如果你需要它,你就会陷入创建联合案例或其他类型来包装你的值.有关详细信息,请参阅此讨论:[F#中字符串的编译时约束,类似于度量单位 - 是否可能?](http://stackoverflow.com/questions/9417150/compile-time-constraints-for-strings -in-F-相似到单元-的 - 测量 - 是 - 它) (2认同)
  • F# 4.1 现在具有 struct 单例区分联合,使用 [<Struct>] 注释,请参阅 RFC FS-1014:https://github.com/fsharp/fslang-design/blob/master/FSharp-4.1/FS-1014 -struct-discriminated-unions.md -- 性能大幅提升 (2认同)

Tom*_*cek 6

Jack在编写高性能F#代码方面比我更有经验,所以我认为他的答案绝对正确(我也认为使用度量单位的想法非常有趣).

只是为了把事情放在上下文中,我写了一个非常基本的测试(只使用F#Interactive - 所以在Release模式下可能会有所不同)来比较性能.它分配一个包装(与非包装)int值的数组.这可能是非包装类型确实是一个不错的选择,因为数组只是一个连续的内存块.

#time
// Using a simple wrapped `int` type
type Foo = Foo of int
let foos = Array.init 1000000 Foo
// Add all 'foos' 1k times and ignore the results
for i in 0 .. 1000 do 
  let mutable total = 0
  for Foo f in foos do total <- total + f
Run Code Online (Sandbox Code Playgroud)

在我的机器上,for循环平均大约需要1050毫秒.现在,打开的版本:

let bars = Array.init 1000000 id    
for i in 0 .. 1000 do 
  let mutable total = 0
  for b in bars do total <- total + b
Run Code Online (Sandbox Code Playgroud)

在我的机器上,这需要大约700毫秒.

因此,肯定存在一些性能损失,但可能比预期的要小(约33%).这是一个测试,除了打开循环中的值之外,几乎没有任何其他功能.在做有用的代码中,开销会小得多.

如果您正在编写高性能代码,处理大量数据或需要花费一些时间并且用户经常运行它的东西(如编译器和工具),这可能是一个问题.另一方面,如果您的应用程序不是性能关键,那么这不太可能是一个问题.


chi*_*tom 5

从 F# 4.1 开始,将[<Struct>]属性添加到合适的单例区分联合将提高性能并减少执行的内存分配数量。