为什么通过字符串或ToString()将F#区分联合转换为字符串这么慢?

问题描述 投票:2回答:2

有没有一种快速的方法将歧视的联合转换为字符串?

我试图找出为什么花费数小时将大量记录集合使用各种方法保存到csv文件。我试过CsvProvider.Save,sprintf,字符串构建器等等都很慢。我想我已经将问题追溯到有区别的联合类型转换。

我的例子说明了这个问题。有没有更好的方法,或者我的“手动转换”是最好的选择。

#time
open System

type Field = | Ying | Yang
let manual = function | Ying -> "Ying" | Yang -> "Yang"

// Discriminated Union versions

[for i = 0 to 100000 do yield (Ying).ToString()] |> ignore
//Real: 00:00:12.963, CPU: 00:00:13.281, GC gen0: 10, gen1: 0, gen2: 0

[for i = 0 to 100000 do yield (Ying) |> manual] |> ignore
//Real: 00:00:00.004, CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0

// Others for comparison

[for i = 0 to 100000 do yield (1).ToString()] |> ignore
//Real: 00:00:00.011, CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0
[for i = 0 to 100000 do yield (1.0).ToString()] |> ignore
//Real: 00:00:00.054, CPU: 00:00:00.062, GC gen0: 0, gen1: 0, gen2: 0
[for i = 0 to 100000 do yield (1.0m).ToString()] |> ignore
//Real: 00:00:00.014, CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0


f# f#-data
2个回答
6
投票

转换为字符串很慢,因为DU案例名称实际上是代码的一部分,而不是程序的数据。将其转换为字符串实际上是一种元编程技术,必须达到程序的正常运行时之外,即.NET中的反射。

通常,标识符名称不会影响程序的运行是一件好事,因为这意味着重命名标识符等重构是完全安全的。

但是,如果你真的想这样做并加快速度,我认为最实用的解决方案是使用memoization:

let memoize fn =
    let cache = System.Collections.Concurrent.ConcurrentDictionary<'a, 'b>()
    (fun x -> cache.GetOrAdd(x, fun _ -> fn x))

let showField : Field -> string = memoize string

memoize函数接受一个函数并创建一个函数版本,用于缓存每个输入的输出。 showField函数现在应该与你的manual函数一样快,因为每个DU情况运行一次。


0
投票

如果你对格式不太挑剔,可能会使用NewtonSoft.Json序列化集合会更快。

或者您可以尝试将每个DU值附加到StringBuilder,然后在StringBuilder上调用ToString以获取完整的字符串。

© www.soinside.com 2019 - 2024. All rights reserved.