F#区分联合大小写到字符串的转换缓慢

时间:2018-08-22 16:00:39

标签: f# discriminated-union

我大约有10万个需要区分为联合的联合案例,但我必须将其转换为字符串,但这似乎非常慢。

作为比较,以下代码平均执行3秒(在F#交互中):

open System

let buf = Text.StringBuilder()
let s = DateTime.Now

for i in 1 .. 100000 do
  Printf.bprintf buf "%A" "OtherFinancingInterest" //string
  buf.Length <- 0

printfn "elapsed : %0.2f" (DateTime.Now - s).TotalMilliseconds

以下内容将在一分钟内执行(同样在F#交互中)...

open System

let buf = Text.StringBuilder()
let s = DateTime.Now

for i in 1 .. 100000 do
  Printf.bprintf buf "%A" OtherFinancingInterest //DU
  buf.Length <- 0

printfn "elapsed : %0.2f" (DateTime.Now - s).TotalMilliseconds

已区分的并集具有25个值(结果仍然非常慢,在两种情况下约为16秒,但少于25个)。知道这是“正常”还是我做错了什么?

非常感谢

1 个答案:

答案 0 :(得分:3)

%A格式说明符漂亮地打印了任何F#值。它使用反射来做到这一点。只能将其真正用于调试目的,而不能用于常规应用程序代码中。

请注意,在您的第一个示例中使用%s时,使用字符串使速度大大提高,因为在运行时不需要类型检查。

对于DU,您可以使用一种技巧来使反射仅在应用程序加载时发生一次:

type FinancingInterest =
    | OtherFinancingInterest

open FSharp.Reflection
let private OtherFinancingInterestStringMap =
    FSharpType.GetUnionCases typeof<FinancingInterest>
    |> Array.map (fun c -> FSharpValue.MakeUnion(c, [||]) :?> FinancingInterest)
    |> Array.map (fun x -> x, sprintf "%A" x)
    |> Map.ofArray

type FinancingInterest with
    member this.AsString = OtherFinancingInterestStringMap |> Map.find this

您还可以将其与%s格式说明符一起使用:

Printf.bprintf buf "%s" OtherFinancingInterest.AsString

在您的示例中,我的计时与您的计时相似,现在这一时间下降到40ms。

这仅在所有DU案例没有参数的情况下才有效。尝试执行以下操作时,您将在应用程序加载时遇到异常:

type FinancingInterest =
    | Foo of string
    | OtherFinancingInterest

说了这么多,我认为您最好编写一个简单的函数,该函数将您的类型显式转换为字符串值,并在必要时以重复方式完整写出名称。受歧视的工会案件的名称通常不应被视为影响程序的数据。通常,您通常希望能够安全地重命名案例名称,而完全不影响运行时行为。