2012-12-04 1 views
11

Das Computer Language Benchmarks Spiel F# entry for Threadring enthält eine scheinbar nutzlose Zeile: if false then(). Wenn ich diese Zeile auskommentiere, läuft das Programm viel schneller (~ 2s vs ~ 55s für eine Eingabe von 50000000) und erzeugt das gleiche Ergebnis. Wie funktioniert das? Warum ist diese Linie da? Was genau macht der Compiler mit dem, was ein No-Op zu sein scheint?Was ist der Effekt von "if false then()" im Eintrag F # Threadring des Computer Language Benchmarks Game?

Der 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 

Antwort

7

Wenn der Berechnungsausdruck enthält if false then() dann die asynchrone Workflow etwas anders übersetzt wird. Mit der Linie verwendet es async.Combine. Etwas vereinfacht Code wie folgt aussieht:

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

Die Übersetzung Combine fügt weil die (potentiell) asynchrone Berechnung durch if Schleife getan werden muss, mit dem folgenden Code kombiniert werden. Nun, wenn Sie if löschen Sie so etwas wie erhalten:

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

Der Unterschied ist, dass jetzt viel mehr Arbeit sofort in der Funktion Delay weitergegeben erfolgt.

EDIT: Ich dachte, das einen Unterschied verursacht, weil der Code Async.StartImmediate verwendet statt Async.Start, aber das scheint nicht der Fall zu sein. Tatsächlich verstehe ich nicht, warum der Code überhaupt asynchrone Workflows verwendet ...

EDIT II.: Ich bin nicht ganz sicher über Mono, aber es repliziert definitiv in der F # interactive - dort ist die Version mit Combine etwa 4 mal langsamer (was ich erwarten würde, wegen der Funktion Allocation Overhead).

9

Ich habe diesen Code ursprünglich geschrieben. Ich kann mich nicht an den genauen Grund erinnern, aus dem ich die Linie hinzugefügt habe, aber ich vermute, dass der Optimierer ohne das etwas tun würde, von dem ich dachte, dass es außerhalb des Geistes des Benchmark-Spiels liegt. Der Grund für die Verwendung von asyncs ist in erster Linie, eine Tail-Call-Fortsetzung zum nächsten async zu erreichen (was diese Leistung so viel besser macht als C# mono). - Jomo

+0

+1 für eine autoritative Antwort (wie es ist), aber ich bin neugierig, warum Sie die Leistung von _a Benchmark_ absichtlich bestrafen wollen ... – ildjarn

+2

Denken Sie daran zurück, ich glaube, ich wollte um Asyncs anzuzeigen, die unterschiedliche legitime Ausführungskontexte sind, während sie die Fortsetzung auf dem gleichen Hardware-Thread teilen (wobei ein ähnliches Verwendungsmuster wie zu Go-Gouutinen gemacht wird). Ohne die Linie würde F # das Ganze in eine While-Schleife auflösen. Dies wäre für einen Benchmark ein guter Tipp, aber sobald Sie versuchen, den Code für etwas Echtes zu verwenden, würde die Leistung um das 20-fache oder so fallen. –

Verwandte Themen