Почему преобразование распознаваемого объединения F # в строку через строку или ToString () происходит так медленно? - PullRequest
0 голосов
/ 15 февраля 2019

Есть ли быстрый способ преобразовать различимые союзы в строки?

Я пытался понять, почему требуются часы для сохранения больших коллекций записей в 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


Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

Если вы не слишком требовательны к формату, возможно, сериализация с использованием NewtonSoft.Json будет быстрее.

Или вы можете попробовать добавить каждое значение DU в StringBuilder, а затем вызвать ToString дляStringBuilder для получения полной строки.

0 голосов
/ 15 февраля 2019

Преобразование в строку происходит медленно, поскольку имя регистра DU действительно является частью кода, а не данными для вашей программы.Преобразование его в строку - это, по сути, метод метапрограммирования, который должен выходить за пределы обычного времени выполнения программы, то есть отражения в .NET.

Как правило, хорошо, что имя идентификатора не может повлиятьзапуск вашей программы, потому что это означает, что рефакторинг, такой как переименование идентификаторов, абсолютно безопасен.

Однако, если вы действительно хотите сделать это и сделать это быстро, я думаю, что наиболее прагматичным решением является использование мемоизации:

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.

...