问题 在计算机语言基准游戏的F#Threadring条目中,“if if then()”有什么影响?


计算机语言基准游戏 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

9003
2017-12-04 17:55


起源



答案:


如果计算表达式包含 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#interactive中复制 - 那里有版本 Combine 大约慢4倍(这是我所期望的,因为功能分配开销)。


7
2017-12-04 18:20





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


9
2017-12-04 22:03



+1是一个权威的答案(例如它),但我很好奇你为什么要惩罚性能 一个基准 故意... - ildjarn
回想一下,我相信我想展示asyncs,它们是独特的合法执行上下文,同时仍然在同一硬件线程上共享延续(对Go goroutines进行类似的使用模式)。如果没有这条线,F#会将整个事物放松到有效的循环中。这对于基准测试来说很花哨,但是一旦你尝试将代码用于真实的东西,性能就会下降20倍左右。 - Jomo Fisher


答案:


如果计算表达式包含 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#interactive中复制 - 那里有版本 Combine 大约慢4倍(这是我所期望的,因为功能分配开销)。


7
2017-12-04 18:20





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


9
2017-12-04 22:03



+1是一个权威的答案(例如它),但我很好奇你为什么要惩罚性能 一个基准 故意... - ildjarn
回想一下,我相信我想展示asyncs,它们是独特的合法执行上下文,同时仍然在同一硬件线程上共享延续(对Go goroutines进行类似的使用模式)。如果没有这条线,F#会将整个事物放松到有效的循环中。这对于基准测试来说很花哨,但是一旦你尝试将代码用于真实的东西,性能就会下降20倍左右。 - Jomo Fisher