我需要帮助优化这段代码,需要12秒钟,我需要大约4秒钟。
let publication idx (lst: string [] list) = // returns a specific value of the string [] list
lst
|>Array.map (fun arr -> arr.[idx])
let rec Tuple (x:int) (ID:string list) (Information:string [] list) =
if x < ID.Length then
let muro = [|(ID.[x], Information|> List.filter (fun elem -> elem.[1] = humanID.[x] )|> publication 0 |> List.toArray )|]
let rest = Tuple (x+1) ID Information
Array.append muro rest
else [||]
let FinalTuple= Tuple 0 ID Information
finalTuple是:(string*string []) []
递归需要很长时间才能完成,我似乎无法让它更快(ID.Length是1600)
感谢您的帮助
答案 0 :(得分:3)
在尝试提高一段代码的性能时,首先要做的是消除不必要的内存分配。一些地方你可以改善这个:
在数组上进行映射比在列表上进行映射要快,但不是 比转换为数组,映射然后转换回来更快 列表。
避免使用Array.append
。这导致每次都进行新的内存分配。相反,您可以考虑ResizeArray
或列表,其中附加费用要便宜得多。
尝试使函数尾递归。在您的情况下,这意味着您需要将中间结果传递到下一级递归。在你的情况下(如果我理解正确),这意味着将muro
传递给调用链,然后将该调用的结果添加到muro
,然后再将其传递给下一个递归。
尾递归函数看起来像这样:
let rec Tuple (x:int) (ID:string list) (Information:string [] list) (result:*correct type here*) =
if x < ID.Length then
let muro = [|(ID.[x], Information|> List.filter (fun elem -> elem.[1] = humanID.[x] )|> publication 0 |> List.toArray )|]
Tuple (x+1) ID Information Array.append muro rest
else result
let FinalTuple= Tuple 0 ID Information [||]
这可能会改变结果数组的顺序,因此根据您的需要,您可能需要稍微修改一下。
答案 1 :(得分:3)
我同意@ mydogisbox的一般观点 - 从阵列到列表的来回往返不会对性能有很大帮助。
话虽这么说,我遇到的第一个问题是,我不得不挖掘一下才弄清楚代码在做什么 - 所以我冒昧地做了第一次重写,只是为了看看我是否理解发生了什么:< / p>
let extract1 (ids:string[]) (info:string[][]) =
[|
for id in ids ->
(id, [| for record in info do if record.[1] = id then yield record.[0] |])
|]
我感觉,阅读你的代码是这样的:给定一个“记录”数组 - 包含字段0中的感兴趣的东西(可能是出版物?)和字段1中的作者的数组,目标是给定的一组作者ID,提取他们的出版物。或类似的东西。
现在这不是很漂亮。此外,我不知道这是否有用,所以让我们做一个基准测试 - 一个1,000,000条记录的数据集,看起来像我认为的那样:
let ids = [| 1 .. 100 |] |> Array.map string
let rng = System.Random()
let dataset =
[| for i in 0 .. 1000000 ->
[| System.Guid.NewGuid() |> string; rng.Next(0,100) |> string |] |]
在FSI中运行它给我:
> extract1 ids dataset |> ignore;;
Real: 00:00:01.486, CPU: 00:00:01.484, GC gen0: 3, gen1: 3, gen2: 0
val it : unit = ()
我们可以让它更漂亮,或更实用吗?我们试试吧:
let extract2 (ids:string[]) (info:string[][]) =
ids
|> Seq.map (fun id ->
id,
info
|> Seq.filter (fun record -> record.[1] = id)
|> Seq.map (fun record -> record.[0])
|> Seq.toArray)
|> Seq.toArray
判决结果?
> extract2 ids dataset |> ignore;;
Real: 00:00:01.588, CPU: 00:00:01.593, GC gen0: 3, gen1: 3, gen2: 0
val it : unit = ()
更漂亮,但不是更好。也许问题是我们正在进行多次传递,每个传递一次。听起来好像我们应该分组?
let extract3 (ids:string[]) (info:string[][]) =
let IDs = ids |> Set.ofArray
info
|> Seq.groupBy (fun row -> row.[1])
|> Seq.filter (fun (id,rows) -> IDs |> Set.contains id)
|> Seq.map (fun (id,rows) -> id, rows |> Seq.toArray)
|> Seq.toArray
> extract3 ids dataset |> ignore;;
Real: 00:00:00.387, CPU: 00:00:00.390, GC gen0: 8, gen1: 8, gen2: 0
val it : unit = ()
现在我们正在谈论。我相信我们可以挤得更多 - 随意去做吧。但重点是代码也更简单(IMO),并且更清楚地表达了意图。希望这有帮助!