Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 如何重构代码使其具有功能性?_Performance_F#_Math.net_Mathnet Numerics - Fatal编程技术网

Performance 如何重构代码使其具有功能性?

Performance 如何重构代码使其具有功能性?,performance,f#,math.net,mathnet-numerics,Performance,F#,Math.net,Mathnet Numerics,在玩F#的时候,我试着用一种更实用的方式来思考代码。我的大部分工作碰巧是数字性质的,所以我在想这种再教育是否有意义。以功能性的方式编写数字代码是否就像试图在圆孔中安装方形销钉,还是仅仅是一个陡峭的学习曲线问题,而不考虑应用程序 例如,让我们看一个演示弱大数定律的片段: open System open System.IO open System.Windows.Forms open System.Windows.Forms.DataVisualization open FSharp.Data o

在玩F#的时候,我试着用一种更实用的方式来思考代码。我的大部分工作碰巧是数字性质的,所以我在想这种再教育是否有意义。以功能性的方式编写数字代码是否就像试图在圆孔中安装方形销钉,还是仅仅是一个陡峭的学习曲线问题,而不考虑应用程序

例如,让我们看一个演示弱大数定律的片段:

open System
open System.IO
open System.Windows.Forms
open System.Windows.Forms.DataVisualization
open FSharp.Data
open FSharp.Charting
open FSharp.Core.Operators
open MathNet.Numerics
open MathNet.Numerics.LinearAlgebra
open MathNet.Numerics.Random
open MathNet.Numerics.Distributions
open MathNet.Numerics.Statistics


let T = 1000

let arr1 = Array.init T (fun i -> float i*0.)
for i in 0 .. T-1 do
    arr1.[i] <- [|for j in 1..i do yield Exponential.Sample(0.1)|] |> Statistics.Mean

let arr2 = Array.init T (fun i -> float i*0.)
for i in 0 .. T-1 do
    arr2.[i] <- arr1.[1 .. i] |> Statistics.Mean

arr2 |> Chart.Line |> Chart.Show
开放系统
开放系统
打开System.Windows.Forms
打开System.Windows.Forms.DataVisualization
打开FSharp.Data
打开FSharp.Charting
打开FSharp.Core.Operators
开放MathNet.Numerics
打开MathNet.Numerics.LinearAlgebra
打开MathNet.Numerics.Random
打开MathNet.Numerics.Distributions
打开MathNet.Numerics.Statistics
设T=1000
让arr1=Array.init T(乐趣i->float i*0)
因为我在0。。T-1 do
arr1.[i]统计数据。平均值
让arr2=Array.init T(乐趣i->float i*0)
因为我在0。。T-1 do
arr2.[i]统计数据。平均值
arr2 |>图表.线条|>图表.显示

是否有一种简洁实用的方式来表达上述内容?有多少功能范式可以被纳入到这样的工作中

我认为这是一个很好的问题。我的印象是,编写函数数字代码(想想Matlab vs Mathematica)时遇到的麻烦不是语法问题,而是性能问题。但同时,并行化代码也非常容易

我会这样编写您的代码:

let arr1'=[| for i in 0..1000->Array.init i(fun i->index.Sample(0.1))|>Statistics.Mean |]

请注意,a)没有可变赋值,b)没有索引,c)我没有初始化基于0的数组并填充它,而是使用函数初始化数组

我还将研究是否有可能直接使用index.Sample生成样本,而不是将其称为1000次

编辑

这样:
index.Samples(0.1)|>Seq.take 1000

根据@ChristophRüegg的以下评论:

let expoMean (x:float []) =
    Exponential.Samples(x,0.1)
    x |> Statistics.Mean

Array.init 1000 (fun _ -> Array.replicate 1000 0. |> expoMean)

我还没有做过基准测试

首先,我不会将对
Array.init的调用与初始值的设置分开。您可以使用@s952163在其答案中使用的表格,或基于您的代码:

let arr1 = Array.init T (fun i ->
    [|for j in 1..i do yield Exponential.Sample 0.1 |] |> Statistics.Mean
)
问题是,您正在分配中间数组,这是非常昂贵的,而且在计算平均值之后,您会立即丢弃它们。备选方案:

let arr1 = Array.init T (fun i ->
    Exponential.Samples 0.1 |> Seq.take (i+1) |> Seq.average
)
现在来看第二部分:重复计算元素1..i的平均值,这将成为一个O(n^2)运算。通过使用元素1..i的和是元素1..{i-1}加上第i个元素的和这一事实,可以在O(n)中求解它

let sums, _ =
    arr1
    |> Array.mapFold (fun sumSoFar xi ->
        let s = sumSoFar + xi
        s, s
    ) 0.0
let arr2 = 
    sums
    |> Array.mapi (fun i sumi -> sumi / (float (i + 1)))
当然,你们都可以在一个管道中编写

或者,使用库函数
Array.scan
计算累积和,在本例中,它将给出长度
T+1
的结果,然后从中删除第一个元素:

let arr2 =
    Array.sub (Array.scan (+) 0.0 arr1) 1 T
    |> Array.mapi (fun i sumi -> sumi / (float (i + 1)))
或者避免使用中间阵列:

Seq.scan (+) 0.0 arr1
|> Seq.skip 1
|> Seq.mapi (fun i sumi -> sumi / (float (i + 1)))
|> Seq.toArray

这实际上是两个问题:一个是关于改进给定代码的问题,另一个是关于F#中的函数数字代码的问题。由于其他答案已经集中在特定的代码上,所以我将集中在更一般的问题上

是关于性能的吗? 根据我的经验,函数式编程在数值计算中的适用性取决于性能要求。执行时间越重要,您就越想在功能风格上妥协

如果性能不是问题,则功能代码往往工作得很好。它简洁、安全,比命令式编程更接近于数学写作。当然,有些问题可以很好地映射到命令式程序,但总的来说,函数式风格是一个很好的默认选择

如果性能是一个问题,您可能希望在不变性方面做出妥协。F#中函数代码的主要成本来自垃圾收集器,尤其是具有中间生存期的对象。使昂贵的对象可变并重新使用它们可以极大地提高执行速度。如果你想以简洁、安全的方式编写流体力学、n体模拟或游戏之类的东西,但不想让踏板达到金属的执行速度,多半径F#风格可能是一个不错的选择

如果性能决定一切,那么很可能您还是希望GPU执行。或者可以充分利用CPU向量单元、多线程等。尽管有人试图在GPU上使用F#,但这种语言并不是为了速度而不惜一切代价设计的。在这种情况下,最好使用更接近硬件的语言


当问题是这些问题的混合体时,通常可以混合解决方案。例如,昨天我需要对一组图像进行逐像素计算,执行时间很重要。因此,我使用.NET库读取了F#中的图像,然后将它们与GLSL计算着色器一起上传到GPU,该着色器可以转换像素,然后将它们下载回“F#land”。这里的要点是管理运作效率不高;代码仍然在无缘无故地复制东西。但是这仅仅是一个会消耗所有性能的操作,因此在这一个操作中使用高性能工具是合理的,而所有其他操作都在F#中整齐安全地进行。

最快的方法是创建一个数组,然后将其传递给静态
index.Samples(数组,0.1)
来填充它。是的!我在等一本关于折叠的书:)顺便说一句,这是一本关于科学家的书,虽然有点过时了。这里有这本书的摘录。也许最近的一些书对mathnet有了更好的解释。很高兴有人选择详细回答这部分问题。我还要补充一点,您可以通过不同的途径来提高性能。命令式代码是alw