Почему printf в F # такой медленный? - PullRequest
17 голосов
/ 06 июня 2011

Меня просто удивило, насколько медленный printf из F #.У меня есть несколько программ на C #, которые обрабатывают большие файлы данных и записывают несколько файлов CSV.Сначала я использовал fprintf writer "%s,%d,%f,%f,%f,%s", думая, что это будет просто и достаточно эффективно.

Однако через некоторое время я немного устал от ожидания обработки файлов.(У меня есть XML-файлы 4 ГБ, которые можно просмотреть и записать в них записи.).

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

Я изменил код, чтобы не использовать printf, и теперь производительность намного лучше.Производительность Printf снижала общую производительность моего приложения.

Например, мой оригинальный код:

fprintf sectorWriter "\"%s\",%f,%f,%d,%d,\"%s\",\"%s\",\"%s\",%d,%d,%d,%d,\"%s\",%d,%d,%d,%d,%s,%d"
    sector.Label sector.Longitude sector.Latitude sector.RNCId sector.CellId
    siteName sector.Switch sector.Technology (int sector.Azimuth) sector.PrimaryScramblingCode
    (int sector.FrequencyBand) (int sector.Height) sector.PatternName (int sector.Beamwidth) 
    (int sector.ElectricalTilt) (int sector.MechanicalTilt) (int (sector.ElectricalTilt + sector.MechanicalTilt))
    sector.SectorType (int sector.Radius)

И я изменил его на следующий

seq {
    yield sector.Label; yield string sector.Longitude; yield string sector.Latitude; yield string sector.RNCId; yield string sector.CellId; 
    yield siteName; yield sector.Switch; yield sector.Technology; yield string (int sector.Azimuth); yield string sector.PrimaryScramblingCode;
    yield string (int sector.FrequencyBand); yield string (int sector.Height); yield sector.PatternName; yield string (int sector.Beamwidth); 
    yield string (int sector.ElectricalTilt); yield string (int sector.MechanicalTilt); 
    yield string (int (sector.ElectricalTilt + sector.MechanicalTilt));
    yield sector.SectorType; yield string (int sector.Radius)
}
|> writeCSV sectorWriter

Вспомогательные функции

let writeDelimited delimiter (writer:TextWriter) (values:seq<string>) =
    values
    |> Seq.fold (fun (s:string) v -> if s.Length = 0 then v else s + delimiter + v) ""
    |> writer.WriteLine

let writeCSV (writer:TextWriter) (values:seq<string>) = writeDelimited "," writer values

Я пишу файлы с примерно 30 000 строк.Ничего особенного.

Ответы [ 4 ]

11 голосов
/ 07 июня 2011

Я не уверен, насколько это важно, но ...

Проверка кода для printf:

https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/printf.fs

Я вижу

// The general technique used this file is to interpret
// a format string and use reflection to construct a function value that matches
// the specification of the format string.  

и я думаю, что слово «отражение», вероятно, отвечает на этот вопрос.

printf отлично подходит для написания простого вывода с безопасным типом, но если вам нужен хороший perf во внутреннем цикле, вы можете захотетьиспользовать низкоуровневый .NET API для записи вывода.Я не сделал свой собственный тест, чтобы увидеть.

10 голосов
/ 07 июня 2011

TextWriter уже буферизует свой вывод. Я рекомендую использовать Write для вывода каждого значения по одному вместо форматирования всей строки и передачи ее в WriteLine. На моем ноутбуке написание 100 000 строк занимает почти минуту с использованием вашей функции, а при использовании следующей функции - за полсекунды.

let writeRow (writer:TextWriter) siteName (sector:Sector) = 
  let inline write (value:'a) (delim:char) = 
    writer.Write(value)
    writer.Write(delim)
  let inline quote s = "\"" + s + "\""
  write (quote sector.Label) ','
  write sector.Longitude ','
  write sector.Latitude ','
  write sector.RNCId ','
  write sector.CellId ','
  write (quote siteName) ','
  write (quote sector.Switch) ','
  write (quote sector.Technology) ','
  write (int sector.Azimuth) ','
  write sector.PrimaryScramblingCode ','
  write (int sector.FrequencyBand) ','
  write (int sector.Height) ','
  write (quote sector.PatternName) ','
  write (int sector.Beamwidth) ','
  write (int sector.ElectricalTilt) ','
  write (int sector.MechanicalTilt) ','
  write (int (sector.ElectricalTilt + sector.MechanicalTilt)) ','
  write sector.SectorType ','
  write (int sector.Radius) '\n'
8 голосов
/ 28 июня 2013

Теперь, когда F # 3.1 был выпущен для предварительного просмотра, производительность printf, как утверждается, увеличилась в 40 раз. Возможно, вы захотите взглянуть на это:

Компилятор F # 3.1 / Дополнения к библиотеке

Производительность печати

В базовой библиотеке F # 3.1 улучшена производительность семейства printf функций для типобезопасного форматирования. Например, печать с использованием следующая строка формата теперь работает до 40x быстрее (хотя ваш точный пробег может отличаться):

sprintf "%d: %d, %x %X %d %d %s" Никаких изменений в вашем коде не требуется для воспользоваться этой улучшенной производительностью, хотя вы должны быть используя компонент времени выполнения F # 3.1 FSharp.Core.dll.

6 голосов
/ 08 июня 2011

РЕДАКТИРОВАТЬ: Этот ответ действителен только для строк простого формата, таких как "% s" или "% d". См. Комментарии ниже.

Интересно также отметить, что если вы можете сделать функцию карри и использовать ее повторно, отражение будет выполнено только один раз. Пример:

let w = new System.IO.StringWriter() :> System.IO.TextWriter
let printer = fprintf w "%d"
let printer2 d = fprintf w "%d" d

let print1() = 
   for i = 1 to 100000 do
      printer 2
let print2() = 
   for i = 1 to 100000 do
      printer2 2
let time f = 
   let sw = System.Diagnostics.Stopwatch()
   sw.Start()
   f()
   printfn "%s" (sw.ElapsedMilliseconds.ToString())

time print1
time print2

print1 занимает 48 мс на моем аппарате, а print2 - 1158 мс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...