Performance Seq.map比常规for循环快吗?
我正在学习F#,关于这门语言,有一件事让我心神不宁,那就是性能。我编写了一个小的基准测试,将惯用的F#与用同一种语言编写的命令式代码进行比较——令我惊讶的是,函数式版本的速度要快得多 基准包括:Performance Seq.map比常规for循环快吗?,performance,f#,Performance,F#,我正在学习F#,关于这门语言,有一件事让我心神不宁,那就是性能。我编写了一个小的基准测试,将惯用的F#与用同一种语言编写的命令式代码进行比较——令我惊讶的是,函数式版本的速度要快得多 基准包括: 使用file.ReadAllLines读取文本文件 颠倒每行中字符的顺序 使用file.writeAllines将结果写回同一文件 代码如下: open System open System.IO open System.Diagnostics let reverseString(str:string
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
开放系统
开放系统
开放系统诊断
让反向字符串(str:string)=
新字符串(Array.rev(str.ToCharArray()))
让CSharpStyle()
让lines=File.ReadAllLines(“text.txt”)
因为我在0。。行。长度-1 do
行。[i]序列图反向限制
|>(有趣的行->File.writeAllines(“text.txt”,行))
让基准函数消息=
//第一次热身
func()
设sw=Stopwatch.StartNew()
因为我在0。。19做
func()
打印FN消息sw.ElapsedMilliseconds
[]
让主参数=
基准CSharpStyle“C#时间:%d毫秒”
基准FSharpStyle“飞行时间:%d毫秒”
0
无论文件大小如何,“F#style”版本的完成时间约为“C#style”版本的75%。我的问题是,为什么会这样?我认为命令式版本没有明显的低效。与常规循环相比,Seq.map表单有几个优点。它可以只预计算一次函数引用;它可以避免变量赋值;它可以使用输入序列长度来预先确定结果数组的大小
Seq.map
与Array.map
不同。由于序列(IEnumerable
)在被枚举之前不会被计算,因此在F#风格的代码中,只有在File.writeAllines
循环通过Seq.map
生成的序列(而非数组)时,才会真正进行计算
换句话说,您的C#风格版本正在反转所有字符串并将反转后的字符串存储在数组中,然后在数组中循环以写入文件。F#风格的版本是反转所有字符串,并或多或少地将它们直接写入文件。这意味着C风格的代码在整个文件中循环三次(读取到数组、构建反向数组、写入数组到文件),而F风格的代码只在整个文件中循环两次(读取到数组、写入反向行到文件)
如果使用
File.ReadLines
而不是File.ReadAllLines
与Seq.map
组合使用,您将获得最好的性能-但是您的输出文件必须与输入文件不同,因为您在写入输出的同时仍在读取输入。这看起来非常有效,但是我很难理解你的意思。你能详细说明一下每一点吗?谢谢。啊,我现在看到了——C版本调用File.writeAllines(string,string[]),而F版本调用File.writeAllines(string,IEnumerable)。因此,实际上只有2个循环,而不是3个。我没有想到该方法还有其他重载。谢谢你的解释!感谢Dr_Asik准备充分的问题。