我对F#的printf有多慢感到惊讶.我有许多C#程序处理大型数据文件并写出许多CSV文件.我最初的fprintf writer "%s,%d,%f,%f,%f,%s"想法是认为这将是简单而合理有效的.
然而,过了一段时间我有点厌倦了等待文件处理.(我有4GB的XML文件可以通过它们写出来.).
当我通过分析器运行我的应用程序时,我惊讶地发现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)
Run Code Online (Sandbox Code Playgroud)
我已将其改为以下内容
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
Run Code Online (Sandbox Code Playgroud)
助手功能
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
Run Code Online (Sandbox Code Playgroud)
我正在写出大约30,000行的文件.没什么特别的.
Bri*_*ian 11
我不确定它有多重要,但......
检查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.
Run Code Online (Sandbox Code Playgroud)
我认为"反思"这个词可能会回答这个问题.
printf非常适合编写简单的类型安全输出,但如果你想在内部循环中获得良好的性能,则可能需要使用较低级别的.NET API来编写输出.我还没有完成自己的基准测试.
Dan*_*iel 10
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'
Run Code Online (Sandbox Code Playgroud)
现在F#3.1已经预览发布,printf声称其性能提高了40倍.你可能想看看这个:
F#3.1编译器/库添加
Printf性能
F#3.1核心库可以提高printf系列函数的性能,以实现类型安全格式化.例如,使用以下格式字符串打印现在可以快40倍(尽管您的确切里程可能会有所不同):
sprintf "%d: %d, %x %X %d %d %s"虽然您确实需要使用F#3.1 FSharp.Core.dll运行时组件,但不需要更改代码来利用这种改进的性能.
编辑:此答案仅对简单格式字符串有效,如"%s"或"%d".见下面的评论.
值得注意的是,如果你可以制作一个curried函数并重用它,那么反射只会执行一次.样品:
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
Run Code Online (Sandbox Code Playgroud)
print1在我的机器上需要48毫秒,而print2需要1158毫秒.