Performance F#码的性能很差
这是我的第一个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
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