1 / 32

Today

F# Asynchronous and Reactive Programming Don Syme, Microsoft Research with help from Nikolaj Bjorner, Laurent le Brun, Luke Hoban, Brian McNamara, James Margetson. Today. A bit about F# Background: F# Monadic Syntax Asynchronous and Parallel Programming using F# workflows.

wardah
Download Presentation

Today

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. F# Asynchronous and Reactive Programming Don Syme, Microsoft Researchwith 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 Visual Studio Debuggers, Profilers .NET Common Language Runtime C# XML Libraries GUI Libraries, etc. Visual Basic F# ML Graphics Libraries System.I/O System.Net etc. Database Connection Libraries

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

  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 { ... } option { ... } seq<'a> = IEnumerable<'a> Async<'a> ≈ ('a -> unit) -> unit Optional<'a> = unit -> 'a option server { ... } client { ... } dist { ... } Server<'a> = unit -> 'a Client<'a> = unit -> 'a Distribution<'a>

  7. F# “Workflow” Syntax async { let! image = readAsync "cat.jpg" let image2 = f image do! writeAsync image2 "dog.jpg" do printfn "done!" return image2 } Async<'a> { for x in 0 .. 10 -> (x,x*x) } [ for x in 0 .. 10 -> (x,x*x) ] seq<'a> = IEnumerable<'a> [| for x in 0 .. 10 -> (x,x*x) |] <@ { 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) } @> seq<'a> + meta-program member x.OnClick(EventArgs) = client { let! x = ... do! ... } member x.OnPageLoad() = server { ... } member OnClick : EventArgs -> Client<'a> member OnPageLoad : unit -> Server<'a>

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

  9. Full Workflow Syntax & Translation expr = ...      | expr { cexpr }                  -- let v = expr in v.Delay(fun () -> «cexpr»)      | { cexpr }                               | [| cexpr |]                             | [  cexpr  ]                        cexpr = let! pat = exprincexpr-- v.Bind(expr,(fun pat -> «cexpr»))       | let pat = exprincexpr-- v.Let(expr,(fun pat -> «cexpr»))       | use pat = exprincexpr-- v.Using(expr,(fun pat -> «cexpr»))       | use! pat = cexprincexpr-- v.BindUsing(expr,(fun pat -> «cexpr»))       | do! cexpr1 in cexpr2            -- «let! () = cexpr1 in cexpr2»       | doexprincexpr -- «let () = cexpr1 in cexpr2»       | for pat inexprdocexpr -- v.For(expr,(fun pat -> «cexpr»))       | whileexprdocexpr-- v.While((fun () -> expr), v.Delay(fun () -> «cexpr»))       | ifexprthencexprelsecexpr-- if expr then «cexpr1» else «cexpr2»       | ifexprthencexpr-- 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)       | matchexprwith [pat ->cexpr]+ -- ...       | trycexprfinallycexpr -- ...       | trycexprwith pat ->cexpr -- ...

  10. type MBuilder() with member For: #seq<'a> * ('a -> M<unit>) -> M<unit> member Zero : unit -> M<unit> member Sequence : M<unit> * M<'a> -> M<'a> member While : (unit -> bool) * M<unit> -> M<unit> member Return : 'a -> M<'a> member Delay : (unit -> M<'a>) -> M<'a> member Using: 'a * ('a -> M<'b>) -> M<'b> when 'a :> IDisposable member Let: 'a * ('a -> M<'b>) -> M<'b> member Bind: M<'a> * ('a -> M<'b>) -> M<'b> member TryFinally : M<'a> * (unit -> unit) -> M<'a> member TryWith : M<'a> * (exn -> M<'a>) -> M<'a> 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 // randomWalk : seq<float> letrandomWalk = { let state = ref 0.0 whiletruedo yield state.Value state := state.Value + rand() } // allLines : seq<string> letallLines(file) = { let inputStream = System.IO.File.OpenRead(file) while not inputStream.IsEndOfFiledo yield inputStream.ReadLine() } // dequeueAll : Queue<_> -> seq<string> letdequeueAll(queue:Queue<_>) = [ whilequeue.Count <> 0do yield queue.Dequeue() ]

  12. F# Meta-programming SQL <@ { for c in db.Customersdo for e db.Employeesdo if c.Name = e.Name then yield (c.Name,c.Address,e.Address) } @> SQL : Expr<seq<'a>> -> seq<'a> 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. 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<T> (“task generators”) • Return, Bind, TryCatch, TryFinally, Raise, Delay, Sequence etc. • Run, Spawn, SpawnFuture, SpawnChild • CancelCheck, WhenCancelled, CancelAsync • Parallel (= ForkJoin) • SwitchToNewThread, SwitchToThreadPool • BuildPrimitive

  19. Taming Asynchronous I/O Target: make it easy to use Begin/End operations Stream.BeginRead : ... Stream.EndRead : IAsyncResult * ...

  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 intnumImages = 200;     public const intnumPixels = 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 intprocessImageRepeats = 20;     // Threads must decrement NumImagesToFinish, and protect     // their access to it through a mutex.     public static intNumImagesToFinish = 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 intimageNum;         public FileStreamfs;     }     public static void ReadInImageCallback(IAsyncResultasyncResult)     { ImageStateObject state = (ImageStateObject)asyncResult.AsyncState;         Stream stream = state.fs; intbytesRead = 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. FileStreamfs = 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; AsyncCallbackreadImageCallback = new AsyncCallback(ReadInImageCallback);         for (inti = 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. FileStreamfs = 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. boolmustBlock = 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{letinStream =File.OpenRead(sprintf"Image%d.tmp"i) let!pixels =inStream.ReadAsync(numPixels) letpixels' =TransformImage(pixels,i) letoutStream=File.OpenWrite(sprintf"Image%d.done"i) do!outStream.WriteAsync(pixels') doConsole.WriteLine"done!" } letProcessImagesAsyncWorkflow() = Async.Run (Async.Parallel [foriin1 .. numImages->ProcessImageAsynci ]) Processing 200 images in parallel

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

  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(IAsyncResultasyncResult)     { ImageStateObject state = (ImageStateObject)asyncResult.AsyncState;         Stream stream = state.fs; intbytesRead = 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. FileStreamfs = 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; AsyncCallbackreadImageCallback = new AsyncCallback(ReadInImageCallback);         for (inti = 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. FileStreamfs = 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. boolmustBlock = 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));     } } Create 10, 000s of “asynchronous tasks” Mostly queued, suspended and executed in the thread pool let ProcessImageAsync () = async{letinStream =File.OpenRead(sprintf"Image%d.tmp"i) let!pixels =inStream.ReadAsync(numPixels) letpixels' =TransformImage(pixels,i) letoutStream=File.OpenWrite(sprintf"Image%d.done"i) do!outStream.WriteAsync(pixels') doConsole.WriteLine"done!" } letProcessImagesAsyncWorkflow() = Async.Run (Async.Parallel [foriin1 .. numImages->ProcessImageAsynci ]) Exceptions can be handled properly Cancellation checks inserted automatically Resources can be disposed properly on failure CPU threads are not blocked

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

  25. Taming Asynchronous I/O letProcessImage(i) = async{useinStream =File.OpenRead(sprintf"source%d.jpg"i) let!pixels =inStream.ReadAsync(1024*1024) letpixels' =TransformImage(pixels,i) useoutStream=File.OpenWrite(sprintf"result%d.jpg"i) do!outStream.WriteAsync(pixels') doConsole.WriteLine"done!" } letProcessImages() = Async.Run (Async.Parallel [foriin1 .. numImages->ProcessImage(i) ])

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

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

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

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

  30. Thanks

  31. Books about F# Visit http://research.microsoft.com/fsharp http://blogs.msdn.com/dsyme

  32. F# Overview

More Related