Skip to content

Instantly share code, notes, and snippets.

@marklam
Created May 23, 2023 18:23
Show Gist options
  • Save marklam/ddb74120bf2c6a70b4e4d551faae60dd to your computer and use it in GitHub Desktop.
Save marklam/ddb74120bf2c6a70b4e4d551faae60dd to your computer and use it in GitHub Desktop.
Benchmark on equality of F# types containing large arrays
namespace Benchmarks
open BenchmarkDotNet.Attributes
type SingleCaseDU = | One of int[]
type TwoCaseDU = | Empty | Full of int[]
type Record = { Values : int[] }
type [<Struct>] StructRecord = { Values : int[] }
module Custom =
let compareSingle (x : SingleCaseDU) (y : SingleCaseDU) =
match x, y with
| One x, One y -> LanguagePrimitives.PhysicalEquality x y || x = y
let compareTwo (x : TwoCaseDU) (y : TwoCaseDU) =
match x, y with
| Empty, Empty -> true
| Full x, Full y -> LanguagePrimitives.PhysicalEquality x y || x = y
| _ -> false
[<SimpleJob()>]
type BenchmarkEquality() =
let values = Array.zeroCreate 100_000_000
let copiedValues = Array.copy values
let oneCaseDU = One values
let equivalentOneCaseDU = One values
let twoCaseDU = Full values
let equivalentTwoCaseDU = Full values
let record = { Record.Values = values }
let equivalentRecord = { Record.Values = values }
let structRecord = { StructRecord.Values = values }
let equivalentStructRecord = { StructRecord.Values = values }
[<Benchmark()>]
member _.SameArray() =
values.Equals values
|> ignore
[<Benchmark()>]
member _.EquivalentArray() =
values.Equals copiedValues
|> ignore
[<Benchmark()>]
member _.SameOneCaseDU() =
oneCaseDU.Equals oneCaseDU
|> ignore
[<Benchmark()>]
member _.EquivalentOneCaseDU() =
oneCaseDU.Equals equivalentOneCaseDU
|> ignore
[<Benchmark()>]
member _.SameTwoCaseDU() =
twoCaseDU.Equals twoCaseDU
|> ignore
[<Benchmark()>]
member _.EquivalentTwoCaseDU() =
twoCaseDU.Equals equivalentTwoCaseDU
|> ignore
[<Benchmark()>]
member _.SameRecord() =
record.Equals record
|> ignore
[<Benchmark()>]
member _.EquivalentRecord() =
equivalentRecord.Equals equivalentRecord
|> ignore
[<Benchmark()>]
member _.SameStructRecord() =
structRecord.Equals structRecord
|> ignore
[<Benchmark()>]
member _.EquivalentStructRecord() =
equivalentStructRecord.Equals equivalentStructRecord
|> ignore
[<Benchmark()>]
member _.opSameOneCaseDU() =
oneCaseDU = oneCaseDU
|> ignore
[<Benchmark()>]
member _.opEquivalentOneCaseDU() =
oneCaseDU = equivalentOneCaseDU
|> ignore
[<Benchmark()>]
member _.opSameTwoCaseDU() =
twoCaseDU = twoCaseDU
|> ignore
[<Benchmark()>]
member _.opEquivalentTwoCaseDU() =
twoCaseDU = equivalentTwoCaseDU
|> ignore
[<Benchmark()>]
member _.opSameRecord() =
record = record
|> ignore
[<Benchmark()>]
member _.opEquivalentRecord() =
equivalentRecord = equivalentRecord
|> ignore
[<Benchmark()>]
member _.opSameStructRecord() =
structRecord = structRecord
|> ignore
[<Benchmark()>]
member _.opEquivalentStructRecord() =
equivalentStructRecord = equivalentStructRecord
|> ignore
[<Benchmark()>]
member _.customSameOneCaseDU() =
Custom.compareSingle oneCaseDU oneCaseDU
|> ignore
[<Benchmark()>]
member _.customEquivalentOneCaseDU() =
Custom.compareSingle oneCaseDU equivalentOneCaseDU
|> ignore
[<Benchmark()>]
member _.customSameTwoCaseDU() =
Custom.compareTwo twoCaseDU twoCaseDU
|> ignore
[<Benchmark()>]
member _.customEquivalentTwoCaseDU() =
Custom.compareTwo twoCaseDU equivalentTwoCaseDU
|> ignore
@marklam
Copy link
Author

marklam commented May 23, 2023

BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2965/22H2/2022Update)
Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
.NET SDK=7.0.302
  [Host]     : .NET 6.0.16 (6.0.1623.17311), X64 RyuJIT AVX2 DEBUG
  DefaultJob : .NET 6.0.16 (6.0.1623.17311), X64 RyuJIT AVX2


|                    Method |              Mean |           Error |          StdDev |
|-------------------------- |------------------:|----------------:|----------------:|
|                 SameArray |          1.145 ns |       0.0094 ns |       0.0088 ns |
|           EquivalentArray |          1.552 ns |       0.0081 ns |       0.0076 ns |
|             SameOneCaseDU | 81,049,076.923 ns | 167,455.0098 ns | 139,832.5172 ns |
|       EquivalentOneCaseDU | 81,351,780.952 ns | 409,548.7878 ns | 383,092.1902 ns |
|             SameTwoCaseDU | 80,879,020.879 ns | 163,913.1718 ns | 136,874.9221 ns |
|       EquivalentTwoCaseDU | 80,822,615.306 ns | 173,922.2531 ns | 154,177.5472 ns |
|                SameRecord | 80,736,776.531 ns | 143,072.5836 ns | 126,830.1187 ns |
|          EquivalentRecord | 80,865,691.667 ns | 131,882.6442 ns | 102,965.2937 ns |
|          SameStructRecord | 80,963,208.163 ns | 547,544.1434 ns | 485,383.6212 ns |
|    EquivalentStructRecord | 80,752,518.095 ns | 234,183.9689 ns | 219,055.8298 ns |
|           opSameOneCaseDU | 80,881,650.000 ns | 124,689.3460 ns | 110,533.8575 ns |
|     opEquivalentOneCaseDU | 80,672,534.524 ns |  87,665.2093 ns |  68,443.2290 ns |
|           opSameTwoCaseDU | 80,706,104.762 ns | 317,167.4151 ns | 247,623.4553 ns |
|     opEquivalentTwoCaseDU | 80,678,138.776 ns | 114,368.3034 ns | 101,384.5220 ns |
|              opSameRecord | 80,755,301.020 ns | 158,004.1856 ns | 140,066.5950 ns |
|        opEquivalentRecord | 80,858,879.048 ns | 262,187.6574 ns | 245,250.4974 ns |
|        opSameStructRecord | 80,707,754.082 ns | 206,429.5735 ns | 182,994.4401 ns |
|  opEquivalentStructRecord | 80,805,840.816 ns | 102,420.3820 ns |  90,793.0009 ns |
|       customSameOneCaseDU |          1.090 ns |       0.0042 ns |       0.0039 ns |
| customEquivalentOneCaseDU |          1.377 ns |       0.0043 ns |       0.0040 ns |
|       customSameTwoCaseDU |          5.503 ns |       0.1348 ns |       0.1753 ns |
| customEquivalentTwoCaseDU |          5.575 ns |       0.0266 ns |       0.0222 ns |

// * Warnings *
MultimodalDistribution
  BenchmarkEquality.customSameTwoCaseDU: Default -> It seems that the distribution is bimodal (mValue = 3.67)

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