我已经制作了一段大约运行的java代码的“副本”。 400毫秒(https://gist.github.com/threecee/cb1c55ad1ce9ac4b1903)。我在F#中使用Parallel.ForEach
的版本,我也尝试使用PSeq
,但没有一个版本超过7秒。
并不是说我必须让我的代码比我复制的代码更快,但我真的想知道在F#中这样的示例计算中可以做些什么来提高性能。
open System.Threading.Tasks
#time
let calculateProducts n =
let bits = [| for i in 1 .. ((n+1)*(n+1)) -> 0 |]
let inner i =
[|i..n|] |> Array.map (fun j -> bits.[j*i] <- 1) |> ignore
Parallel.ForEach([|1 .. n|], (fun i -> inner i)) |> ignore
bits |> Array.sum
printfn "%i" (calculateProducts 8000)
代码的作用是计算所有独特的产品x*y where x: 1->8000 and y: 1-8000
。
更新
在答案中使用Array.init
作为jpe建议后更新的代码如下:
open System.Threading.Tasks
#time
let calculateProducts n =
let bits = Array.init ((n+1)*(n+1)) (fun _ -> 0)
let inner i =
let arr = Array.init (n-i+1) (fun x -> x+i)
Parallel.ForEach(arr, (fun j -> bits.[j*i] <- 1)) |> ignore
let arr = Array.init n (fun x -> (x+1))
Parallel.ForEach(arr, (fun i -> inner i)) |> ignore
bits |> Array.sum
printfn "%i" (calculateProducts 8000)
答案 0 :(得分:5)
您需要使用内置数组初始化代码,因为您的当前数组初始化在示例中需要很长时间。因此,替换
行let bits = [| for i in 1 .. ((n+1)*(n+1)) -> 0 |]
带行
let bits = Array.init ((n+1)*(n+1)) (fun _ -> 0)
并且您应该获得与Java代码相当的性能。
更新:正如John Palmer建议的那样,Array.zeroCreate
将使数组初始化为零更快。因此,如果您只需要将数组初始化为零而不计算任何初始值,则使用
let bits = Array.zeroCreate ((n+1)*(n+1))
更新:原因如此之快,之前已经解释过here。简短摘要:它使用适当的IL字节码命令newarr
进行初始化,而后者又在.Net的运行时间内进行了优化,速度非常快。 Array.init
比直接前进更快&#34;手册&#34;初始化,因为它调用zeroCreateUnchecked
来初始化数组,然后在已经初始化的数组上运行初始化函数。
如果您想知道代码的位置,那么这里的链接是implementation of Microsoft.FSharp.Collections.Array,而internal implementation in Microsoft.FSharp.Primitives.Basics.Array则会调用{{3}}。
答案 1 :(得分:0)
我认为为什么Java速度快得多的理论答案可以在这里: http://palladin.github.io/StreamsPresentation/#/10
在F#中使用该库应该(或者至少它“承诺”)更快。
其来源可在以下网站获得: https://github.com/nessos/Streams
或者在nuget:https://www.nuget.org/packages/Streams/
希望你比较Java苹果和F#苹果(好吧不是苹果,但是溪流; - )
我认为它不会改变你的代码......