F# 区分联合大小写到字符串的缓慢转换
Slow conversion of F# discriminated union case to string
我有大约 10 万个已区分的联合案例我必须转换为字符串,但它似乎非常慢。
作为比较,以下执行(在 F# 交互中)平均需要 3 秒:
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 个要少)。知道这是 "normal" 还是我做错了什么?
非常感谢
%A
格式说明符可以很好地打印任何 F# 值。它使用反射来做到这一点。它只应真正用于调试目的,而不应用于正常的应用程序代码。
请注意,在您的第一个使用字符串的示例中使用 %s
会使其速度更快,因为在运行时不需要进行类型检查。
对于 DU,有一个 hack 可以用来使反射仅在应用程序加载时发生一次:
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
我在你的例子中有类似的时间,现在这个时间减少到 40 毫秒。
这只适用于所有 DU 案例都没有参数的情况。只要您尝试这样的操作,您就会在应用程序加载时遇到异常:
type FinancingInterest =
| Foo of string
| OtherFinancingInterest
说了这么多,我认为您最好编写一个简单的函数,将您的类型显式转换为字符串值,并在必要时完整地写出名称并重复。受歧视联合案例的名称通常不应被视为影响您的程序的数据。您通常希望能够安全地重命名案例名称而不影响运行时行为。
我有大约 10 万个已区分的联合案例我必须转换为字符串,但它似乎非常慢。
作为比较,以下执行(在 F# 交互中)平均需要 3 秒:
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 个要少)。知道这是 "normal" 还是我做错了什么?
非常感谢
%A
格式说明符可以很好地打印任何 F# 值。它使用反射来做到这一点。它只应真正用于调试目的,而不应用于正常的应用程序代码。
请注意,在您的第一个使用字符串的示例中使用 %s
会使其速度更快,因为在运行时不需要进行类型检查。
对于 DU,有一个 hack 可以用来使反射仅在应用程序加载时发生一次:
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
我在你的例子中有类似的时间,现在这个时间减少到 40 毫秒。
这只适用于所有 DU 案例都没有参数的情况。只要您尝试这样的操作,您就会在应用程序加载时遇到异常:
type FinancingInterest =
| Foo of string
| OtherFinancingInterest
说了这么多,我认为您最好编写一个简单的函数,将您的类型显式转换为字符串值,并在必要时完整地写出名称并重复。受歧视联合案例的名称通常不应被视为影响您的程序的数据。您通常希望能够安全地重命名案例名称而不影响运行时行为。