Presentation is loading. Please wait.

Presentation is loading. Please wait.

F# Asynchronous and Reactive Programming Don Syme, Microsoft Research with help from Nikolaj Bjorner, Laurent le Brun, Luke Hoban, Brian McNamara, James.

Similar presentations


Presentation on theme: "F# Asynchronous and Reactive Programming Don Syme, Microsoft Research with help from Nikolaj Bjorner, Laurent le Brun, Luke Hoban, Brian McNamara, James."— Presentation transcript:

1 F# Asynchronous and Reactive Programming Don Syme, Microsoft Research with help from Nikolaj Bjorner, Laurent le Brun, Luke Hoban, Brian McNamara, James Margetson

2 Today A bit about F# Background: F# Monadic Syntax Asynchronous and Parallel Programming using F# workflows

3 The.NET Context XML Libraries Visual Studio Debuggers, Profilers GUI Libraries, etc. System.I/O System.Net etc. C#.NET Common Language Runtime Visual Basic Database Connection Libraries Graphics Libraries ML F#

4 F# as a Language Core ML Modules-as- values, functors OCaml F# “OCaml-Objects” and other extensions Core ML.NET Nominal Objects Other extensions + tools Similar core language

5 Pattern Matching F# has five primary syntactic categories – Expressions – Declarations – Types – Patterns – Computation Expressions (= Workflows = Monadic Syntax)

6 F# “Workflow” Syntax Monadic syntax added to F# aimed at: List/Sequence/Array comprehensions Database queries Asynchronous/Reactive Programming Probabilistic modelling Modalities (client/server) ≈≈ "monads", but rebranded, partly on advice seq {... }async {... } client {... }dist {... }server {... } option {... } Client = unit -> 'a Optional = unit -> 'a option Server = unit -> 'a seq = IEnumerable Async ≈ ('a -> unit) -> unit Distribution

7 F# “Workflow” Syntax async { let! image = readAsync "cat.jpg" let image2 = f image do! writeAsync image2 "dog.jpg" do printfn "done!" return image2 } { for x in > (x,x*x) } [ for x in > (x,x*x) ] [| for x in > (x,x*x) |] seq = IEnumerable seq + meta-program Async member OnClick : EventArgs -> Client member OnPageLoad : unit -> Server { for c in db.Customers do for e in db.Employees do if c.Name = e.Name then yield (c.Name,c.Address,e.Address) member x.OnClick(EventArgs) = client { let! x =... do!... } member x.OnPageLoad() = server {... }

8 F# “Workflow” Syntax async.Delay(fun () -> async.Bind(readAsync "cat.jpg", (fun image -> async.Bind(async.Return(f image),(fun image2 async.Bind(writeAsync "dog.jpg",(fun () -> async.Bind(async.Return(printfn "done!"),(fun () -> async.Return()))))))))) async { let! image = readAsync "cat.jpg" let image2 = f image do! writeAsync image2 "dog.jpg" do printfn "done!" return image2 } Continuation/ Event callback Asynchronous "non- blocking" action You're actually writing this (approximately):

9 Full Workflow Syntax & Translation expr =... | expr { cexpr } -- let v = expr in v.Delay(fun () -> «cexpr») | { cexpr } | [| cexpr |] | [ cexpr ] cexpr = let! pat = expr in cexpr -- v.Bind(expr,(fun pat -> «cexpr»)) | let pat = expr in cexpr -- v.Let(expr,(fun pat -> «cexpr»)) | use pat = expr in cexpr -- v.Using(expr,(fun pat -> «cexpr»)) | use! pat = cexpr in cexpr -- v.BindUsing(expr,(fun pat -> «cexpr»)) | do! cexpr1 in cexpr2 -- «let! () = cexpr1 in cexpr2» | do expr in cexpr -- «let () = cexpr1 in cexpr2» | for pat in expr do cexpr -- v.For(expr,(fun pat -> «cexpr»)) | while expr do cexpr -- v.While((fun () -> expr), v.Delay(fun () -> «cexpr»)) | if expr then cexpr else cexpr -- if expr then «cexpr1» else «cexpr2» | if expr then cexpr -- if expr then «cexpr1» else v.Zero() | cexpr; cexpr -- v.Combine(«cexpr1», v.Delay(fun () -> «cexpr2»)) | -> expr -- v.Return(expr) | return expr -- v.Return(expr) | yield expr -- v.Yield(expr) | match expr with [pat -> cexpr] | try cexpr finally cexpr | try cexpr with pat -> cexpr --...

10 type MBuilder() with member For: #seq * ('a -> M ) -> M member Zero : unit -> M member Sequence : M * M -> M member While : (unit -> bool) * M -> M member Return : 'a -> M member Delay : (unit -> M ) -> M member Using: 'a * ('a -> M ) -> M when 'a :> IDisposable member Let: 'a * ('a -> M ) -> M member Bind: M * ('a -> M ) -> M member TryFinally : M * (unit -> unit) -> M member TryWith : M * (exn -> M ) -> M let async = AsyncBuilder() let seq = SeqBuilder() let attempt = AttemptBuilder()

11 F# “Workflow” Syntax For "declarative control“, “modalities” and "queries“ Not particularly for tracking effects, though can be used for this Often combined with "separated" imperative programming // dequeueAll : Queue -> seq let dequeueAll(queue:Queue ) = [ while queue.Count <> 0 do yield queue.Dequeue() ] // randomWalk : seq let randomWalk = { let state = ref 0.0 while true do yield state.Value state := state.Value + rand() } // allLines : seq let allLines(file) = { let inputStream = System.IO.File.OpenRead(file) while not inputStream.IsEndOfFile do yield inputStream.ReadLine() }

12 F# Meta-programming SQL { for c in db.Customers do for e db.Employees do if c.Name = e.Name then yield (c.Name,c.Address,e.Address) SQL : Expr > -> seq Runtime intensional meta programming (= Typed Scheme Quotations) (= Expression Trees) SQLServer

13 Using F# Workflow Syntax for Async/Reactive Programming

14 Why is it so hard? To get 50 web pages in parallel? To get from thread to thread? To create a worker thread that reads messages? To handle failure on worker threads?

15 Why isn’t it this easy? let GetWebPages(urls) = Async.Run (Async.Parallel [ for i in urls -> GetWebPage(i) ])

16 © 2007 Microsoft Corporation. All rights reserved. Microsoft Confidential. For Internal Use Only. Why isn’t it this easy? let ProcessImages() = Async.Run (Async.Parallel [ for i in 1.. numImages -> ProcessImage(i) ])

17 Why isn’t it this easy? let task = async {... do! SwitchToNewThread()... do! SwitchToThreadPool()... do! SwitchToGuiThread().... }

18 Async (“task generators”) Return, Bind, TryCatch, TryFinally, Raise, Delay, Sequence etc. Run, Spawn, SpawnFuture, SpawnChild CancelCheck, WhenCancelled, CancelAsync Parallel (= ForkJoin) SwitchToNewThread, SwitchToThreadPool BuildPrimitive

19 © 2007 Microsoft Corporation. All rights reserved. Microsoft Confidential. For Internal Use Only. Taming Asynchronous I/O Stream.BeginRead :... Stream.EndRead : IAsyncResult *... Target: make it easy to use Begin/End operations

20 Stream.ReadAsync = BuildPrimitive(Stream.BeginRead,Stream.EndRead) WebRequest.GetResponseAsync = BuildPrimitive(WebRequest.BeginRead,WebRequest.EndRead) WaitHandle.WaitOneAsync(timeout) = BuildPrimitive(WaitHandle.BeginRead,WaitHandle.EndRead,timeout) Database.GetDataAsync....

21 Taming Asynchronous I/O using System; using System.IO; using System.Threading; public class BulkImageProcAsync { public const String ImageBaseName = "tmpImage-"; public const int numImages = 200; public const int numPixels = 512 * 512; // ProcessImage has a simple O(N) loop, and you can vary the number // of times you repeat that loop to make the application more CPU- // bound or more IO-bound. public static int processImageRepeats = 20; // Threads must decrement NumImagesToFinish, and protect // their access to it through a mutex. public static int NumImagesToFinish = numImages; public static Object[] NumImagesMutex = new Object[0]; // WaitObject is signalled when all image processing is done. public static Object[] WaitObject = new Object[0]; public class ImageStateObject { public byte[] pixels; public int imageNum; public FileStream fs; } public static void ReadInImageCallback(IAsyncResult asyncResult) { ImageStateObject state = (ImageStateObject)asyncResult.AsyncState; Stream stream = state.fs; int bytesRead = stream.EndRead(asyncResult); if (bytesRead != numPixels) throw new Exception(String.Format ("In ReadInImageCallback, got the wrong number of " + "bytes from the image: {0}.", bytesRead)); ProcessImage(state.pixels, state.imageNum); stream.Close(); // Now write out the image. // Using asynchronous I/O here appears not to be best practice. // It ends up swamping the threadpool, because the threadpool // threads are blocked on I/O requests that were just queued to // the threadpool. FileStream fs = new FileStream(ImageBaseName + state.imageNum + ".done", FileMode.Create, FileAccess.Write, FileShare.None, 4096, false); fs.Write(state.pixels, 0, numPixels); fs.Close(); // This application model uses too much memory. // Releasing memory as soon as possible is a good idea, // especially global state. state.pixels = null; fs = null; // Record that an image is finished now. lock (NumImagesMutex) { NumImagesToFinish--; if (NumImagesToFinish == 0) { Monitor.Enter(WaitObject); Monitor.Pulse(WaitObject); Monitor.Exit(WaitObject); } public static void ProcessImagesInBulk() { Console.WriteLine("Processing images... "); long t0 = Environment.TickCount; NumImagesToFinish = numImages; AsyncCallback readImageCallback = new AsyncCallback(ReadInImageCallback); for (int i = 0; i < numImages; i++) { ImageStateObject state = new ImageStateObject(); state.pixels = new byte[numPixels]; state.imageNum = i; // Very large items are read only once, so you can make the // buffer on the FileStream very small to save memory. FileStream fs = new FileStream(ImageBaseName + i + ".tmp", FileMode.Open, FileAccess.Read, FileShare.Read, 1, true); state.fs = fs; fs.BeginRead(state.pixels, 0, numPixels, readImageCallback, state); } // Determine whether all images are done being processed. // If not, block until all are finished. bool mustBlock = false; lock (NumImagesMutex) { if (NumImagesToFinish > 0) mustBlock = true; } if (mustBlock) { Console.WriteLine("All worker threads are queued. " + " Blocking until they complete. numLeft: {0}", NumImagesToFinish); Monitor.Enter(WaitObject); Monitor.Wait(WaitObject); Monitor.Exit(WaitObject); } long t1 = Environment.TickCount; Console.WriteLine("Total time processing images: {0}ms", (t1 - t0)); } let ProcessImageAsync () = async { let inStream = File.OpenRead(sprintf "Image%d.tmp" i) let! pixels = inStream.ReadAsync(numPixels) let pixels' = TransformImage(pixels,i) let outStream = File.OpenWrite(sprintf "Image%d.done" i) do! outStream.WriteAsync(pixels') do Console.WriteLine "done!" } let ProcessImagesAsyncWorkflow() = Async.Run (Async.Parallel [ for i in 1.. numImages -> ProcessImageAsync i ]) Processing 200 images in parallel

22 let ProcessImageAsync(i) = async { let inStream = File.OpenRead(sprintf "source%d.jpg" i) let! pixels = inStream.ReadAsync(numPixels) let pixels' = TransformImage(pixels,i) let outStream = File.OpenWrite(sprintf "result%d.jpg" i) do! outStream.WriteAsync(pixels') do Console.WriteLine "done!" } let ProcessImagesAsync() = Async.Run (Async.Parallel [ for i in 1.. numImages -> ProcessImageAsync(i) ]) Taming Asynchronous I/O Read from the file, asynchronously “!” = “asynchronous” Write the result, asynchronously This object coordinates Equivalent F# code (same perf) Generate the tasks and queue them in parallel Open the file, synchronously

23 Taming Asynchronous I/O using System; using System.IO; using System.Threading; public class BulkImageProcAsync { public const String ImageBaseName = "tmpImage-"; public const int numImages = 200; public const int numPixels = 512 * 512; // ProcessImage has a simple O(N) loop, and you can vary the number // of times you repeat that loop to make the application more CPU- // bound or more IO-bound. public static int processImageRepeats = 20; // Threads must decrement NumImagesToFinish, and protect // their access to it through a mutex. public static int NumImagesToFinish = numImages; public static Object[] NumImagesMutex = new Object[0]; // WaitObject is signalled when all image processing is done. public static Object[] WaitObject = new Object[0]; public class ImageStateObject { public byte[] pixels; public int imageNum; public FileStream fs; } public static void ReadInImageCallback(IAsyncResult asyncResult) { ImageStateObject state = (ImageStateObject)asyncResult.AsyncState; Stream stream = state.fs; int bytesRead = stream.EndRead(asyncResult); if (bytesRead != numPixels) throw new Exception(String.Format ("In ReadInImageCallback, got the wrong number of " + "bytes from the image: {0}.", bytesRead)); ProcessImage(state.pixels, state.imageNum); stream.Close(); // Now write out the image. // Using asynchronous I/O here appears not to be best practice. // It ends up swamping the threadpool, because the threadpool // threads are blocked on I/O requests that were just queued to // the threadpool. FileStream fs = new FileStream(ImageBaseName + state.imageNum + ".done", FileMode.Create, FileAccess.Write, FileShare.None, 4096, false); fs.Write(state.pixels, 0, numPixels); fs.Close(); // This application model uses too much memory. // Releasing memory as soon as possible is a good idea, // especially global state. state.pixels = null; fs = null; // Record that an image is finished now. lock (NumImagesMutex) { NumImagesToFinish--; if (NumImagesToFinish == 0) { Monitor.Enter(WaitObject); Monitor.Pulse(WaitObject); Monitor.Exit(WaitObject); } public static void ProcessImagesInBulk() { Console.WriteLine("Processing images... "); long t0 = Environment.TickCount; NumImagesToFinish = numImages; AsyncCallback readImageCallback = new AsyncCallback(ReadInImageCallback); for (int i = 0; i < numImages; i++) { ImageStateObject state = new ImageStateObject(); state.pixels = new byte[numPixels]; state.imageNum = i; // Very large items are read only once, so you can make the // buffer on the FileStream very small to save memory. FileStream fs = new FileStream(ImageBaseName + i + ".tmp", FileMode.Open, FileAccess.Read, FileShare.Read, 1, true); state.fs = fs; fs.BeginRead(state.pixels, 0, numPixels, readImageCallback, state); } // Determine whether all images are done being processed. // If not, block until all are finished. bool mustBlock = false; lock (NumImagesMutex) { if (NumImagesToFinish > 0) mustBlock = true; } if (mustBlock) { Console.WriteLine("All worker threads are queued. " + " Blocking until they complete. numLeft: {0}", NumImagesToFinish); Monitor.Enter(WaitObject); Monitor.Wait(WaitObject); Monitor.Exit(WaitObject); } long t1 = Environment.TickCount; Console.WriteLine("Total time processing images: {0}ms", (t1 - t0)); } let ProcessImageAsync () = async { let inStream = File.OpenRead(sprintf "Image%d.tmp" i) let! pixels = inStream.ReadAsync(numPixels) let pixels' = TransformImage(pixels,i) let outStream = File.OpenWrite(sprintf "Image%d.done" i) do! outStream.WriteAsync(pixels') do Console.WriteLine "done!" } let ProcessImagesAsyncWorkflow() = Async.Run (Async.Parallel [ for i in 1.. numImages -> ProcessImageAsync i ]) Create 10, 000s of “asynchronous tasks” Mostly queued, suspended and executed in the thread pool Exceptions can be handled properly Cancellation checks inserted automatically Resources can be disposed properly on failure CPU threads are not blocked

24 let ProcessImage(i) = async { use inStream = File.OpenRead(sprintf "source%d.jpg" i) let! pixels = inStream.ReadAsync(1024*1024) let pixels' = TransformImage(pixels,i) use outStream = File.OpenWrite(sprintf "result%d.jpg" i) do! outStream.WriteAsync(pixels') do Console.WriteLine "done!" } let ProcessImages() = Async.Run (Async.Parallel [ for i in 1.. numImages -> ProcessImage(i) ]) Taming Asynchronous I/O

25 let ProcessImage(i) = async { use inStream = File.OpenRead(sprintf "source%d.jpg" i) let! pixels = inStream.ReadAsync(1024*1024) let pixels' = TransformImage(pixels,i) use outStream = File.OpenWrite(sprintf "result%d.jpg" i) do! outStream.WriteAsync(pixels') do Console.WriteLine "done!" } let ProcessImages() = Async.Run (Async.Parallel [ for i in 1.. numImages -> ProcessImage(i) ]) Taming Asynchronous I/O

26 F# - Erlang-style Message Agents open Microsoft.FSharp.Control.Mailboxes let counter = new MailboxProcessor (fun inbox -> let rec loop(n) = async { do printfn "n = %d" n let! msg = inbox.Receive() return! loop(n+msg) } loop(0)) Reactive State Machine

27 Show me the types type Async = P of { ctxt: AsyncGroup; cont: 'a -> unit; econt: exn -> unit; ccont: OperationCanceledException -> unit; } -> unit Async Exception continuation Success continuation Execution request

28 Show me the types type Async a = P of { ctxt: AsyncGroup; cont: a -> IO (); econt: exn -> IO (); ccont: OperationCanceledException -> IO (); } -> IO () Async Exception continuation Success continuation Execution request

29 Microsoft Research F# just released Late Summer: “Community Technology Preview” Thereafter moving to be part of standard Microsoft.NET Tools

30 THANKS

31 Books about F# Visit

32 F# Overview Functional Strong Typing SuccinctType Inference Data Types and Patterns 1 st Class FunctionsMeta-Programming Objects.NET OO Model Interoperable Compact type- inferred classes.NET Visual StudioLibrariesToolsConcurrencyDatabase Tools F# CompilerF# Interactive Visual Studio Integration Math/Plot Bindings Lex and Yacc


Download ppt "F# Asynchronous and Reactive Programming Don Syme, Microsoft Research with help from Nikolaj Bjorner, Laurent le Brun, Luke Hoban, Brian McNamara, James."

Similar presentations


Ads by Google