Performance F#码的性能很差

Performance F#码的性能很差,performance,recursion,f#,sequences,Performance,Recursion,F#,Sequences,这是我的第一个F#计划。我想我会把康威的人生游戏作为第一个练习来实施 请帮助我理解为什么下面的代码具有如此糟糕的性能 let GetNeighbours (p : int, w : int, h : int) : seq<int> = let (f1, f2, f3, f4) = (p > w, p % w <> 1, p % w <> 0, p < w * (h - 1)) [ (p - w - 1, f1 &&a

这是我的第一个F#计划。我想我会把康威的人生游戏作为第一个练习来实施

请帮助我理解为什么下面的代码具有如此糟糕的性能

let GetNeighbours (p : int, w : int, h : int) : seq<int> =
    let (f1, f2, f3, f4) = (p > w, p % w <> 1, p % w <> 0, p < w * (h - 1))
    [
    (p - w - 1, f1 && f2);
    (p - w, f1);
    (p - w + 1, f1 && f3);
    (p - 1, f2);
    (p + 1, f3);
    (p + w - 1, f4 && f2);
    (p + w, f4);
    (p + w + 1, f4 && f3)
    ]
    |> List.filter (fun (s, t) -> t)
    |> List.map (fun (s, t) -> s)
    |> Seq.cast

let rec Evolve (B : seq<int>, S : seq<int>, CC : seq<int>, g : int) : unit =
    let w = 10
    let h = 10
    let OutputStr = (sprintf "Generation %d:  %A" g CC) // LINE_MARKER_1
    printfn "%s" OutputStr
    let CCN = CC |> Seq.map (fun s -> (s, GetNeighbours (s, w, h)))
    let Survivors =
        CCN
        |> Seq.map (fun (s, t) -> (s, t |> Seq.map (fun u -> (CC |> Seq.exists (fun v -> u = v)))))
        |> Seq.map (fun (s, t) -> (s, t |> Seq.filter (fun u -> u)))
        |> Seq.map (fun (s, t) -> (s, Seq.length t))
        |> Seq.filter (fun (s, t) -> (S |> Seq.exists (fun u -> t = u)))
        |> Seq.map (fun (s, t) -> s)
    let NewBorns =
        CCN
        |> Seq.map (fun (s, t) -> t)
        |> Seq.concat
        |> Seq.filter (fun s -> not (CC |> Seq.exists (fun t -> t = s)))
        |> Seq.groupBy (fun s -> s)
        |> Seq.map (fun (s, t) -> (s, Seq.length t))
        |> Seq.filter (fun (s, t) -> B |> Seq.exists (fun u -> u = t))
        |> Seq.map (fun (s, t) -> s)
    let NC = Seq.append Survivors NewBorns
    let SWt = new System.Threading.SpinWait ()
    SWt.SpinOnce ()
    if System.Console.KeyAvailable then
        match (System.Console.ReadKey ()).Key with
        | System.ConsoleKey.Q -> ()
        | _ -> Evolve (B, S, NC, (g + 1))
    else 
        Evolve (B, S, NC, (g + 1))

let B = [3]
let S = [2; 3]
let IC = [4; 13; 14]
let g = 0
Evolve (B, S, IC, g)
让getNeights(p:int,w:int,h:int):seq=
设(f1,f2,f3,f4)=(p>w,p%w1,p%w0,pList.filter(乐趣(s,t)->t)
|>List.map(乐趣(s,t)->s)
|>序号
让rec进化(B:seq,S:seq,CC:seq,g:int):单位=
设w=10
设h=10
let OutputStr=(sprintf“第%d代:%A”g CC)//行\u标记\u 1
printfn“%s”OutputStr
设CCN=CC |>Seq.map(fun s->(s,getneights(s,w,h)))
让幸存者=
CCN
|>Seq.map(fun(s,t)->(s,t |>Seq.map(fun u->(CC |>Seq.exists)(fun v->u=v()))
|>Seq.map(fun(s,t)->(s,t |>Seq.filter(fun u->u)))
|>序列图(趣味(s,t)->(s,序列长度t))
|>Seq.filter(fun(s,t)->(s |>Seq.exists(fun u->t=u)))
|>序列图(乐趣(s,t)->s)
让新生儿=
CCN
|>序列图(乐趣(s,t)->t)
|>序号:concat
|>Seq.filter(fun s->not(CC |>Seq.exists(fun t->t=s)))
|>Seq.groupBy(趣味s->s)
|>序列图(趣味(s,t)->(s,序列长度t))
|>Seq.filter(fun(s,t)->B |>Seq.exists(fun u->u=t))
|>序列图(乐趣(s,t)->s)
设NC=序号1.新生儿
设SWt=new System.Threading.SpinWait()
SWt.SpinOnce()
如果System.Console.Key可用,则
将(System.Console.ReadKey()).Key与匹配
|System.ConsoleKey.Q->()
|进化(B,S,NC,(g+1))
其他的
进化(B,S,NC,(g+1))
设B=[3]
设S=[2;3]
设IC=[4;13;14]
设g=0
进化(B、S、IC、g)
前五次迭代(即第0、1、2、3、4代)不会出现问题。然后,在大约100毫秒的短暂停顿后,第5代完成。但在那之后,程序挂起在断点VisualStudio显示的标记为“line_MARKER_1”的行上。它永远不会到达
printfn

奇怪的是,到了第2代,函数
Evolve
中的
CC
序列已经稳定到了序列
[4;13;14;3]
,所以我看不出第6代为什么不能进化

我知道粘贴大量代码段并在调试中寻求帮助通常被认为是不可取的,但我不知道如何将其减少到最低限度。任何有助于我调试的指针都将得到感谢

提前感谢你的帮助

编辑


我真的相信任何想帮助我的人都可能会忽略
getneights
函数。我只是为了完整起见才把它包括进去。

我只是想添加一个简单的答案,以防像我这样的初学者遇到同样的问题

根据上面
Ramon Snir
ildjarn
pad
的建议,我将
Seq.X
调用更改为
List.X
。我不得不添加一个简单的额外施法步骤来解释
List
没有
groupBy
,但这样做之后,代码现在运行起来就像一个魔咒


非常感谢。

请参阅注释和所有内容,但这段代码运行得非常糟糕-既有
列表。*
又有其他一些较小的优化:

let GetNeighbours p w h =
    let (f1, f2, f3, f4) = p > w, p % w <> 1, p % w <> 0, p < w * (h - 1)
    [
        p - w - 1, f1 && f2
        p - w, f1
        p - w + 1, f1 && f3
        p - 1, f2
        p + 1, f3
        p + w - 1, f4 && f2
        p + w, f4
        p + w + 1, f4 && f3
    ]
    |> List.choose (fun (s, t) -> if t then Some s else None)

let rec Evolve B S CC g =
    let w = 10
    let h = 10
    let OutputStr = sprintf "Generation %d:  %A" g CC // LINE_MARKER_1
    printfn "%s" OutputStr
    let CCN = CC |> List.map (fun s -> s, GetNeighbours s w h)
    let Survivors =
        CCN
        |> List.choose (fun (s, t) ->
            let t =
                t
                |> List.filter (fun u -> CC |> List.exists ((=) u))
                |> List.length
            if S |> List.exists ((=) t) then
                Some s
            else None)
    let NewBorns =
        CCN
        |> List.collect snd
        |> List.filter (not << fun s -> CC |> List.exists ((=) s))
        |> Seq.countBy id
        |> List.ofSeq
        |> List.choose (fun (s, t) ->
            if B |> List.exists ((=) t) then
                Some s
            else None)
    let NC = List.append Survivors NewBorns
    let SWt = new System.Threading.SpinWait()
    SWt.SpinOnce()
    if System.Console.KeyAvailable then
        match (System.Console.ReadKey()).Key with
        | System.ConsoleKey.Q -> ()
        | _ -> Evolve B S NC (g + 1)
    else 
        Evolve B S NC (g + 1)

let B = [3]
let S = [2; 3]
let IC = [4; 13; 14]
let g = 0
Evolve B S IC g
让我们来看看=
设(f1,f2,f3,f4)=p>w,p%w1,p%w0,pList.choose(fun(s,t)->如果t,则一些s,否则无)
让rec演化为B S CC g=
设w=10
设h=10
让OutputStr=sprintf“第%d代:%A”g CC//LINE\u标记\u 1
printfn“%s”OutputStr
让CCN=CC |>List.map(fun s->s,gets w h)
让幸存者=
CCN
|>列表。选择(乐趣(s,t)->
让我们=
T
|>List.filter(fun u->CC |>List.exists(=)u))
|>列表长度
如果S |>List.exists(=)t),则
一些
其他(无)
让新生儿=
CCN
|>List.collect snd
|>List.filter(不是CC |>List.exists(=)s))
|>Seq.countBy id
|>表1.1
|>列表。选择(乐趣(s,t)->
如果B |>List.exists(=)t),则
一些
其他(无)
设NC=List.append幸存者新生儿
设SWt=new System.Threading.SpinWait()
SWt.SpinOnce()
如果System.Console.Key可用,则
将(System.Console.ReadKey()).Key与匹配
|System.ConsoleKey.Q->()
|进化B S NC(g+1)
其他的
发展B S NC(g+1)
设B=[3]
设S=[2;3]
设IC=[4;13;14]
设g=0
进化B-S-IC-g

修复性能的最简单方法是使用
Seq.cache

let GetNeighbours (p : int, w : int, h : int) : seq<int> =
    let (f1, f2, f3, f4) = (p > w, p % w <> 1, p % w <> 0, p < w * (h - 1))
    [
    (p - w - 1, f1 && f2);
    (p - w, f1);
    (p - w + 1, f1 && f3);
    (p - 1, f2);
    (p + 1, f3);
    (p + w - 1, f4 && f2);
    (p + w, f4);
    (p + w + 1, f4 && f3)
    ]
    |> List.filter (fun (s, t) -> t)
    |> List.map (fun (s, t) -> s)
    :> seq<_> // <<<<<<<<<<<<<<<<<<<<<<<< MINOR EDIT, avoid boxing

let rec Evolve (B : seq<int>, S : seq<int>, CC : seq<int>, g : int) : unit =
    let w = 10
    let h = 10
    let OutputStr = (sprintf "Generation %d:  %A" g CC) // LINE_MARKER_1
    printfn "%s" OutputStr
    let CCN =
        CC
        |> Seq.map (fun s -> (s, GetNeighbours (s, w, h)))
        |> Seq.cache // <<<<<<<<<<<<<<<<<< EDIT
    let Survivors =
        CCN
        |> Seq.map (fun (s, t) -> (s, t |> Seq.map (fun u -> (CC |> Seq.exists (fun v -> u = v)))))
        |> Seq.map (fun (s, t) -> (s, t |> Seq.filter (fun u -> u)))
        |> Seq.map (fun (s, t) -> (s, Seq.length t))
        |> Seq.filter (fun (s, t) -> (S |> Seq.exists (fun u -> t = u)))
        |> Seq.map (fun (s, t) -> s)
    let NewBorns =
        CCN
        |> Seq.map (fun (s, t) -> t)
        |> Seq.concat
        |> Seq.filter (fun s -> not (CC |> Seq.exists (fun t -> t = s)))
        |> Seq.groupBy (fun s -> s)
        |> Seq.map (fun (s, t) -> (s, Seq.length t))
        |> Seq.filter (fun (s, t) -> B |> Seq.exists (fun u -> u = t))
        |> Seq.map (fun (s, t) -> s)
    let NC =
        Seq.append Survivors NewBorns
        |> Seq.cache // <<<<<<<<<<<<<<<<<< EDIT
    let SWt = new System.Threading.SpinWait ()
    SWt.SpinOnce ()
    if System.Console.KeyAvailable then
        match (System.Console.ReadKey ()).Key with
        | System.ConsoleKey.Q -> ()
        | _ -> Evolve (B, S, NC, (g + 1))
    else
        Evolve (B, S, NC, (g + 1))

let B = [3]
let S = [2; 3]
let IC = [4; 13; 14]
let g = 0
Evolve (B, S, IC, g)
让getNeights(p:int,w:int,h:int):seq=
设(f1,f2,f3,f4)=(p>w,p%w1,p%w0,pList.filter(乐趣(s,t)->t)
|>List.map(乐趣(s,t)->s)

:>seq/ML语言家族最惊人的特征之一是短代码通常是快速代码,这也适用于F

将您的实现与我在博客上发布的更快的实现进行比较:

let count(a:uz[,,
let count (a: _ [,]) x y =
  let m, n = a.GetLength 0, a.GetLength 1
  let mutable c = 0
  for x=x-1 to x+1 do
    for y=y-1 to y+1 do
      if x>=0 && x<m && y>=0 && y<n && a.[x, y] then
        c <- c + 1
  if a.[x, y] then c-1 else c

let rule (a: _ [,]) x y =
  match a.[x, y], count a x y with
  | true, (2 | 3) | false, 3 -> true
  | _ -> false