Why is printf in F# so slow?

前端 未结 4 2009
悲&欢浪女
悲&欢浪女 2020-12-14 02:50

I\'ve just been really surprised by how slow printf from F# is. I have a number of C# programs that process large data files and write out a number of CSV files. I originall

相关标签:
4条回答
  • 2020-12-14 03:33

    I am not sure how much it matters, but...

    Inspecting the code for printf:

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

    I see

    // 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.  
    

    and I think the word 'reflection' probably answers the question.

    printf is great for writing simple type-safe output, but if you want good perf in an inner loop, you might want to use a lower-level .NET API to write output. I haven't done my own benchmarking to see.

    0 讨论(0)
  • 2020-12-14 03:38

    TextWriter already buffers its output. I recommend using Write to output each value, one at a time, instead of formatting an entire line and passing it to WriteLine. On my laptop, writing 100,000 lines takes nearly a minute using your function, while, using the following function, it runs in half a second.

    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'
    
    0 讨论(0)
  • 2020-12-14 03:40

    EDIT: This answer is only valid for simple format strings, like "%s" or "%d". See comments below.

    It is also interesting to note that if you can make a curried function and reuse that, the reflection will only be carried out once. Sample:

    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 takes 48 ms on my machine while print2 takes 1158 ms.

    0 讨论(0)
  • 2020-12-14 03:43

    Now that F# 3.1 has been preview released, the performance of printf is claimed to have increased by 40x. You might want to have a look at this:

    F# 3.1 Compiler/Library Additions

    Printf performance

    The F# 3.1 core library sees improved performance of the printf family of functions for type-safe formatting. For example, printing using the following format string now runs up to 40x faster (though your exact mileage may vary):

    sprintf "%d: %d, %x %X %d %d %s" No changes in your code are needed to take advantage of this improved performance, though you do need to be using the F# 3.1 FSharp.Core.dll runtime component.

    0 讨论(0)
提交回复
热议问题