Skip to content

Instantly share code, notes, and snippets.

@thinkbeforecoding
Created February 17, 2022 17:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkbeforecoding/1d07315cfb21ee779f5b75a497773731 to your computer and use it in GitHub Desktop.
Save thinkbeforecoding/1d07315cfb21ee779f5b75a497773731 to your computer and use it in GitHub Desktop.
StackStack
| Method | Mean | Error | StdDev | Ratio | RatioSD |
|-------------- |---------:|--------:|--------:|------:|--------:|
| UseSpan | 241.7 ns | 2.98 ns | 2.64 ns | 1.00 | 0.00 |
| UseCollection | 403.4 ns | 5.58 ns | 5.22 ns | 1.67 | 0.03 |
// Learn more about F# at http://docs.microsoft.com/dotnet/fsharp
open System
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Running
// Define a function to construct a message to print
#nowarn "9" "42"
open System.Runtime.CompilerServices
let inline stackalloc<'t> n = Span<'t>( (# "localloc" (n*sizeof<'t>) : voidptr #), n)
[<Struct;IsByRefLike>]
type StackStack<'t> =
{ Span: Span<'t>
mutable Index: int}
module StackStack =
let inline create<'t> n =
{ Span = stackalloc<int64> n; Index = 0 }
let inline pop (stack: StackStack<'t> byref) =
stack.Index <- stack.Index-1
stack.Span[stack.Index]
let inline push value (stack: StackStack<'t> byref) =
stack.Span[stack.Index] <- value
stack.Index <- stack.Index+1
open System.Collections.Generic
[<RyuJitX64Job>]
type StackBench() =
let rec algo n (stack: StackStack<int64> byref) =
if n > 0 then
StackStack.push (int64 n) &stack
algo (n-1) &stack
let dowork() =
let mutable stack = StackStack.create 256
algo 100 &stack
let mutable result = 0L
for _ in 0 .. 99 do
result <- result + StackStack.pop &stack
result
let stack = Stack<int64>(256)
let rec algo' n =
if n > 0 then
stack.Push (int64 n)
algo' (n-1)
let dowork'() =
stack.Clear()
algo' 100
let mutable result = 0L
for i in 0 .. 99 do
result <- result + stack.Pop()
result
[<Benchmark(Baseline = true)>]
member _.UseSpan() =
dowork()
[<Benchmark()>]
member _.UseCollection() =
dowork'()
let defaultSwitch () = BenchmarkSwitcher [| typeof<StackBench>|]
[<EntryPoint>]
let main argv =
let sumarry = defaultSwitch().Run argv
0 // return an integer exit code
@matthewcrews
Copy link

I'm curious why you define a function for stack allocating that emits IL instead of using built-in methods. Is there a reason for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment