This document discusses F# server-side programming and concepts like functional programming, concurrency, and asynchronous programming. It provides an overview of fundamental F# concepts like pure functions and immutability. It also describes patterns for object-oriented programming in F# using techniques like higher-order functions instead of inheritance. The document demonstrates examples of building an object pool and processing images in a pipeline using F# and the Fracture library. It compares asynchronous programming approaches and the performance of different socket models.
1. F# Server side programming
Dave Thomas
MoiraeSoftware.com
twitter.com/7sharp9
2. F# Server based programming
Why F#
• Concise succinct code
• Advanced asynchronous support
• Language Orientated Programming
• Generic by default
• Immutable by default
3. Fundamental Concepts
• Pure Functions
– No side effects
– memoization
• Immutability
– No threading issues
• Lambda expressions
• Higher order functions
• Recursion
8. F# ObjectPool
//Agent alias for MailboxProcessor
type Agent<'T> = MailboxProcessor<'T>
///One of three messages for our Object Pool agent
typePoolMessage<'a> =
| Get ofAsyncReplyChannel<'a>
| Put of 'a
| Clear ofAsyncReplyChannel<List<'a>>
/// Object pool representing a reusable pool of objects
typeObjectPool<'a>(generate: unit -> 'a, initialPoolCount) =
let initial = List.initinitialPoolCount (fun (x) -> generate())
let agent = Agent.Start(fun inbox ->
letrecloop(x) = async {
let!msg = inbox.Receive()
matchmsgwith
| Get(reply) ->
let res = matchxwith
| 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.
memberthis.ToListAndClear() =
agent.PostAndAsyncReply(Clear)
/// Puts an item into the pool
memberthis.Put(item ) =
agent.Post(item)
/// Gets an item from the pool or if there are none present use the
generator
memberthis.Get(item) =
agent.PostAndAsyncReply(Get)
9. F# - Fracture IO
Open source high performance
Socket, Pipeline, and agent library
10. Sockets
2 Models of Operation
• AsyncResult Model
• SocketAsyncEventArgs Model
11. IAsyncResults Model
Pros
• 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
13. 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
14. 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
19. Agents
• Provide a message passing mechanism
• Agents read and respond to a queue of
messages
• Typically a discriminated union is used to
describe messages
23. Image Processing Pipeline
1: // Phase 1: Load images from disk and put them into a queue
2: letloadImages=async {
3: letclockOffset=Environment.TickCount
4: letrec numbers n=seq { yieldn; yield! numbers (n+1) }
5: for count, imginfileNames|>Seq.zip (numbers 0) do
6: let info =loadImageimgsourceDir count clockOffset
7: do!loadedImages.AsyncAdd(info) }
8:
9: // Phase 2: Scale to a thumbnail size and add frame
10: letscalePipelinedImages=async {
11: whiletruedo
12: let! info =loadedImages.AsyncGet()
13: scaleImage info
14: do!scaledImages.AsyncAdd(info) }
1: letloadedImages=newBlockingQueueAgent<_>(queueLength)
2: letscaledImages=newBlockingQueueAgent<_>(queueLength)
3: letfilteredImages=newBlockingQueueAgent<_>(queueLength)
1: // Phase 4: Display images as they become available
2: letdisplayPipelinedImages=async {
3: whiletruedo
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 phases
11: Async.Start(loadImages, cts.Token)
12: Async.Start(scalePipelinedImages, cts.Token)
13: Async.Start(filterPipelinedImages, cts.Token)
14: tryAsync.RunSynchronously(displayPipelinedImages, cancellationToken=cts.Token)
15: with:?OperationCanceledException-> ()
24. 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