Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to get async workflows in F# to pipeline automatically?

(Note I'm talking about pipelining as in running independent processes in parallel; not related to the |> operator).

So if I've got two functions

let incr x = 
  Thread.Sleep 1000
  x + 1

let product x y = 
  Thread.Sleep 1000
  x * y

would there be an easy way to write a workflow something like (pseudocode)

let productOfIncrements x y = 
  async {
    let! x1 = incr x
    let! y1 = incr y
    let! result = product x1 y1
    return result
  }

that pipelines the first two independent operations and thus executes in two seconds, or are async workflows the wrong approach to this problem? If there is a good approach to the problem, is there a straightforward way to extend such an approach to do, say, recursive factorial calculation in N+1 seconds rather than 2N?

like image 901
Dax Fohl Avatar asked Aug 01 '11 13:08

Dax Fohl


1 Answers

The easiest option is to use Async.StartChild. This primitve starts an asynchronous operation in background (in a thread pool) and returns a "token" that can be used to wait for a completion of the operation. This doesn't block the workflow, so you can then continue running other operations:

let productOfIncrements x y = 
  async {
    // Start the 'incr x' operation in background
    let! x1Op = async {return incr x} |> Async.StartChild
    // Continue doing other work 
    // (synchronously since it takes the same time as the above.)
    let y1 = incr y
    // Now wait for the result of 'incr x' (if it is still running)
    let! x1 = x1Op
    // return the product (just call the function synchronously)
    return product x1 y1
  }

If the two operations return the same type, then you can also use Async.Parallel, which composes multiple operations to run in parallel.

If you're working with purely CPU-bound computations and you need to create a large number of them, then you can also use .NET Tasks directly (see for example this article). Tasks are more efficient, but are not as elegant to use (and don't support asynchronous waiting nicely).

As a side-note, the term pipeline is usually used (at least in F# or .NET world) for a more complicated thing - For example, say you have a series of steps that depend on each other. When processing multiple inputs, you can run the steps in parallel (and still limit the total paralellism). This can be done using F# async workflows too - see for example this article. There is also a framework named pipelets that implements the concept.

like image 72
Tomas Petricek Avatar answered Nov 15 '22 06:11

Tomas Petricek