F# Server-side programming

Preview:

DESCRIPTION

An F# presentation that I done a few years ago.

Citation preview

F# Server side programming

Dave ThomasMoiraeSoftware.comtwitter.com/7sharp9

F# Server based programming

Why F#• Concise succinct code

• Advanced asynchronous support

• Language Orientated Programming

• Generic by default

• Immutable by default

Fundamental Concepts

• Pure Functions– No side effects– memoization

• Immutability– No threading issues

• Lambda expressions• Higher order functions• Recursion

Patterns and Practices

C#• Inheritance• Polymorphism

F#• Higher order functions• Type augmentation

Patterns / similarities

• Discriminated unions -> Small class hierarchies

• Higher Order Functions -> Command Pattern

• Fold -> Visitor pattern

A comparison of styles

F#• 1 source file• 43 lines• 2 types

C#• 2 source files• 166 lines• 2 type

75% reduction in code

C# ObjectPool

F# ObjectPool//Agent alias for MailboxProcessortype Agent<'T> = MailboxProcessor<'T>

///One of three messages for our Object Pool agenttype PoolMessage<'a> = | Get of AsyncReplyChannel<'a> | Put of 'a | Clear of AsyncReplyChannel<List<'a>>

/// Object pool representing a reusable pool of objectstype ObjectPool<'a>(generate: unit -> 'a, initialPoolCount) = let initial = List.init initialPoolCount (fun (x) -> generate()) let agent = Agent.Start(fun inbox -> let rec loop(x) = async { let! msg = inbox.Receive() match msg with | Get(reply) -> let res = match x with | a :: b -> reply.Reply(a);b | [] as empty-> reply.Reply(generate());empty return! loop(res) | Put(value)-> return! loop(value :: x) | Clear(reply) -> reply.Reply(x) return! loop(List.empty<'a>) } loop(initial))

/// Clears the object pool, returning all of the data that was in the pool. member this.ToListAndClear() = agent.PostAndAsyncReply(Clear)

/// Puts an item into the pool member this.Put(item ) = agent.Post(item)

/// Gets an item from the pool or if there are none present use the generator member this.Get(item) = agent.PostAndAsyncReply(Get)

F# - Fracture IO

Open source high performance Socket, Pipeline, and agent library

Sockets

2 Models of Operation

• AsyncResult Model

• SocketAsyncEventArgs Model

IAsyncResults ModelPros• Well documented API• Can be simplified with helpers in

Asynchronous workflows in F#• Fast to get an initial result

Cons• Allocation of IAsyncResult on every Operation• Consumes more CPU for each send and receive operation

F# AsyncResult

SocketAsyncEventArgs Model

Pros• Less memory allocations• Better performance• Closer to the metal

Cons• Not a well understood or documented API• Can be complex to get right

Performance Differences

• 5 minute test

• 50 clients connecting to the server

• 15ms interval between each one

• Server sends each client a 128 byte message every 100ms

• Total of 500 messages per second

Test Harness

CPU & Threads

SocketAsyncEventArgs IAsyncResult

MemorySocketAsyncEventArgs IAsyncResult

Garbage collectionSocketAsyncEventArgs IAsyncResult

Fracture Socket Implementation

Agents

• Provide a message passing mechanism

• Agents read and respond to a queue of messages

• Typically a discriminated union is used to describe messages

Introducing Fracture-IO

• High performance Socket library

• Highly compositional pipeline library

Pipelines

• Closed Pipelines– Tomas Petricek• Image Processing Pipeline

• Open Pipelines– Pipelets

Closed Pipelines

Image Processing Pipeline

1: // Phase 1: Load images from disk and put them into a queue 2: let loadImages = async { 3: let clockOffset = Environment.TickCount 4: let rec numbers n = seq { yield n; yield! numbers (n + 1) } 5: for count, img in fileNames |> Seq.zip (numbers 0) do 6: let info = loadImage img sourceDir count clockOffset 7: do! loadedImages.AsyncAdd(info) } 8: 9: // Phase 2: Scale to a thumbnail size and add frame10: let scalePipelinedImages = async {11: while true do 12: let! info = loadedImages.AsyncGet()13: scaleImage info14: do! scaledImages.AsyncAdd(info) }

1: let loadedImages = new BlockingQueueAgent<_>(queueLength)2: let scaledImages = new BlockingQueueAgent<_>(queueLength) 3: let filteredImages = new BlockingQueueAgent<_>(queueLength)

1: // Phase 4: Display images as they become available 2: let displayPipelinedImages = async { 3: while true do 4: let! info = filteredImages.AsyncGet() 5: info.QueueCount1 <- loadedImages.Count 6: info.QueueCount2 <- scaledImages.Count 7: info.QueueCount3 <- filteredImages.Count 8: displayImage info } 9: 10: // Start workflows that implement pipeline phases11: Async.Start(loadImages, cts.Token)12: Async.Start(scalePipelinedImages, cts.Token)13: Async.Start(filterPipelinedImages, cts.Token)14: try Async.RunSynchronously(displayPipelinedImages, cancellationToken = cts.Token)15: with :? OperationCanceledException -> ()

Open Pipelines

Advantages• Composability• Reusability• Can be used at any stage of processing• Separation of concerns• Flexible routing arrangements i.e. round robin, multicast,

content based routing

Disadvantages• Pipeline stages need to be balanced• Developers can have trouble debugging and visualizing

Pipelets Implementation

Pipelets in use

Future Directions

• Distributed Pipelets– Wave Pipelines

– Synchronous buffered Pipeline

• Advanced agent based programming- Supervision

- Pooling

- Control Messages