Running MBrace workflows in the Thread Pool
It is possible to run and test your MBrace workflows using the local thread pool. To do this, you will need to reference MBrace.Runtime library.
1: 2: 3: 4: 5: |
|
The thread pool runtime can be used to test arbitrary cloud code in the confines of your current process:
1: 2: 3: 4: 5: 6: 7: 8: |
|
Emulating distribution
By default, the thread pool runtime uses the expected async semantics, i.e. everything runs in shared memory.
However, when debugging cloud applications it is often useful to emulate conditions particular to distribution.
In the thread pool runtime this can be done by overriding the MemoryEmulation
parameter, which is an enumeration
with three possible values:
- Shared: the default async semantics, values passed to child workflows are shared among worker threads.
-
EnsureSerializable: values passed to child workflows are shared among worker threads, however checks are made at runtime to ensure that closures are serializable.
- Copied: values are passed to child workflows as cloned copies, ensuring proper emulation of distribution semantics.
Let's highlight the differences using a couple of examples:
1:
|
|
Running example1
with the default shared semantics produces no error
1:
|
|
however attempting to do the same with either of the other two options
1: 2: |
|
produces the error:
System.Runtime.Serialization.SerializationException: Cloud process returns non-serializable type 'System.Net.WebClient'.
which is precisely what would happen if we were to run the computation in a distributed MBrace cluster. Consider now the following example
1: 2: 3: 4: 5: 6: |
|
Running the example using Shared
and EnsureSerializable
emulation modes produces the same expected result (100):
1: 2: |
|
However, using Copied
produces a wholly different output (0):
1:
|
|
This is entirely consistent with the behaviour of the workflow in the cloud, since each child work item will be performing the increment operation in a copy of the value.
Testing MBrace code locally before deploying
When working with a distributed MBrace cluster, it is easy to test your code locally before deploying using the companion ThreadPool runtime available to every MBrace client instance:
1:
|
|
When we are certain that everything works fine in the local process, we can confidently deploy as usual
1:
|
|
Gotchas
It should always be kept in mind that thread pool emulation does not sufficiently reproduce all fine semantics variations that occur when executing the same workflow in the cloud. The simplest example are side-effects:
1: 2: 3: 4: 5: |
|
In general, side effects affecting global state behave differently in the local as opposed to the distributed setting:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: |
|
Summary
In this tutorial, you've learned about MBrace.ThreadPool
as ways of executing and testing
cloud workflows using the thread pool of your local client process.
Note, you can use the above techniques from both scripts and compiled projects. To see the components referenced by this script, see ThespianCluster.fsx or AzureCluster.fsx.
from MBrace.Core
Full name: 400-advanced-core-thread-pool.someDistributedCluster
from Microsoft.FSharp.Core.Operators
Full name: Microsoft.FSharp.Core.Operators.Unchecked.defaultof
Full name: 400-advanced-core-thread-pool.tp
private new : resources:ResourceRegistry * _logger:ICloudLogger * _memoryEmulation:MemoryEmulation -> ThreadPoolRuntime
member RunSynchronously : workflow:Cloud<'T> * cancellationToken:CancellationToken * ?memoryEmulation:MemoryEmulation * ?logger:ICloudLogger * ?resources:ResourceRegistry -> 'T
member RunSynchronously : workflow:Cloud<'T> * ?cancellationToken:ICloudCancellationToken * ?memoryEmulation:MemoryEmulation * ?logger:ICloudLogger * ?resources:ResourceRegistry -> 'T
member StartAsTask : workflow:Cloud<'T> * cancellationToken:CancellationToken * ?memoryEmulation:MemoryEmulation * ?logger:ICloudLogger * ?resources:ResourceRegistry -> ThreadPoolProcess<'T>
member StartAsTask : workflow:Cloud<'T> * ?cancellationToken:ICloudCancellationToken * ?memoryEmulation:MemoryEmulation * ?logger:ICloudLogger * ?resources:ResourceRegistry -> ThreadPoolProcess<'T>
member ToAsync : workflow:Cloud<'T> * ?memoryEmulation:MemoryEmulation * ?logger:ICloudLogger * ?resources:ResourceRegistry -> Async<'T>
member MemoryEmulation : MemoryEmulation
member Resources : ResourceRegistry
static member Create : ?logger:ICloudLogger * ?memoryEmulation:MemoryEmulation * ?fileStore:ICloudFileStore * ?serializer:ISerializer * ?textSerializer:ITextSerializer * ?valueProvider:ICloudValueProvider * ?atomProvider:ICloudAtomProvider * ?queueProvider:ICloudQueueProvider * ?dictionaryProvider:ICloudDictionaryProvider * ?resources:ResourceRegistry -> ThreadPoolRuntime
static member CreateCancellationToken : sysToken:CancellationToken -> ThreadPoolCancellationToken
...
Full name: MBrace.ThreadPool.ThreadPoolRuntime
member ThreadPoolRuntime.RunSynchronously : workflow:MBrace.Core.Cloud<'T> * ?cancellationToken:MBrace.Core.ICloudCancellationToken * ?memoryEmulation:MBrace.Core.MemoryEmulation * ?logger:MBrace.Core.Internals.ICloudLogger * ?resources:MBrace.Core.Internals.ResourceRegistry -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
Full name: 400-advanced-core-thread-pool.test
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
Full name: Microsoft.FSharp.Core.Operators.incr
Full name: 400-advanced-core-thread-pool.example1
type WebClient =
inherit Component
new : unit -> WebClient
member BaseAddress : string with get, set
member CachePolicy : RequestCachePolicy with get, set
member CancelAsync : unit -> unit
member Credentials : ICredentials with get, set
member DownloadData : address:string -> byte[] + 1 overload
member DownloadDataAsync : address:Uri -> unit + 1 overload
member DownloadFile : address:string * fileName:string -> unit + 1 overload
member DownloadFileAsync : address:Uri * fileName:string -> unit + 1 overload
member DownloadString : address:string -> string + 1 overload
...
Full name: System.Net.WebClient
--------------------
System.Net.WebClient() : unit
Full name: 400-advanced-core-thread-pool.example2
val ref : value:'T -> 'T ref
Full name: Microsoft.FSharp.Core.Operators.ref
--------------------
type 'T ref = Ref<'T>
Full name: Microsoft.FSharp.Core.ref<_>
Full name: Microsoft.FSharp.Core.Operators.ignore
static member Add : location1:int * value:int -> int + 1 overload
static member CompareExchange : location1:int * value:int * comparand:int -> int + 6 overloads
static member Decrement : location:int -> int + 1 overload
static member Exchange : location1:int * value:int -> int + 6 overloads
static member Increment : location:int -> int + 1 overload
static member Read : location:int64 -> int64
Full name: System.Threading.Interlocked
System.Threading.Interlocked.Increment(location: byref<int>) : int
Full name: 400-advanced-core-thread-pool.hello
type GlobalCounter =
private new : unit -> GlobalCounter
static member Incr : unit -> int
static member Reset : unit -> unit
static member Value : int
Full name: 400-advanced-core-thread-pool.GlobalCounter
--------------------
private new : unit -> GlobalCounter
Full name: 400-advanced-core-thread-pool.GlobalCounter.Value
Full name: 400-advanced-core-thread-pool.GlobalCounter.Incr
Full name: 400-advanced-core-thread-pool.GlobalCounter.Reset
Full name: 400-advanced-core-thread-pool.example3
private new : unit -> GlobalCounter
static member Incr : unit -> int
static member Reset : unit -> unit
static member Value : int
Full name: 400-advanced-core-thread-pool.GlobalCounter