Skip to content

Repository files navigation

Fsharp.Array.Parallel.Sort.Benchmark

Picking the implementation for Array.Parallel.sort family of functions

What is this about

As part of the F# RFC - 1130 Additions to collection functions in Array.Parallel, several new functions will be added. One of the proposals is for the family of sort functions, like sortBy, sortDescending, etc. This repository exists to benchmark implementation proposals againts a standard baseline and compare them with each other.

How to get involved

Simply add your code into a new module and add a benchmark case for it, that is it. You can ask questions here or on the F# slack channel for "code" here

Comparison

Check the benchmarks in the project to see the baselines and evaluate your approach against them, everyone is invited!

Current output

The benchmark starts with 2 baselines - using sequential Array.sortBy, and using PLINQ in default settings (for example I have 8 physical cores, and 16 logical CPUs. The default chooses based on number of logical CPUs).

It then has additional implementations which:

  • Chunk input into N runs, sort each in parallel, and then do pairwise merging in parallel
  • Just for reference, measure the cost of only sorting N runs without merging them back
  • Again N presorted runs, put together using a binary min heap

The benefit of parallelism get's bigger if projection function is more expensive. Here are the benchmark configurations used with respect to class being sorted and projection used for sorting.

typeReferenceRecord={Id :int; Value :float}withinterface IBenchMarkElement<ReferenceRecord>withstatic memberCreate(id,value)={Id = id; Value = value}static memberProjection()=fun x -> x.Value |> sin |> cos |> string |> hash |> string |> String.length
[<Struct>]typeStructRecord={Id :int; Value :float}withinterface IBenchMarkElement<StructRecord>withstatic memberCreate(id,value)={Id = id; Value = value}static memberProjection()=fun x -> x.Value |> sin |> cos |> string |> hash |> string |> String.length
[<Struct>]typeStructRecordCheapProjection={Id :int; Value :float}withinterface IBenchMarkElement<StructRecordCheapProjection>withstatic memberCreate(id,value)={Id = id; Value = value}static memberProjection()=fun x -> x.Value.ToString()|> hash
[<Struct>]typeStructRecordNoOpProjectionPresorted={Id :int; Value :float}withinterface IBenchMarkElement<StructRecordNoOpProjectionPresorted>withstatic memberCreate(id,value)={Id = id; Value = value}static memberProjection()=fun x -> x.Id
[<Struct>]typeStructRecordNoOpProjectionInverselyPresorted={Id :int; Value :float}withinterface IBenchMarkElement<StructRecordNoOpProjectionInverselyPresorted>withstatic memberCreate(id,value)={Id = id; Value = value}static memberProjection()=fun x ->-x.Id

// * Summary *

BenchmarkDotNet=v0.13.4, OS=Windows 11 (10.0.22621.1265) 11th Gen Intel Core i9-11950H 2.60GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.300-preview.23122.5 [Host] : .NET 7.0.3 (7.0.323.6910), X64 RyuJIT AVX2 DEBUG DefaultJob : .NET 7.0.3 (7.0.323.6910), X64 RyuJIT AVX2

StructRecordNoOpProjectionPresorted

MethodNumberOfItemsMeanErrorStdDevCompleted Work ItemsLock ContentionsGen0Gen1Gen2Allocated
PLINQDefault50000023.554 ms0.4587 ms0.7912 ms15.00000.06251687.50001625.00001343.750095.1 MB
MergeUsingPivotPartitioning5000002.073 ms0.0359 ms0.0369 ms27.47660.0117398.4375394.5313394.53139.56 MB
PLINQDefault4000000151.818 ms3.0206 ms7.4662 ms15.00000.25001750.00001500.00001500.0000761 MB
MergeUsingPivotPartitioning400000019.540 ms0.3887 ms0.6494 ms29.75000.1250343.7500343.7500343.750076.3 MB
PLINQDefault20000000773.709 ms15.4316 ms18.3703 ms15.0000-1000.00001000.00001000.00004669.1 MB
MergeUsingPivotPartitioning2000000098.946 ms1.9657 ms2.4859 ms31.0000-166.6667166.6667166.6667381.48 MB

StructRecordCheapProjection

MethodNumberOfItemsMeanErrorStdDevCompleted Work ItemsLock ContentionsGen0Gen1Gen2Allocated
PLINQDefault50000051.80 ms1.033 ms2.496 ms15.0000-4500.00003300.00001700.0000125.34 MB
MergeUsingPivotPartitioning50000025.75 ms0.333 ms0.312 ms32.00000.03132781.2500250.0000250.000039.78 MB
PLINQDefault4000000418.10 ms8.337 ms20.134 ms15.0000-21000.00002000.00001000.00001002.88 MB
MergeUsingPivotPartitioning4000000192.50 ms3.824 ms4.552 ms32.0000-20000.0000--318.19 MB
PLINQDefault200000003,047.41 ms82.363 ms241.555 ms15.0000-102000.00002000.00001000.00005878.53 MB
MergeUsingPivotPartitioning20000000970.46 ms17.743 ms22.439 ms47.0000-101000.0000--1590.93 MB

ReferenceRecord

MethodNumberOfItemsMeanErrorStdDevCompleted Work ItemsLock ContentionsGen0Gen1Gen2Allocated
PLINQDefault50000055.45 ms1.094 ms1.604 ms15.00000.10005500.00003100.0000800.0000108.22 MB
MergeUsingPivotPartitioning50000029.16 ms0.367 ms0.343 ms20.0000-4656.2500312.5000312.500057.74 MB
PLINQDefault4000000510.32 ms10.205 ms10.023 ms15.0000-36000.00003000.00001000.0000865.93 MB
MergeUsingPivotPartitioning4000000189.29 ms2.949 ms3.835 ms20.0000-34666.6667--461.83 MB
PLINQDefault200000003,122.62 ms101.060 ms297.978 ms15.0000-176000.00004000.00002000.00004809.78 MB
MergeUsingPivotPartitioning20000000969.69 ms19.353 ms34.897 ms20.0000-174000.0000--2309.1 MB

Results using other losing algorithms are uploaded in the results folder

About

Picking the implementation for Array.Parallel.sort family of functions

Resources

Stars

2 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages