Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the effect of "if false then ()" in the Computer Language Benchmarks Game's F# Threadring entry?

Tags:

f#

The Computer Language Benchmarks Game's F# entry for Threadring contains a seemingly useless line: if false then (). When I comment out this line, the program runs much faster (~2s vs ~55s for an input of 50000000) and produces the same result. How does this work? Why is this line there? What exactly is the compiler doing with what appears to be a no-op?

The code:

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
like image 748
mjolka Avatar asked Dec 04 '12 17:12

mjolka


2 Answers

I wrote this code originally. I don't remember the exact reason I added the line, but I'm guessing that, without it, the optimizer would do something I thought was outside of the spirit of the benchmark game. The reason for using asyncs in the first place is to achieve tail-call continuation to the next async (which is what makes this perform so much better than C# mono). - Jomo

like image 175
Jomo Fisher Avatar answered Nov 16 '22 03:11

Jomo Fisher


If the computation expression contains if false then () then the asynchronous workflow gets translated a bit differently. With the line, it uses async.Combine. Slightly simplified code looks like:

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

The translation inserts Combine because the (potentially) asynchronous computation done by if loop needs to be combined with the following code. Now, if you delete if you get something like:

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

The difference is that now a lot more work is done immediately in the function passed to Delay.

EDIT: I thought this caused a difference because the code uses Async.StartImmediate instead of Async.Start, but that does not seem to be the case. In fact, I do not understand why the code uses asynchronous workflows at all...

EDIT II.: I'm not entirely sure about Mono, but it definitely does replicate in the F# interactive - there, the version with Combine is about 4 times slower (which is what I'd expect, because of the function allocation overhead).

like image 29
Tomas Petricek Avatar answered Nov 16 '22 03:11

Tomas Petricek