diff --git a/index.html b/index.html index bd9bcd5..05fabcb 100644 --- a/index.html +++ b/index.html @@ -65,7 +65,7 @@

Web (Giraffe)

Lecture 10

-

+

Efficiency

Can be found here
diff --git a/slides/105/efficiency.md b/slides/10/efficiency.md similarity index 66% rename from slides/105/efficiency.md rename to slides/10/efficiency.md index bc32e17..d52445e 100644 --- a/slides/105/efficiency.md +++ b/slides/10/efficiency.md @@ -389,221 +389,6 @@ let foldBack f m x = ``` - ----- - -### Performance - -```fsharp -type A = {X: int; Y: int} -// vs -[] -type B = {X: int; Y: int} -``` - -* Type `B` is 16 bytes smaller, not having `8B` Object header + `8B` vTabel -* `B` is passed as values, therefore not always faster - ----- - -### Padding - -```fsharp -type A = {X: int; Y: int} -type B = {X: int; Y: int; Y: int} -``` - -* Here `B` is padded with 4 bytes extra, from the .NET Allocator - - ----- - -### Union types - -![Discriminated unions](./img/class-vs-struct-union-1.png) - - ----- - -### Immutable data structures - -Records, unions, Lists etc etc. - -* Fewer movings parts -* Thread-safe by default -* % Not nessesary performance optimal. - * Change to use mutable datastructures within a module/function to gain performance. - ----- - -### Other things to consider - -* Hardware, L1, L2, caches -* OS pointer sizes 32/64b -* Inlining functions - -F# can be very [performant](https://www.youtube.com/@FastFSharp) - used by stock companies because of this and its safety. But it requires work and knowledge about .Net Il, Hardware and F# of course. - ---- - -### Amortized bounds - -* An extension to the Big-O notation from DOA -* Used to analysis average run time - * Usually used when some operations are fast and other are slow - -* C# List is an example, its worst case bounds - * insert: `$ O(n) $` -- **why?** - * lookup: `$ O(1) $` - * delete: `$ O(1) $` - ----- - -### C# List analysis over time - -* `$ \rightarrow $` `n` insertions are `$ O(n^2) $` worst case -* You can make the amortized analysis that shows - * `n` insertions are `$ O(2n) $` - * So amortized bounds are `$ O(1) $` for all operations -* See references for detailed analysis - - ---- - -## Queue - -![Queue](./img/queue.jpg "Queue") - ----- - -### Definition and operation - -```fsharp [5-10] -module Queue - -type Queue<'a> = 'a list * 'a list - -val empty: Queue<'a> -val isEmpty: Queue<'a> -> bool - -val cons: 'a -> Queue<'a> -> Queue<'a> -val head: Queue<'a> -> 'a -val tail: Queue<'a> -> Queue<'a> -``` - -* Could be done with a single list - ----- - -### Operation implementations - -```fsharp [2,7|3,8|4,9] -let rec head = function - | ([], []) -> raise (ArgumentException "Empty") - | ([], r) -> head (List.rev r, []) - | (l, _) -> List.head l - -let rec tail = function - | ([], []) -> raise (ArgumentException "Empty") - | ([], r) -> tail (List.rev r, []) - | (l, r) -> (List.tail l, r) -``` - ----- - - -### Using lazy evaluation - -* To optimize our queue [https://en.wikipedia.org/wiki/Chris_Okasaki](C. Okasaki) proposes to use lazy lists. - * Almost like `seq`` in F# - -```fsharp -module LazyQueue - -type LazyQueue<'a> = seq<'a> * seq<'a> - -val empty: LazyQueue<'a> -val isEmpty: LazyQueue<'a> -> bool - -val cons: 'a -> LazyQueue<'a> -> LazyQueue<'a> -val head: LazyQueue<'a> -> 'a -val tail: LazyQueue<'a> -> LazyQueue<'a> -``` - - ----- - -### Lazy queue impl (1/2) - -So here we utilises that `Seq` is lazy - -```fsharp -let l' = Seq.append l r -``` - -This do not evalute `r` before its needed - ----- - -### Lazy queue impl (2/2) - -```fsharp -let l` = Seq.append l (Seq.rev r) -``` - -1. since append is lazy, we need to do `Seq.rev` incrementally. -2. so do one step of `Seq.rev` for each step of append -3. we then need the invariant `Seq.length r <= Seq.length l` - -Note: -Seq.rev - will reverse the list in one go - -```fsharp -// https://github.com/fsharp/fsharp/blob/577d06b9ec7192a6adafefd09ade0ed10b13897d/src/fsharp/FSharp.Core/seq.fs#L1424 -let rev source = - checkNonNull "source" source - mkDelayedSeq (fun () -> - let array = source |> toArray - Array.Reverse array - array :> seq<_>) -``` - ----- - -### Incremental rotation - -```fsharp -let rec rot (l, r, a) = match Seq.length l - | 0 -> (Seq.head r) :: a - | 1 -> (Seq.head l) :: (rot - (Seq.tail l, Seq.tail r, Seq.head r :: a)) -``` - -\* Not battle tested code - -\*\* Not optimized code - ----- - -### Bounds - -* Amortized bounds for all operations are still `$ O(1) $` -* Worst case `$ O(log n) $` - ----- - -### Amortization - -* Not useful in `realtime` systems - * why? - ----- - -### Deque idea - -![Deque](./img/deque.png "Deque") - - --- ## References diff --git a/slides/105/img/class-vs-struct-union-1.png b/slides/10/img/class-vs-struct-union-1.png similarity index 100% rename from slides/105/img/class-vs-struct-union-1.png rename to slides/10/img/class-vs-struct-union-1.png diff --git a/slides/105/img/collection.png b/slides/10/img/collection.png similarity index 100% rename from slides/105/img/collection.png rename to slides/10/img/collection.png diff --git a/slides/105/img/deque.png b/slides/10/img/deque.png similarity index 100% rename from slides/105/img/deque.png rename to slides/10/img/deque.png diff --git a/slides/105/img/lock-free.png b/slides/10/img/lock-free.png similarity index 100% rename from slides/105/img/lock-free.png rename to slides/10/img/lock-free.png diff --git a/slides/105/img/queue.jpg b/slides/10/img/queue.jpg similarity index 100% rename from slides/105/img/queue.jpg rename to slides/10/img/queue.jpg diff --git a/slides/105/img/recursion.jpg b/slides/10/img/recursion.jpg similarity index 100% rename from slides/105/img/recursion.jpg rename to slides/10/img/recursion.jpg diff --git a/slides/105/index.html b/slides/10/index.html similarity index 100% rename from slides/105/index.html rename to slides/10/index.html diff --git a/slides/105/heap and queue.md b/slides/105/heap and queue.md new file mode 100644 index 0000000..476fe71 --- /dev/null +++ b/slides/105/heap and queue.md @@ -0,0 +1,213 @@ +---- + +### Performance + +```fsharp +type A = {X: int; Y: int} +// vs +[] +type B = {X: int; Y: int} +``` + +* Type `B` is 16 bytes smaller, not having `8B` Object header + `8B` vTabel +* `B` is passed as values, therefore not always faster + +---- + +### Padding + +```fsharp +type A = {X: int; Y: int} +type B = {X: int; Y: int; Y: int} +``` + +* Here `B` is padded with 4 bytes extra, from the .NET Allocator + + +---- + +### Union types + +![Discriminated unions](./img/class-vs-struct-union-1.png) + + +---- + +### Immutable data structures + +Records, unions, Lists etc etc. + +* Fewer movings parts +* Thread-safe by default +* % Not nessesary performance optimal. + * Change to use mutable datastructures within a module/function to gain performance. + +---- + +### Other things to consider + +* Hardware, L1, L2, caches +* OS pointer sizes 32/64b +* Inlining functions + +F# can be very [performant](https://www.youtube.com/@FastFSharp) - used by stock companies because of this and its safety. But it requires work and knowledge about .Net Il, Hardware and F# of course. + +--- + +### Amortized bounds + +* An extension to the Big-O notation from DOA +* Used to analysis average run time + * Usually used when some operations are fast and other are slow + +* C# List is an example, its worst case bounds + * insert: `$ O(n) $` -- **why?** + * lookup: `$ O(1) $` + * delete: `$ O(1) $` + +---- + +### C# List analysis over time + +* `$ \rightarrow $` `n` insertions are `$ O(n^2) $` worst case +* You can make the amortized analysis that shows + * `n` insertions are `$ O(2n) $` + * So amortized bounds are `$ O(1) $` for all operations +* See references for detailed analysis + + +--- + +## Queue + +![Queue](./img/queue.jpg "Queue") + +---- + +### Definition and operation + +```fsharp [5-10] +module Queue + +type Queue<'a> = 'a list * 'a list + +val empty: Queue<'a> +val isEmpty: Queue<'a> -> bool + +val cons: 'a -> Queue<'a> -> Queue<'a> +val head: Queue<'a> -> 'a +val tail: Queue<'a> -> Queue<'a> +``` + +* Could be done with a single list + +---- + +### Operation implementations + +```fsharp [2,7|3,8|4,9] +let rec head = function + | ([], []) -> raise (ArgumentException "Empty") + | ([], r) -> head (List.rev r, []) + | (l, _) -> List.head l + +let rec tail = function + | ([], []) -> raise (ArgumentException "Empty") + | ([], r) -> tail (List.rev r, []) + | (l, r) -> (List.tail l, r) +``` + +---- + + +### Using lazy evaluation + +* To optimize our queue [https://en.wikipedia.org/wiki/Chris_Okasaki](C. Okasaki) proposes to use lazy lists. + * Almost like `seq`` in F# + +```fsharp +module LazyQueue + +type LazyQueue<'a> = seq<'a> * seq<'a> + +val empty: LazyQueue<'a> +val isEmpty: LazyQueue<'a> -> bool + +val cons: 'a -> LazyQueue<'a> -> LazyQueue<'a> +val head: LazyQueue<'a> -> 'a +val tail: LazyQueue<'a> -> LazyQueue<'a> +``` + + +---- + +### Lazy queue impl (1/2) + +So here we utilises that `Seq` is lazy + +```fsharp +let l' = Seq.append l r +``` + +This do not evalute `r` before its needed + +---- + +### Lazy queue impl (2/2) + +```fsharp +let l` = Seq.append l (Seq.rev r) +``` + +1. since append is lazy, we need to do `Seq.rev` incrementally. +2. so do one step of `Seq.rev` for each step of append +3. we then need the invariant `Seq.length r <= Seq.length l` + +Note: +Seq.rev - will reverse the list in one go + +```fsharp +// https://github.com/fsharp/fsharp/blob/577d06b9ec7192a6adafefd09ade0ed10b13897d/src/fsharp/FSharp.Core/seq.fs#L1424 +let rev source = + checkNonNull "source" source + mkDelayedSeq (fun () -> + let array = source |> toArray + Array.Reverse array + array :> seq<_>) +``` + +---- + +### Incremental rotation + +```fsharp +let rec rot (l, r, a) = match Seq.length l + | 0 -> (Seq.head r) :: a + | 1 -> (Seq.head l) :: (rot + (Seq.tail l, Seq.tail r, Seq.head r :: a)) +``` + +\* Not battle tested code + +\*\* Not optimized code + +---- + +### Bounds + +* Amortized bounds for all operations are still `$ O(1) $` +* Worst case `$ O(log n) $` + +---- + +### Amortization + +* Not useful in `realtime` systems + * why? + +---- + +### Deque idea + +![Deque](./img/deque.png "Deque") +