Is Seq.map faster than a regular loop?

I am learning F #, and one thing that bothers me about this language is performance. I wrote a small guideline where I compare the idiomatic F # with the imperative style code written in the same language, and, to my great surprise, the functional version comes out much faster.

The test consists of:

  • Reading in a text file using File.ReadAllLines
  • Reverse character order on each line
  • Writing the result to the same file using File.WriteAllLines.

Here is the code:

open System open System.IO open System.Diagnostics let reverseString(str:string) = new string(Array.rev(str.ToCharArray())) let CSharpStyle() = let lines = File.ReadAllLines("text.txt") for i in 0 .. lines.Length - 1 do lines.[i] <- reverseString(lines.[i]) File.WriteAllLines("text.txt", lines) let FSharpStyle() = File.ReadAllLines("text.txt") |> Seq.map reverseString |> (fun lines -> File.WriteAllLines("text.txt", lines)) let benchmark func message = // initial call for warm-up func() let sw = Stopwatch.StartNew() for i in 0 .. 19 do func() printfn message sw.ElapsedMilliseconds [<EntryPoint>] let main args = benchmark CSharpStyle "C# time: %d ms" benchmark FSharpStyle "F# time: %d ms" 0 

Regardless of the file size, the version of "F # -style" completes at about 75% of the time of the version of "C # -style". My question is why? I do not see obvious inefficiency in the imperative version.

+8
performance f #
source share
2 answers

Seq.map is different from Array.map . Since sequences ( IEnumerable<T> ) are not evaluated until they are listed, in F # style code, no computation actually happens until File.WriteAllLines passes through the sequence (not an array) generated with using Seq.map .

In other words, your C # -style version reverses all the lines and stores the reverse lines in the array, and then iterates over the array to write to the file. The F # line version reverses all lines and writes more or less of them directly to the file. This means that C # style code cycles through the entire file three times (reading an array, creating an inverse array, writing an array to a file), while F # style code cycles through the entire file only two times (reading into an array, writing return lines to file).

You will get maximum performance if you use File.ReadLines instead of File.ReadAllLines in combination with Seq.map - but your output file should be different from your input file, since you write output when reading from input.

+10
source share

The Seq.map form has several advantages over the regular cycle. It can pre-copy a link to a function only once; it can avoid variable assignments; and he can use the length of the input sequence to specify an array of results.

+1
source share

All Articles