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


4

2 回答 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函数接受一个函数并创建该函数的一个版本,该版本为每个输入缓存输出。在为每个 DU 案例运行一次后,该showField函数现在应该与您的函数一样快。manual

于 2019-02-15T14:35:00.093 回答
0

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

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

于 2019-02-16T15:45:00.487 回答