11

Threadring的计算机语言基准游戏的F# 条目包含一个看似无用的行:if false then (). 当我注释掉这一行时,程序运行得更快(对于 50000000 的输入,~2s vs ~55s)并产生相同的结果。这是如何运作的?为什么会有这条线?编译器到底在做什么似乎是无操作的?

编码:

let ringLength = 503

let cells = Array.zeroCreate ringLength
let threads = Array.zeroCreate ringLength
let answer = ref -1

let createWorker i = 
    let next = (i+1)%ringLength
    async { let value = cells.[i]
            if false then () 
            match value with
            | 0 -> answer := i+1
            | _ -> 
                cells.[next] <- value - 1 
                return! threads.[next] }

[<EntryPoint>]
let main args = 
    cells.[0] <- if args.Length>0 then int args.[0] else 50000000
    for i in 0..ringLength-1 do 
        threads.[i]<-createWorker i

    let result = Async.StartImmediate(threads.[0])
    printfn "%d" !answer
    0
4

2 回答 2

9

我最初编写了这段代码。我不记得添加该行的确切原因,但我猜如果没有它,优化器会做一些我认为超出基准游戏精神的事情。首先使用 asyncs 的原因是为了实现对下一个 async 的尾调用延续(这使得它的性能比 C# mono 好得多)。- 乔莫

于 2012-12-04T22:03:09.750 回答
7

如果计算表达式包含if false then (),则异步工作流的转换会有所不同。有了这条线,它使用async.Combine. 稍微简化的代码如下所示:

async.Delay(fun () ->
  value = cells.[i]
  async.Combine
    ( async.Return(if false then ())
      async.Delay(fun () ->
        match value with (...) ) ))

翻译插入Combine是因为if循环完成的(可能)异步计算需要与以下代码结合使用。现在,如果你删除if你会得到类似的东西:

async.Delay(fun () ->
  value = cells.[i]
  match value with (...) ) ))

不同之处在于,现在在传递给的函数中立即完成了更多工作Delay

编辑:我认为这造成了差异,因为代码使用Async.StartImmediate而不是Async.Start,但似乎并非如此。事实上,我完全不明白为什么代码使用异步工作流......

编辑二。:我不完全确定 Mono,但它确实在 F# 交互中复制 - 在那里,版本Combine慢了大约 4 倍(这是我所期望的,因为函数分配开销)。

于 2012-12-04T18:20:12.610 回答