From 0e07797f3dc4f7a2cc77a575f7ee1ffb870aca7a Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sat, 7 Sep 2024 17:32:47 +0200 Subject: [PATCH 01/65] WeakOutputSet: avoid temporary allocations when changing from Array to HashSet --- src/FSharp.Data.Adaptive/Core/Core.fs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Core/Core.fs b/src/FSharp.Data.Adaptive/Core/Core.fs index 35b925f..6e30a5f 100644 --- a/src/FSharp.Data.Adaptive/Core/Core.fs +++ b/src/FSharp.Data.Adaptive/Core/Core.fs @@ -227,6 +227,10 @@ and [] private VolatileSetData = /// entries. The only other functionality is Consume which returns all the /// (currently live) entries and clears the set. and internal WeakOutputSet() = + + let [] ArrayCapacity = 8 + let [] HashSetCapacity = 16 + let mutable data = Unchecked.defaultof let mutable setOps = 0 let mutable valueReader = ref Unchecked.defaultof @@ -246,7 +250,7 @@ and internal WeakOutputSet() = if found then false else - let arr = Array.zeroCreate 8 + let arr = Array.zeroCreate ArrayCapacity arr.[0] <- data.Single arr.[1] <- weakObj data.Tag <- 1 @@ -282,8 +286,13 @@ and internal WeakOutputSet() = true else // r cannot be null here (empty index would have been found) - let all = data.Array |> Array.choose (fun r -> if r.TryGetTarget(valueReader) then Some r else None) - let set = HashSet all + let set = HashSet>() + #if NET5_0_OR_GREATER // NOTE: could also use netstadnard2.1 + set.EnsureCapacity(HashSetCapacity) |> ignore + #endif + for r in data.Array do + if r.TryGetTarget(valueReader) then + set.Add r |> ignore let res = set.Add weakObj data.Tag <- 2 data.Set <- set From 333940b14de7903dbc0ec528af1784455d0c793e Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 8 Sep 2024 10:26:04 +0200 Subject: [PATCH 02/65] changed Cache to use value options and tuples --- .../AdaptiveHashSet/AdaptiveHashSet.fs | 10 ++-- src/FSharp.Data.Adaptive/Utilities/Cache.fs | 52 +++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index 3e43829..cc7949a 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -465,10 +465,10 @@ module AdaptiveHashSetImplementation = Add(cache.Invoke v) |> Some | Rem(1, v) -> match cache.RevokeAndGetDeletedTotal v with - | Some (del, v) -> + | ValueSome (del, v) -> if del then (v :> IDisposable).Dispose() Rem v |> Some - | None -> + | ValueNone -> None | _ -> unexpected() @@ -593,7 +593,7 @@ module AdaptiveHashSetImplementation = | Rem(1, v) -> // r is no longer dirty since we either pull or destroy it here. - let deleted, r = cache.RevokeAndGetDeleted v + let struct(deleted, r) = cache.RevokeAndGetDeleted v dirty.Remove r |> ignore if deleted then // in case r was not dirty we need to explicitly remove ourselves @@ -822,7 +822,7 @@ module AdaptiveHashSetImplementation = | Rem(1, value) -> match cache.RevokeAndGetDeletedTotal value with - | Some (deleted, r) -> + | ValueSome (deleted, r) -> // r is no longer dirty since we either pull or destroy it here. dirty.Remove r |> ignore if deleted then @@ -832,7 +832,7 @@ module AdaptiveHashSetImplementation = CountingHashSet.removeAll r.State else r.GetChanges token - | None -> + | ValueNone -> // weird HashSetDelta.empty diff --git a/src/FSharp.Data.Adaptive/Utilities/Cache.fs b/src/FSharp.Data.Adaptive/Utilities/Cache.fs index 778ec06..17df674 100644 --- a/src/FSharp.Data.Adaptive/Utilities/Cache.fs +++ b/src/FSharp.Data.Adaptive/Utilities/Cache.fs @@ -20,10 +20,10 @@ type internal Cache<'T1, 'T2>(mapping : 'T1 -> 'T2) = #endif /// cache for non-null values. - let cache = DefaultDictionary.create<'T1, 'T2 * ref>() + let cache = DefaultDictionary.create<'T1, struct('T2 * ref)>() /// cache for null values (needed for option, unit, etc.) - let mutable nullCache : option<_ * ref<_>> = None + let mutable nullCache : ValueOption)> = ValueNone /// Clear removes all entries from the Cache and /// executes a function for all removed cache entries. @@ -34,10 +34,10 @@ type internal Cache<'T1, 'T2>(mapping : 'T1 -> 'T2) = remove v cache.Clear() match nullCache with - | Some (v,_) -> + | ValueSome (v,_) -> remove v - nullCache <- None - | None -> () + nullCache <- ValueNone + | ValueNone -> () /// Invoke returns the function value associated /// With the given argument (possibly executing the function) @@ -45,12 +45,12 @@ type internal Cache<'T1, 'T2>(mapping : 'T1 -> 'T2) = member x.Invoke (v : 'T1) = if isNull v then match nullCache with - | Some (r, ref) -> + | ValueSome (r, ref) -> ref.Value <- ref.Value + 1 r - | None -> + | ValueNone -> let r = mapping v - nullCache <- Some(r, ref 1) + nullCache <- ValueSome(r, ref 1) r else match cache.TryGetValue v with @@ -66,23 +66,23 @@ type internal Cache<'T1, 'T2>(mapping : 'T1 -> 'T2) = member x.RevokeAndGetDeleted (v : 'T1) = if isNull v then match nullCache with - | Some (r, ref) -> + | ValueSome (r, ref) -> ref.Value <- ref.Value - 1 if ref.Value = 0 then - nullCache <- None - (true, r) + nullCache <- ValueNone + struct(true, r) else - (false, r) - | None -> failwithf "cannot revoke null" + struct(false, r) + | ValueNone -> failwithf "cannot revoke null" else match cache.TryGetValue v with | (true, (r, ref)) -> ref.Value <- ref.Value - 1 if ref.Value = 0 then cache.Remove v |> ignore - (true, r) + struct(true, r) else - (false, r) + struct(false, r) | _ -> failwithf "cannot revoke unknown value: %A" v /// Revoke returns the function value associated @@ -90,31 +90,31 @@ type internal Cache<'T1, 'T2>(mapping : 'T1 -> 'T2) = member x.RevokeAndGetDeletedTotal (v : 'T1) = if isNull v then match nullCache with - | Some (r, ref) -> + | ValueSome (r, ref) -> ref.Value <- ref.Value - 1 if ref.Value = 0 then - nullCache <- None - Some (true, r) + nullCache <- ValueNone + ValueSome (true, r) else - Some(false, r) - | None -> - None + ValueSome(false, r) + | ValueNone -> + ValueNone else match cache.TryGetValue v with | (true, (r, ref)) -> ref.Value <- ref.Value - 1 if ref.Value = 0 then cache.Remove v |> ignore - Some(true, r) + ValueSome(true, r) else - Some(false, r) + ValueSome(false, r) | _ -> - None + ValueNone /// Revoke the value and return its associated cache value. member x.Revoke (v : 'T1) = - x.RevokeAndGetDeleted v |> snd + x.RevokeAndGetDeleted v |> (fun struct(_,b) -> b) /// Enumerate over all cache values. member x.Values = - cache.Values |> Seq.map fst + cache.Values |> Seq.map (fun struct(a,_) -> a) From 34208cb91862f4776ec89abf50ef27d0db0cdd37 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 8 Sep 2024 13:14:00 +0200 Subject: [PATCH 03/65] updated Tests to net 8.0 --- .config/dotnet-tools.json | 14 +- paket.dependencies | 2 +- paket.lock | 307 ++++++++++-------- .../FSharp.Data.Adaptive.Tests.fsproj | 2 +- 4 files changed, 175 insertions(+), 150 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 77619ed..35874ed 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,28 +3,32 @@ "isRoot": true, "tools": { "paket": { - "version": "7.0.2", + "version": "8.0.3", "commands": [ "paket" - ] + ], + "rollForward": false }, "fable": { "version": "3.7.6", "commands": [ "fable" - ] + ], + "rollForward": false }, "aardpack": { "version": "1.0.11", "commands": [ "aardpack" - ] + ], + "rollForward": false }, "fsdocs-tool": { "version": "20.0.0-beta-002", "commands": [ "fsdocs" - ] + ], + "rollForward": false } } } \ No newline at end of file diff --git a/paket.dependencies b/paket.dependencies index 24acb21..db1cb4f 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -14,7 +14,7 @@ nuget FsCheck.NUnit ~> 2.14.0 nuget Microsoft.NET.Test.Sdk ~> 16.4.0 nuget NUnit ~> 3.12.0 nuget NUnit3TestAdapter ~> 3.15.1 -nuget BenchmarkDotNet ~> 0.12.0 +nuget BenchmarkDotNet ~> 0.14.0 # Fable demo nuget Fable.Core ~> 3.2.5 diff --git a/paket.lock b/paket.lock index a88cc0a..c7cda2f 100644 --- a/paket.lock +++ b/paket.lock @@ -1,27 +1,27 @@ REDIRECTS: OFF STORAGE: NONE -RESTRICTION: || (== net5.0) (== net6.0) (== netstandard2.0) +RESTRICTION: || (== net5.0) (== net6.0) (== net8.0) (== netstandard2.0) NUGET remote: https://api.nuget.org/v3/index.json - Aardvark.Build (1.0.18) - BenchmarkDotNet (0.12.1) - BenchmarkDotNet.Annotations (>= 0.12.1) - CommandLineParser (>= 2.4.3) - Iced (>= 1.4) - Microsoft.CodeAnalysis.CSharp (>= 2.10) - Microsoft.Diagnostics.NETCore.Client (>= 0.2.61701) - Microsoft.Diagnostics.Runtime (>= 1.1.57604) - Microsoft.Diagnostics.Tracing.TraceEvent (>= 2.0.49) - Microsoft.DotNet.PlatformAbstractions (>= 2.1) - Microsoft.Win32.Registry (>= 4.5) - Perfolizer (>= 0.2.1) - System.Management (>= 4.5) - System.Reflection.Emit (>= 4.3) - System.Reflection.Emit.Lightweight (>= 4.3) - System.Threading.Tasks.Extensions (>= 4.5.2) - System.ValueTuple (>= 4.5) - BenchmarkDotNet.Annotations (0.12.1) - CommandLineParser (2.7.82) + Aardvark.Build (1.0.25) + BenchmarkDotNet (0.14) + BenchmarkDotNet.Annotations (>= 0.14) + CommandLineParser (>= 2.9.1) + Gee.External.Capstone (>= 2.3) + Iced (>= 1.17) + Microsoft.CodeAnalysis.CSharp (>= 4.1) + Microsoft.Diagnostics.Runtime (>= 2.2.332302) + Microsoft.Diagnostics.Tracing.TraceEvent (>= 3.1.8) + Microsoft.DotNet.PlatformAbstractions (>= 3.1.6) + Microsoft.Win32.Registry (>= 5.0) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + Perfolizer (0.3.17) + System.Management (>= 5.0) + System.Numerics.Vectors (>= 4.5) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Reflection.Emit (>= 4.7) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Reflection.Emit.Lightweight (>= 4.7) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + BenchmarkDotNet.Annotations (0.14) + CommandLineParser (2.9.1) Fable.Browser.Blob (1.1) Fable.Core (>= 3.0) FSharp.Core (>= 4.6.2) @@ -50,33 +50,49 @@ NUGET FSharp.Core (>= 4.2.3) NETStandard.Library (>= 2.0.3) NUnit (>= 3.12 < 4.0) - Iced (1.9) - Microsoft.Bcl.AsyncInterfaces (1.1) - System.Threading.Tasks.Extensions (>= 4.5.2) - restriction: || (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netcoreapp2.1)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.1)) (== netstandard2.0) - Microsoft.CodeAnalysis.Analyzers (2.9.8) - Microsoft.CodeAnalysis.Common (3.4) - Microsoft.CodeAnalysis.Analyzers (>= 2.9.6) - System.Collections.Immutable (>= 1.5) - System.Memory (>= 4.5.3) - System.Reflection.Metadata (>= 1.6) - System.Runtime.CompilerServices.Unsafe (>= 4.5.2) - System.Text.Encoding.CodePages (>= 4.5.1) - System.Threading.Tasks.Extensions (>= 4.5.3) - Microsoft.CodeAnalysis.CSharp (3.4) - Microsoft.CodeAnalysis.Common (3.4) - Microsoft.CodeCoverage (16.4) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) - Microsoft.Diagnostics.NETCore.Client (0.2.152202) - Microsoft.Bcl.AsyncInterfaces (>= 1.1) - Microsoft.Diagnostics.Runtime (2.0.156101) - Microsoft.Diagnostics.NETCore.Client (>= 0.2.61701) - System.Buffers (>= 4.5.1) - System.Collections.Immutable (>= 1.7.1) - System.Memory (>= 4.5.4) - System.Reflection.Metadata (>= 1.8.1) - System.Runtime.CompilerServices.Unsafe (>= 4.7.1) - Microsoft.Diagnostics.Tracing.TraceEvent (2.0.62) - System.Runtime.CompilerServices.Unsafe (>= 4.5.2) - Microsoft.DotNet.InternalAbstractions (1.0) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Gee.External.Capstone (2.3) + Iced (1.21) + Microsoft.Bcl.AsyncInterfaces (8.0) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net5.0) (>= net462)) (&& (== net5.0) (< netstandard2.1)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.CodeAnalysis.Analyzers (3.3.4) + Microsoft.CodeAnalysis.Common (4.11) + Microsoft.CodeAnalysis.Analyzers (>= 3.3.4) + System.Buffers (>= 4.5.1) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Collections.Immutable (>= 8.0) + System.Memory (>= 4.5.5) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Numerics.Vectors (>= 4.5) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Reflection.Metadata (>= 8.0) + System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Text.Encoding.CodePages (>= 7.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + Microsoft.CodeAnalysis.CSharp (4.11) + Microsoft.CodeAnalysis.Analyzers (>= 3.3.4) + Microsoft.CodeAnalysis.Common (4.11) + System.Buffers (>= 4.5.1) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Collections.Immutable (>= 8.0) + System.Memory (>= 4.5.5) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Numerics.Vectors (>= 4.5) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Reflection.Metadata (>= 8.0) + System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Text.Encoding.CodePages (>= 7.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + Microsoft.CodeCoverage (16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Microsoft.Diagnostics.NETCore.Client (0.2.532401) + Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + Microsoft.Extensions.Logging.Abstractions (>= 6.0.4) + System.Buffers (>= 4.5.1) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + Microsoft.Diagnostics.Runtime (3.1.512801) + Microsoft.Diagnostics.NETCore.Client (>= 0.2.410101) + System.Collections.Immutable (>= 6.0) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + Microsoft.Diagnostics.Tracing.TraceEvent (3.1.15) + Microsoft.Diagnostics.NETCore.Client (>= 0.2.510501) + Microsoft.Win32.Registry (>= 5.0) + System.Collections.Immutable (>= 8.0) + System.Reflection.Metadata (>= 8.0) + System.Reflection.TypeExtensions (>= 4.7) + System.Runtime.CompilerServices.Unsafe (>= 6.0) + Microsoft.DotNet.InternalAbstractions (1.0) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.AppContext (>= 4.1) System.Collections (>= 4.0.11) System.IO (>= 4.1) @@ -85,65 +101,73 @@ NUGET System.Runtime.Extensions (>= 4.1) System.Runtime.InteropServices (>= 4.1) System.Runtime.InteropServices.RuntimeInformation (>= 4.0) - Microsoft.DotNet.PlatformAbstractions (3.1) + Microsoft.DotNet.PlatformAbstractions (3.1.6) + Microsoft.Extensions.DependencyInjection.Abstractions (8.0.1) + Microsoft.Bcl.AsyncInterfaces (>= 8.0) - restriction: || (&& (== net5.0) (>= net462)) (&& (== net5.0) (< netstandard2.1)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net5.0) (>= net462)) (&& (== net5.0) (< netstandard2.1)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Extensions.Logging.Abstractions (8.0.1) + Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.1) + System.Buffers (>= 4.5.1) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.5) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) Microsoft.NET.Test.Sdk (16.4) - Microsoft.CodeCoverage (>= 16.4) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) - Microsoft.TestPlatform.TestHost (>= 16.4) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Microsoft.CodeCoverage (>= 16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Microsoft.TestPlatform.TestHost (>= 16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) Microsoft.NETCore.Platforms (3.1) - Microsoft.NETCore.Targets (3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.TestPlatform.ObjectModel (16.4) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Microsoft.NETCore.Targets (3.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.TestPlatform.ObjectModel (16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) NuGet.Frameworks (>= 5.0) - Microsoft.TestPlatform.TestHost (16.4) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) - Microsoft.TestPlatform.ObjectModel (>= 16.4) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.0) (>= uap10.0)) - Newtonsoft.Json (>= 9.0.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.0) (>= uap10.0)) - Microsoft.Win32.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.TestPlatform.TestHost (16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Microsoft.TestPlatform.ObjectModel (>= 16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.0) (>= uap10.0)) + Newtonsoft.Json (>= 9.0.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.0) (>= uap10.0)) + Microsoft.Win32.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - Microsoft.Win32.Registry (4.7) - System.Buffers (>= 4.5) - restriction: || (&& (== net5.0) (>= monoandroid) (< netstandard1.3)) (&& (== net5.0) (>= monotouch)) (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (>= xamarinios)) (&& (== net5.0) (>= xamarinmac)) (&& (== net5.0) (>= xamarintvos)) (&& (== net5.0) (>= xamarinwatchos)) (&& (== net6.0) (>= monoandroid) (< netstandard1.3)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Memory (>= 4.5.3) - restriction: || (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (< netcoreapp2.1)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0) - System.Security.AccessControl (>= 4.7) - System.Security.Principal.Windows (>= 4.7) + Microsoft.Win32.Registry (5.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net5.0) (>= monoandroid) (< netstandard1.3)) (&& (== net5.0) (>= monotouch)) (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (>= xamarinios)) (&& (== net5.0) (>= xamarinmac)) (&& (== net5.0) (>= xamarintvos)) (&& (== net5.0) (>= xamarinwatchos)) (&& (== net6.0) (>= monoandroid) (< netstandard1.3)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net8.0) (>= monoandroid) (< netstandard1.3)) (&& (== net8.0) (>= monotouch)) (&& (== net8.0) (< netcoreapp2.0)) (&& (== net8.0) (>= xamarinios)) (&& (== net8.0) (>= xamarinmac)) (&& (== net8.0) (>= xamarintvos)) (&& (== net8.0) (>= xamarinwatchos)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (< netcoreapp2.1)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (>= uap10.1)) (&& (== net8.0) (< netcoreapp2.0)) (&& (== net8.0) (< netcoreapp2.1)) (&& (== net8.0) (>= uap10.1)) (== netstandard2.0) + System.Security.AccessControl (>= 5.0) + System.Security.Principal.Windows (>= 5.0) NETStandard.Library (2.0.3) Microsoft.NETCore.Platforms (>= 1.1) - Newtonsoft.Json (12.0.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) - NuGet.Frameworks (5.4) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Newtonsoft.Json (12.0.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) + NuGet.Frameworks (5.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) NUnit (3.12) NETStandard.Library (>= 2.0) NUnit3TestAdapter (3.15.1) - Microsoft.DotNet.InternalAbstractions (>= 1.0) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.ComponentModel.EventBasedAsync (>= 4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.ComponentModel.TypeConverter (>= 4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.Diagnostics.Process (>= 4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.Reflection (>= 4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.Runtime.InteropServices.RuntimeInformation (>= 4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.Threading.Thread (>= 4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.Xml.XmlDocument (>= 4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.Xml.XPath.XmlDocument (>= 4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Perfolizer (0.2.1) - System.Memory (>= 4.5.3) - runtime.native.System (4.3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.DotNet.InternalAbstractions (>= 1.0) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.ComponentModel.EventBasedAsync (>= 4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.ComponentModel.TypeConverter (>= 4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Diagnostics.Process (>= 4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Reflection (>= 4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Runtime.InteropServices.RuntimeInformation (>= 4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Threading.Thread (>= 4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Xml.XmlDocument (>= 4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Xml.XPath.XmlDocument (>= 4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Perfolizer (0.3.17) + System.Memory (>= 4.5.5) - restriction: || (&& (== net5.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) + runtime.native.System (4.3.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) - System.AppContext (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.AppContext (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Runtime (>= 4.3) - System.Buffers (4.5.1) - System.CodeDom (4.7) - System.Collections (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Buffers (4.5.1) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (>= monoandroid) (< netstandard1.3)) (&& (== net8.0) (>= monotouch)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (&& (== net8.0) (< netcoreapp2.0)) (== netstandard2.0) + System.CodeDom (8.0) + System.Collections (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Collections.Immutable (5.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netcoreapp2.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0) - System.Collections.NonGeneric (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Collections.Immutable (8.0) + System.Memory (>= 4.5.5) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Collections.NonGeneric (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Diagnostics.Debug (>= 4.3) System.Globalization (>= 4.3) System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) System.Runtime.Extensions (>= 4.3) System.Threading (>= 4.3) - System.Collections.Specialized (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Collections.Specialized (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Collections.NonGeneric (>= 4.3) System.Globalization (>= 4.3) System.Globalization.Extensions (>= 4.3) @@ -151,18 +175,18 @@ NUGET System.Runtime (>= 4.3) System.Runtime.Extensions (>= 4.3) System.Threading (>= 4.3) - System.ComponentModel (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.ComponentModel (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Runtime (>= 4.3) - System.ComponentModel.EventBasedAsync (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.ComponentModel.EventBasedAsync (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) System.Threading (>= 4.3) System.Threading.Tasks (>= 4.3) - System.ComponentModel.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.ComponentModel.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.ComponentModel (>= 4.3) System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) - System.ComponentModel.TypeConverter (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.ComponentModel.TypeConverter (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Collections (>= 4.3) System.Collections.NonGeneric (>= 4.3) System.Collections.Specialized (>= 4.3) @@ -178,11 +202,11 @@ NUGET System.Runtime (>= 4.3) System.Runtime.Extensions (>= 4.3) System.Threading (>= 4.3) - System.Diagnostics.Debug (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Diagnostics.Debug (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Diagnostics.Process (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Diagnostics.Process (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.Win32.Primitives (>= 4.3) Microsoft.Win32.Registry (>= 4.3) @@ -204,24 +228,24 @@ NUGET System.Threading.Tasks (>= 4.3) System.Threading.Thread (>= 4.3) System.Threading.ThreadPool (>= 4.3) - System.Globalization (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Globalization (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Globalization.Extensions (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Globalization.Extensions (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) System.Globalization (>= 4.3) System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) System.Runtime.Extensions (>= 4.3) System.Runtime.InteropServices (>= 4.3) - System.IO (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.IO (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) System.Text.Encoding (>= 4.3) System.Threading.Tasks (>= 4.3) - System.IO.FileSystem (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.IO.FileSystem (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.IO (>= 4.3) @@ -230,72 +254,71 @@ NUGET System.Runtime.Handles (>= 4.3) System.Text.Encoding (>= 4.3) System.Threading.Tasks (>= 4.3) - System.IO.FileSystem.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.IO.FileSystem.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Runtime (>= 4.3) - System.Linq (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Linq (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Collections (>= 4.3) System.Diagnostics.Debug (>= 4.3) System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) System.Runtime.Extensions (>= 4.3) - System.Management (4.7) - Microsoft.NETCore.Platforms (>= 3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.0)) - Microsoft.Win32.Registry (>= 4.7) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.0)) - System.CodeDom (>= 4.7) - System.Memory (4.5.4) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net5.0) (>= monotouch)) (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= xamarinios)) (&& (== net5.0) (>= xamarinmac)) (&& (== net5.0) (>= xamarintvos)) (&& (== net5.0) (>= xamarinwatchos)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net5.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.0)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net5.0) (>= monotouch)) (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (< netcoreapp2.1)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= uap10.1)) (&& (== net5.0) (>= xamarinios)) (&& (== net5.0) (>= xamarinmac)) (&& (== net5.0) (>= xamarintvos)) (&& (== net5.0) (>= xamarinwatchos)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (4.5) - restriction: || (&& (== net5.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.0)) (== netstandard2.0) - System.Reflection (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Management (8.0) + System.CodeDom (>= 8.0) + System.Memory (4.5.5) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net5.0) (>= monotouch)) (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= xamarinios)) (&& (== net5.0) (>= xamarinmac)) (&& (== net5.0) (>= xamarintvos)) (&& (== net5.0) (>= xamarinwatchos)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net8.0) (>= monotouch)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< netcoreapp2.0)) (&& (== net8.0) (< netstandard1.1)) (&& (== net8.0) (< netstandard2.0)) (&& (== net8.0) (>= xamarinios)) (&& (== net8.0) (>= xamarinmac)) (&& (== net8.0) (>= xamarintvos)) (&& (== net8.0) (>= xamarinwatchos)) (== netstandard2.0) + System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net5.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net8.0) (< netcoreapp2.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net5.0) (>= monotouch)) (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (< netcoreapp2.1)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= uap10.1)) (&& (== net5.0) (>= xamarinios)) (&& (== net5.0) (>= xamarinmac)) (&& (== net5.0) (>= xamarintvos)) (&& (== net5.0) (>= xamarinwatchos)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net8.0) (>= monotouch)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< netcoreapp2.0)) (&& (== net8.0) (< netcoreapp2.1)) (&& (== net8.0) (< netstandard1.1)) (&& (== net8.0) (< netstandard2.0)) (&& (== net8.0) (>= uap10.1)) (&& (== net8.0) (>= xamarinios)) (&& (== net8.0) (>= xamarinmac)) (&& (== net8.0) (>= xamarintvos)) (&& (== net8.0) (>= xamarinwatchos)) (== netstandard2.0) + System.Numerics.Vectors (4.5) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (&& (== net8.0) (< netcoreapp2.0)) (== netstandard2.0) + System.Reflection (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.IO (>= 4.3) System.Reflection.Primitives (>= 4.3) System.Runtime (>= 4.3) - System.Reflection.Emit (4.7) - System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net5.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0) - System.Reflection.Emit.ILGeneration (4.7) - restriction: || (&& (== net5.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (< portable-net45+wp8)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0) - System.Reflection.Emit.Lightweight (4.6) - System.Reflection.Emit.ILGeneration (>= 4.6) - restriction: || (&& (== net5.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (< portable-net45+wp8)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (== netstandard2.0) - System.Reflection.Extensions (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Reflection.Emit (4.7) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net5.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net8.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net8.0) (< netstandard1.1)) (&& (== net8.0) (< netstandard2.0)) (&& (== net8.0) (>= uap10.1)) (== netstandard2.0) + System.Reflection.Emit.ILGeneration (4.7) - restriction: || (&& (== net5.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (< portable-net45+wp8)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (&& (== net8.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net8.0) (< netstandard1.1)) (&& (== net8.0) (< netstandard2.0)) (&& (== net8.0) (< portable-net45+wp8)) (&& (== net8.0) (>= uap10.1)) (== netstandard2.0) + System.Reflection.Emit.Lightweight (4.7) + System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net5.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (< portable-net45+wp8)) (&& (== net5.0) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (&& (== net8.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net8.0) (< netstandard2.0)) (&& (== net8.0) (< portable-net45+wp8)) (&& (== net8.0) (>= uap10.1)) (== netstandard2.0) + System.Reflection.Extensions (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Reflection (>= 4.3) System.Runtime (>= 4.3) - System.Reflection.Metadata (5.0) - System.Collections.Immutable (>= 5.0) - restriction: || (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netstandard1.1)) (&& (== net5.0) (< netstandard2.0)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< net5.0)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (== netstandard2.0) - System.Reflection.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Reflection.Metadata (8.0) + System.Collections.Immutable (>= 8.0) + System.Memory (>= 4.5.5) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Reflection.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Reflection.TypeExtensions (4.7) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.Resources.ResourceManager (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Reflection.TypeExtensions (4.7) + System.Resources.ResourceManager (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Globalization (>= 4.3) System.Reflection (>= 4.3) System.Runtime (>= 4.3) - System.Runtime (4.3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Runtime (4.3.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime.CompilerServices.Unsafe (5.0) - System.Runtime.Extensions (4.3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Runtime.CompilerServices.Unsafe (6.0) + System.Runtime.Extensions (4.3.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) System.Runtime (>= 4.3.1) - System.Runtime.Handles (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Runtime.Handles (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Runtime.InteropServices (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Runtime.InteropServices (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Reflection (>= 4.3) System.Reflection.Primitives (>= 4.3) System.Runtime (>= 4.3) System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices.RuntimeInformation (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Runtime.InteropServices.RuntimeInformation (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) runtime.native.System (>= 4.3) System.Reflection (>= 4.3) System.Reflection.Extensions (>= 4.3) @@ -303,40 +326,38 @@ NUGET System.Runtime (>= 4.3) System.Runtime.InteropServices (>= 4.3) System.Threading (>= 4.3) - System.Security.AccessControl (4.7) - Microsoft.NETCore.Platforms (>= 3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.0)) - System.Security.Principal.Windows (>= 4.7) - System.Security.Principal.Windows (4.7) - System.Text.Encoding (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Security.AccessControl (6.0.1) - restriction: || (== net5.0) (&& (== net8.0) (>= monoandroid) (< netstandard1.3)) (&& (== net8.0) (>= monotouch)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< netcoreapp2.0)) (&& (== net8.0) (>= uap10.1)) (== netstandard2.0) + System.Security.Principal.Windows (>= 5.0) - restriction: || (== net5.0) (&& (== net6.0) (>= net461)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Security.Principal.Windows (5.0) - restriction: || (== net5.0) (&& (== net8.0) (>= monoandroid) (< netstandard1.3)) (&& (== net8.0) (>= monotouch)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< netcoreapp2.0)) (&& (== net8.0) (>= uap10.1)) (== netstandard2.0) + System.Text.Encoding (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Text.Encoding.CodePages (4.7) - Microsoft.NETCore.Platforms (>= 3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp2.0)) (&& (== netstandard2.0) (>= netcoreapp3.1)) - System.Runtime.CompilerServices.Unsafe (>= 4.7) - restriction: || (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netcoreapp2.0)) (&& (== net5.0) (< netcoreapp3.1)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Text.Encoding.Extensions (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Text.Encoding.CodePages (8.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Memory (>= 4.5.5) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Text.Encoding.Extensions (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) System.Text.Encoding (>= 4.3) - System.Text.RegularExpressions (4.3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Text.RegularExpressions (4.3.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Runtime (>= 4.3.1) - System.Threading (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Threading (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Runtime (>= 4.3) System.Threading.Tasks (>= 4.3) - System.Threading.Tasks (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Threading.Tasks (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Threading.Tasks.Extensions (4.5.3) - System.Runtime.CompilerServices.Unsafe (>= 4.5.2) - restriction: || (&& (== net5.0) (>= net45)) (&& (== net5.0) (< netcoreapp2.1)) (&& (== net5.0) (< netstandard1.0)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= wp8)) (&& (== net6.0) (>= net45)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.0)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= wp8)) (== netstandard2.0) - System.Threading.Thread (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Threading.Tasks.Extensions (4.5.4) + System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net5.0) (>= net461)) (&& (== net5.0) (< netcoreapp2.1)) (&& (== net5.0) (< netstandard1.0)) (&& (== net5.0) (< netstandard2.0)) (&& (== net5.0) (>= wp8)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.0)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= wp8)) (&& (== net8.0) (>= net461)) (&& (== net8.0) (< netcoreapp2.1)) (&& (== net8.0) (< netstandard1.0)) (&& (== net8.0) (< netstandard2.0)) (&& (== net8.0) (>= wp8)) (== netstandard2.0) + System.Threading.Thread (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Runtime (>= 4.3) - System.Threading.ThreadPool (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Threading.ThreadPool (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Runtime (>= 4.3) System.Runtime.Handles (>= 4.3) - System.ValueTuple (4.5) - System.Xml.ReaderWriter (4.3.1) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Xml.ReaderWriter (4.3.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Collections (>= 4.3) System.Diagnostics.Debug (>= 4.3) System.Globalization (>= 4.3) @@ -352,7 +373,7 @@ NUGET System.Text.RegularExpressions (>= 4.3) System.Threading.Tasks (>= 4.3) System.Threading.Tasks.Extensions (>= 4.3) - System.Xml.XmlDocument (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Xml.XmlDocument (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Collections (>= 4.3) System.Diagnostics.Debug (>= 4.3) System.Globalization (>= 4.3) @@ -363,7 +384,7 @@ NUGET System.Text.Encoding (>= 4.3) System.Threading (>= 4.3) System.Xml.ReaderWriter (>= 4.3) - System.Xml.XPath (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Xml.XPath (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Collections (>= 4.3) System.Diagnostics.Debug (>= 4.3) System.Globalization (>= 4.3) @@ -373,7 +394,7 @@ NUGET System.Runtime.Extensions (>= 4.3) System.Threading (>= 4.3) System.Xml.ReaderWriter (>= 4.3) - System.Xml.XPath.XmlDocument (4.3) - restriction: || (== net5.0) (== net6.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + System.Xml.XPath.XmlDocument (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) System.Collections (>= 4.3) System.Globalization (>= 4.3) System.IO (>= 4.3) diff --git a/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj b/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj index 8e86046..fee8f4c 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj +++ b/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 false true From acb174dc39692379bc279c9a8324dca10c98b271 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 8 Sep 2024 13:22:54 +0200 Subject: [PATCH 04/65] updated Newtonsoft.Json --- paket.lock | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/paket.lock b/paket.lock index c7cda2f..3336d97 100644 --- a/paket.lock +++ b/paket.lock @@ -22,22 +22,26 @@ NUGET System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) BenchmarkDotNet.Annotations (0.14) CommandLineParser (2.9.1) - Fable.Browser.Blob (1.1) - Fable.Core (>= 3.0) - FSharp.Core (>= 4.6.2) + Fable.Browser.Blob (1.4) + Fable.Core (>= 3.2.8) + FSharp.Core (>= 4.7.2) Fable.Browser.Dom (1.1) Fable.Browser.Blob (>= 1.1) Fable.Browser.Event (>= 1.0) Fable.Browser.WebStorage (>= 1.0) Fable.Core (>= 3.0) FSharp.Core (>= 4.6.2) - Fable.Browser.Event (1.0) - Fable.Core (>= 3.0) - FSharp.Core (>= 4.5.2) - Fable.Browser.WebStorage (1.0) - Fable.Browser.Event (>= 1.0) - Fable.Core (>= 3.0) - FSharp.Core (>= 4.5.2) + Fable.Browser.Event (1.6) + Fable.Browser.Gamepad (>= 1.3) + Fable.Core (>= 3.2.8) + FSharp.Core (>= 4.7.2) + Fable.Browser.Gamepad (1.3) + Fable.Core (>= 3.2.8) + FSharp.Core (>= 4.7.2) + Fable.Browser.WebStorage (1.3) + Fable.Browser.Event (>= 1.6) + Fable.Core (>= 3.2.8) + FSharp.Core (>= 4.7.2) Fable.Core (3.2.9) FSharp.Core (>= 4.7.2) FsCheck (2.14.6) @@ -76,7 +80,7 @@ NUGET System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) System.Text.Encoding.CodePages (>= 7.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) - Microsoft.CodeCoverage (16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Microsoft.CodeCoverage (17.11.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) Microsoft.Diagnostics.NETCore.Client (0.2.532401) Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) Microsoft.Extensions.Logging.Abstractions (>= 6.0.4) @@ -112,13 +116,13 @@ NUGET Microsoft.NET.Test.Sdk (16.4) Microsoft.CodeCoverage (>= 16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) Microsoft.TestPlatform.TestHost (>= 16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) - Microsoft.NETCore.Platforms (3.1) - Microsoft.NETCore.Targets (3.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.TestPlatform.ObjectModel (16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) - NuGet.Frameworks (>= 5.0) - Microsoft.TestPlatform.TestHost (16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) - Microsoft.TestPlatform.ObjectModel (>= 16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.0) (>= uap10.0)) - Newtonsoft.Json (>= 9.0.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.0) (>= uap10.0)) + Microsoft.NETCore.Platforms (7.0.4) + Microsoft.NETCore.Targets (5.0) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.TestPlatform.ObjectModel (17.11.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + System.Reflection.Metadata (>= 1.6) + Microsoft.TestPlatform.TestHost (17.11.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Microsoft.TestPlatform.ObjectModel (>= 17.11.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + Newtonsoft.Json (>= 13.0.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) Microsoft.Win32.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -130,8 +134,7 @@ NUGET System.Security.Principal.Windows (>= 5.0) NETStandard.Library (2.0.3) Microsoft.NETCore.Platforms (>= 1.1) - Newtonsoft.Json (12.0.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) - NuGet.Frameworks (5.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) + Newtonsoft.Json (13.0.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) NUnit (3.12) NETStandard.Library (>= 2.0) NUnit3TestAdapter (3.15.1) From b34cfa78267efd0d963f9da65e443c2be375fc57 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 8 Sep 2024 13:41:31 +0200 Subject: [PATCH 05/65] added tryGetV and tryRemoveV to IndexList module --- .../Datastructures/IndexList.fs | 8 +++++++ .../Datastructures/MapExt.fs | 23 ++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/IndexList.fs b/src/FSharp.Data.Adaptive/Datastructures/IndexList.fs index 9fb9251..65fb633 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/IndexList.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/IndexList.fs @@ -830,6 +830,10 @@ module IndexList = let inline tryRemove (index : Index) (list : IndexList<'T>) = list.TryRemove(index) + /// Removes the entry associated to the given index, returns the (optional) value and the list without the specific element. + let inline tryRemoveV (index : Index) (list : IndexList<'T>) = + list.TryRemoveV(index) + /// Finds the optional neighbour elements in the list for the given index. let inline neighbours (index : Index) (list : IndexList<'T>) = list.Neighbours(index) @@ -862,6 +866,10 @@ module IndexList = let inline tryGet (index : Index) (list : IndexList<'T>) = list.TryGet index + /// gets the element for the given index (if any). + let inline tryGetV (index : Index) (list : IndexList<'T>) = + list.TryGetV index + /// adds, deletes or updates the element for the given index. /// the update functions gets the optional old value and may optionally return /// a new value (or None for deleting the entry). diff --git a/src/FSharp.Data.Adaptive/Datastructures/MapExt.fs b/src/FSharp.Data.Adaptive/Datastructures/MapExt.fs index 93e9ec5..1ac8515 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/MapExt.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/MapExt.fs @@ -429,19 +429,19 @@ module internal MapExtImplementation = let rec tryGetValue (cmp : IComparer<'Key>) (key : 'Key) (node : Node<'Key, 'Value>) = if isNull node then - struct(false, Unchecked.defaultof<'Value>) + ValueNone elif node.Height = 1uy then if cmp.Compare(key, node.Key) = 0 then - struct(true, node.Value) + ValueSome node.Value else - struct(false, Unchecked.defaultof<'Value>) + ValueNone else let node = node :?> Inner<'Key, 'Value> let c = cmp.Compare(key, node.Key) if c > 0 then tryGetValue cmp key node.Right elif c < 0 then tryGetValue cmp key node.Left - else - struct(true, node.Value) + else + ValueSome node.Value let rec tryFind (cmp : IComparer<'Key>) (key : 'Key) (node : Node<'Key, 'Value>) = if isNull node then @@ -3041,20 +3041,17 @@ type internal MapExt<'Key, 'Value when 'Key : comparison>(comparer : IComparer<' #if !FABLE_COMPILER member x.TryGetValue(key : 'Key, [] value : byref<'Value>) = - let struct(ok, res) = MapExtImplementation.tryGetValue comparer key root - value <- res - ok + match MapExtImplementation.tryGetValue comparer key root with + | ValueSome res -> value <- res + true + | ValueNone -> false #endif member x.TryFind(key : 'Key) = MapExtImplementation.tryFind comparer key root member x.TryFindV(key : 'Key) = - let struct(ok, res) = MapExtImplementation.tryGetValue comparer key root - if ok then - ValueSome res - else - ValueNone + MapExtImplementation.tryGetValue comparer key root member x.ContainsKey(key : 'Key) = MapExtImplementation.containsKey comparer key root From 19f068316cfa0def68cb3170d7ac86c35da5198e Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 8 Sep 2024 23:24:25 +0200 Subject: [PATCH 06/65] added IndexListBenchmarks (tryGet vs tryGetV) --- .../Benchmarks/IndexListBenchmarks.fs | 187 ++++++++++++++++++ .../FSharp.Data.Adaptive.Tests.fsproj | 1 + .../FSharp.Data.Adaptive.Tests/Program.fs | 6 +- 3 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexListBenchmarks.fs diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexListBenchmarks.fs b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexListBenchmarks.fs new file mode 100644 index 0000000..a32a653 --- /dev/null +++ b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexListBenchmarks.fs @@ -0,0 +1,187 @@ +namespace Benchmarks + +open BenchmarkDotNet.Attributes +open FSharp.Data.Adaptive +open System +open BenchmarkDotNet.Configs +open BenchmarkDotNet.Jobs + +//BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4780/22H2/2022Update) +//Intel Core i7-8700K CPU 3.70GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores +//.NET SDK 8.0.400 +// [Host] : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 DEBUG +// DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 + +//| Method | Categories | Count | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | +//|--------------- |---------------- |------ |----------------:|--------------:|-------------:|------:|--------:|----------:|------------:| +//| TryGet_32Val | (1) 4 byte val | 1 | 48.25 ns | 0.096 ns | 0.080 ns | 1.00 | 0.0038 | 24 B | 1.00 | +//| TryGetV_32Val | (1) 4 byte val | 1 | 50.65 ns | 0.151 ns | 0.134 ns | 1.05 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_32Val | (1) 4 byte val | 10 | 354.84 ns | 0.928 ns | 0.868 ns | 1.00 | 0.0114 | 72 B | 1.00 | +//| TryGetV_32Val | (1) 4 byte val | 10 | 365.27 ns | 1.098 ns | 1.027 ns | 1.03 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_32Val | (1) 4 byte val | 500 | 50,223.13 ns | 167.372 ns | 148.371 ns | 1.00 | 0.5493 | 3552 B | 1.00 | +//| TryGetV_32Val | (1) 4 byte val | 500 | 51,237.16 ns | 158.610 ns | 140.603 ns | 1.02 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_32Val | (1) 4 byte val | 10000 | 1,735,799.18 ns | 5,738.214 ns | 5,367.529 ns | 1.00 | 11.7188 | 78625 B | 1.000 | +//| TryGetV_32Val | (1) 4 byte val | 10000 | 1,755,901.65 ns | 10,525.554 ns | 9,845.610 ns | 1.01 | - | 1 B | 0.000 | +//| | | | | | | | | | | +//| TryGet_64Val | (2) 8 byte val | 1 | 47.93 ns | 0.125 ns | 0.117 ns | 1.00 | 0.0038 | 24 B | 1.00 | +//| TryGetV_64Val | (2) 8 byte val | 1 | 47.40 ns | 0.258 ns | 0.216 ns | 0.99 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_64Val | (2) 8 byte val | 10 | 361.18 ns | 1.473 ns | 1.306 ns | 1.00 | 0.0114 | 72 B | 1.00 | +//| TryGetV_64Val | (2) 8 byte val | 10 | 353.09 ns | 1.231 ns | 1.092 ns | 0.98 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_64Val | (2) 8 byte val | 500 | 50,480.46 ns | 138.964 ns | 123.188 ns | 1.00 | 0.5493 | 3552 B | 1.00 | +//| TryGetV_64Val | (2) 8 byte val | 500 | 50,533.77 ns | 142.511 ns | 126.332 ns | 1.00 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_64Val | (2) 8 byte val | 10000 | 1,737,443.82 ns | 7,825.304 ns | 7,319.794 ns | 1.00 | 11.7188 | 78625 B | 1.000 | +//| TryGetV_64Val | (2) 8 byte val | 10000 | 1,737,081.43 ns | 6,858.967 ns | 6,415.882 ns | 1.00 | - | 1 B | 0.000 | +//| | | | | | | | | | | +//| TryGet_128Val | (3) 16 byte val | 1 | 48.46 ns | 0.191 ns | 0.179 ns | 1.00 | 0.0051 | 32 B | 1.00 | +//| TryGetV_128Val | (3) 16 byte val | 1 | 47.94 ns | 0.291 ns | 0.273 ns | 0.99 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_128Val | (3) 16 byte val | 10 | 353.66 ns | 1.426 ns | 1.264 ns | 1.00 | 0.0153 | 96 B | 1.00 | +//| TryGetV_128Val | (3) 16 byte val | 10 | 351.66 ns | 0.971 ns | 0.909 ns | 0.99 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_128Val | (3) 16 byte val | 500 | 51,943.24 ns | 181.379 ns | 160.788 ns | 1.00 | 0.7324 | 4736 B | 1.00 | +//| TryGetV_128Val | (3) 16 byte val | 500 | 50,406.11 ns | 167.637 ns | 148.606 ns | 0.97 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_128Val | (3) 16 byte val | 10000 | 1,737,977.43 ns | 7,508.261 ns | 6,269.738 ns | 1.00 | 15.6250 | 104833 B | 1.000 | +//| TryGetV_128Val | (3) 16 byte val | 10000 | 1,737,448.34 ns | 2,927.987 ns | 2,285.979 ns | 1.00 | - | 1 B | 0.000 | +//| | | | | | | | | | | +//| TryGet_Ref | (4) ref | 1 | 49.51 ns | 0.131 ns | 0.109 ns | 1.00 | 0.0038 | 24 B | 1.00 | +//| TryGetV_Ref | (4) ref | 1 | 59.33 ns | 0.221 ns | 0.184 ns | 1.20 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_Ref | (4) ref | 10 | 356.60 ns | 1.158 ns | 1.083 ns | 1.00 | 0.0114 | 72 B | 1.00 | +//| TryGetV_Ref | (4) ref | 10 | 388.15 ns | 0.983 ns | 0.920 ns | 1.09 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_Ref | (4) ref | 500 | 50,594.42 ns | 134.428 ns | 125.744 ns | 1.00 | 0.5493 | 3552 B | 1.00 | +//| TryGetV_Ref | (4) ref | 500 | 52,137.39 ns | 180.493 ns | 160.002 ns | 1.03 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_Ref | (4) ref | 10000 | 1,738,875.00 ns | 5,090.775 ns | 4,761.914 ns | 1.00 | 11.7188 | 78625 B | 1.000 | +//| TryGetV_Ref | (4) ref | 10000 | 1,777,254.84 ns | 5,872.989 ns | 5,493.597 ns | 1.02 | - | 1 B | 0.000 | + +[] +[] +type IndexListBenchmarks() = + + [] + val mutable public Count : int + + let mutable indices32 = Array.zeroCreate 0 + let mutable ilist32 = IndexList.empty<_> + + let mutable indices64 = Array.zeroCreate 0 + let mutable ilist64 = IndexList.empty<_> + + let mutable indices128 = Array.zeroCreate 0 + let mutable ilist128 = IndexList.empty<_> + + let mutable indicesRef = Array.zeroCreate 0 + let mutable ilistRef = IndexList.empty<_> + + [] + member x.Setup() = + let rnd = Random(101) + let stuff = Array.init x.Count (fun i -> i) + ilist32 <- IndexList.ofArray stuff + let mutable some = stuff |> Array.where (fun _ -> rnd.NextDouble() < 0.33) + if some.Length = 0 then some <- [| Array.head stuff |] + indices32 <- some |> Array.map (fun o -> IndexList.findIndex o ilist32) + + let rnd = Random(101) + let stuff = Array.init x.Count (fun i -> struct(i, i*i)) + ilist64 <- IndexList.ofArray stuff + let mutable some = stuff |> Array.where (fun _ -> rnd.NextDouble() < 0.33) + if some.Length = 0 then some <- [| Array.head stuff |] + indices64 <- some |> Array.map (fun o -> IndexList.findIndex o ilist64) + + let rnd = Random(101) + let stuff = Array.init x.Count (fun i -> struct(int64 i, int64 (i * i))) + ilist128 <- IndexList.ofArray stuff + let mutable some = stuff |> Array.where (fun _ -> rnd.NextDouble() < 0.33) + if some.Length = 0 then some <- [| Array.head stuff |] + indices128 <- some |> Array.map (fun o -> IndexList.findIndex o ilist128) + + let rnd = Random(101) + let stuff = Array.init x.Count (fun i -> Object()) + ilistRef <- IndexList.ofArray stuff + let mutable some = stuff |> Array.where (fun _ -> rnd.NextDouble() < 0.33) + if some.Length = 0 then some <- [| Array.head stuff |] + indicesRef <- some |> Array.map (fun o -> IndexList.findIndex o ilistRef) + + + [] + member x.TryGet_32Val() = + let mutable res = 0 + for i in indices32 do + match IndexList.tryGet i ilist32 with + | Some l -> res <- res ^^^ l + | None -> () + res + + [] + member x.TryGetV_32Val() = + let mutable res = 0 + for i in indices32 do + match IndexList.tryGetV i ilist32 with + | ValueSome l -> res <- res ^^^ l + | ValueNone -> () + res + + [] + member x.TryGet_64Val() = + let mutable res = 0 + for i in indices64 do + match IndexList.tryGet i ilist64 with + | Some (l,l2) -> res <- (res + l) ^^^ l2 + | None -> () + res + + [] + member x.TryGetV_64Val() = + let mutable res = 0 + for i in indices64 do + match IndexList.tryGetV i ilist64 with + | ValueSome (l,l2) -> res <- (res + l) ^^^ l2 + | ValueNone -> () + res + + [] + member x.TryGet_128Val() = + let mutable res = 0L + for i in indices128 do + match IndexList.tryGet i ilist128 with + | Some (l,l2) -> res <- (res + l) ^^^ l2 + | None -> () + res + + [] + member x.TryGetV_128Val() = + let mutable res = 0L + for i in indices128 do + match IndexList.tryGetV i ilist128 with + | ValueSome (l,l2) -> res <- (res + l) ^^^ l2 + | ValueNone -> () + res + + [] + member x.TryGet_Ref() = + let mutable res = 0 + for i in indicesRef do + match IndexList.tryGet i ilistRef with + | Some o -> res <- res ^^^ o.GetHashCode() + | None -> () + res + + [] + member x.TryGetV_Ref() = + let mutable res = 0 + for i in indicesRef do + match IndexList.tryGetV i ilistRef with + | ValueSome o -> res <- res ^^^ o.GetHashCode() + | ValueNone -> () + res + + diff --git a/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj b/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj index fee8f4c..11c51a5 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj +++ b/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj @@ -25,6 +25,7 @@ + diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Program.fs b/src/Test/FSharp.Data.Adaptive.Tests/Program.fs index 31af4c4..df463ad 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/Program.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/Program.fs @@ -48,7 +48,7 @@ let ``[AList] sub``() = [] let main _args = - ASet.``[ASet] mapA/flattenA/chooseA async``() + //ASet.``[ASet] mapA/flattenA/chooseA async``() //``[AList] sub``(); @@ -75,7 +75,7 @@ let main _args = //Profile.run() //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore - //BenchmarkRunner.Run() |> ignore + //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore @@ -87,7 +87,7 @@ let main _args = //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore - //BenchmarkRunner.Run() |> ignore + BenchmarkRunner.Run() |> ignore 0 From 358264358310c944850ffe032ebe9b4c6033aa6b Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 00:33:27 +0200 Subject: [PATCH 07/65] added benchmark results with FSharp.Core 8.0.400 --- .../Benchmarks/IndexListBenchmarks.fs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexListBenchmarks.fs b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexListBenchmarks.fs index a32a653..9314585 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexListBenchmarks.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexListBenchmarks.fs @@ -12,6 +12,8 @@ open BenchmarkDotNet.Jobs // [Host] : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 DEBUG // DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 +// FSharp.Core 4.7.2 + //| Method | Categories | Count | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | //|--------------- |---------------- |------ |----------------:|--------------:|-------------:|------:|--------:|----------:|------------:| //| TryGet_32Val | (1) 4 byte val | 1 | 48.25 ns | 0.096 ns | 0.080 ns | 1.00 | 0.0038 | 24 B | 1.00 | @@ -62,6 +64,60 @@ open BenchmarkDotNet.Jobs //| TryGet_Ref | (4) ref | 10000 | 1,738,875.00 ns | 5,090.775 ns | 4,761.914 ns | 1.00 | 11.7188 | 78625 B | 1.000 | //| TryGetV_Ref | (4) ref | 10000 | 1,777,254.84 ns | 5,872.989 ns | 5,493.597 ns | 1.02 | - | 1 B | 0.000 | + +// FSharp.Core 8.0.400 + +//| Method | Categories | Count | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | +//|--------------- |---------------- |------ |----------------:|--------------:|--------------:|------:|--------:|----------:|------------:| +//| TryGet_32Val | (1) 4 byte val | 1 | 50.36 ns | 0.159 ns | 0.141 ns | 1.00 | 0.0038 | 24 B | 1.00 | +//| TryGetV_32Val | (1) 4 byte val | 1 | 52.64 ns | 0.263 ns | 0.233 ns | 1.05 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_32Val | (1) 4 byte val | 10 | 373.85 ns | 1.596 ns | 1.493 ns | 1.00 | 0.0114 | 72 B | 1.00 | +//| TryGetV_32Val | (1) 4 byte val | 10 | 383.20 ns | 1.575 ns | 1.473 ns | 1.03 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_32Val | (1) 4 byte val | 500 | 52,669.21 ns | 106.041 ns | 88.549 ns | 1.00 | 0.5493 | 3552 B | 1.00 | +//| TryGetV_32Val | (1) 4 byte val | 500 | 53,925.39 ns | 263.128 ns | 233.256 ns | 1.02 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_32Val | (1) 4 byte val | 10000 | 1,817,925.74 ns | 3,685.042 ns | 3,446.990 ns | 1.00 | 11.7188 | 78625 B | 1.000 | +//| TryGetV_32Val | (1) 4 byte val | 10000 | 1,836,068.14 ns | 5,914.893 ns | 5,532.794 ns | 1.01 | - | 1 B | 0.000 | +//| | | | | | | | | | | +//| TryGet_64Val | (2) 8 byte val | 1 | 49.99 ns | 0.272 ns | 0.255 ns | 1.00 | 0.0038 | 24 B | 1.00 | +//| TryGetV_64Val | (2) 8 byte val | 1 | 49.74 ns | 0.179 ns | 0.168 ns | 0.99 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_64Val | (2) 8 byte val | 10 | 374.82 ns | 2.226 ns | 1.973 ns | 1.00 | 0.0114 | 72 B | 1.00 | +//| TryGetV_64Val | (2) 8 byte val | 10 | 375.99 ns | 4.841 ns | 4.292 ns | 1.00 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_64Val | (2) 8 byte val | 500 | 52,818.04 ns | 400.418 ns | 374.551 ns | 1.00 | 0.5493 | 3552 B | 1.00 | +//| TryGetV_64Val | (2) 8 byte val | 500 | 52,724.39 ns | 200.392 ns | 177.642 ns | 1.00 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_64Val | (2) 8 byte val | 10000 | 1,811,588.00 ns | 3,736.670 ns | 3,120.289 ns | 1.00 | 11.7188 | 78625 B | 1.000 | +//| TryGetV_64Val | (2) 8 byte val | 10000 | 1,828,623.84 ns | 10,554.453 ns | 9,356.247 ns | 1.01 | - | 1 B | 0.000 | +//| | | | | | | | | | | +//| TryGet_128Val | (3) 16 byte val | 1 | 50.52 ns | 0.209 ns | 0.195 ns | 1.00 | 0.0051 | 32 B | 1.00 | +//| TryGetV_128Val | (3) 16 byte val | 1 | 49.49 ns | 0.186 ns | 0.165 ns | 0.98 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_128Val | (3) 16 byte val | 10 | 381.23 ns | 1.442 ns | 1.278 ns | 1.00 | 0.0153 | 96 B | 1.00 | +//| TryGetV_128Val | (3) 16 byte val | 10 | 372.11 ns | 1.559 ns | 1.217 ns | 0.98 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_128Val | (3) 16 byte val | 500 | 52,558.80 ns | 190.344 ns | 158.946 ns | 1.00 | 0.7324 | 4736 B | 1.00 | +//| TryGetV_128Val | (3) 16 byte val | 500 | 52,311.57 ns | 123.022 ns | 109.056 ns | 1.00 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_128Val | (3) 16 byte val | 10000 | 1,818,535.97 ns | 5,538.095 ns | 4,909.377 ns | 1.00 | 15.6250 | 104833 B | 1.000 | +//| TryGetV_128Val | (3) 16 byte val | 10000 | 1,832,633.29 ns | 19,122.349 ns | 17,887.058 ns | 1.01 | - | 1 B | 0.000 | +//| | | | | | | | | | | +//| TryGet_Ref | (4) ref | 1 | 51.60 ns | 0.178 ns | 0.149 ns | 1.00 | 0.0038 | 24 B | 1.00 | +//| TryGetV_Ref | (4) ref | 1 | 50.52 ns | 0.148 ns | 0.124 ns | 0.98 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_Ref | (4) ref | 10 | 378.22 ns | 1.390 ns | 1.300 ns | 1.00 | 0.0114 | 72 B | 1.00 | +//| TryGetV_Ref | (4) ref | 10 | 377.51 ns | 1.665 ns | 1.476 ns | 1.00 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_Ref | (4) ref | 500 | 52,517.15 ns | 115.626 ns | 96.553 ns | 1.00 | 0.5493 | 3552 B | 1.00 | +//| TryGetV_Ref | (4) ref | 500 | 52,825.04 ns | 169.184 ns | 141.277 ns | 1.01 | - | - | 0.00 | +//| | | | | | | | | | | +//| TryGet_Ref | (4) ref | 10000 | 1,823,792.83 ns | 8,345.416 ns | 7,397.994 ns | 1.00 | 11.7188 | 78625 B | 1.000 | +//| TryGetV_Ref | (4) ref | 10000 | 1,825,524.56 ns | 7,648.100 ns | 7,154.038 ns | 1.00 | - | 1 B | 0.000 | + + [] [] type IndexListBenchmarks() = From 2209ca9bfc7b9e8efc043b3545b456e2cde8d8e6 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 00:36:42 +0200 Subject: [PATCH 08/65] alist operations/readers: changed to IndexList tryGetV/tryRemoveV --- .../AdaptiveIndexList/AdaptiveIndexList.fs | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 71e809e..89e236b 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -55,25 +55,25 @@ module internal Reductions = let index, op = e.Current match op with | Set a -> - match IndexList.tryGet index state with - | Some old -> + match IndexList.tryGetV index state with + | ValueSome old -> match reduction.sub sum old with | ValueSome s -> sum <- s | ValueNone -> working <- false - | None -> + | ValueNone -> () sum <- reduction.add sum a state <- IndexList.set index a state | Remove -> - match IndexList.tryRemove index state with - | Some(old, rest) -> + match IndexList.tryRemoveV index state with + | ValueSome(old, rest) -> state <- rest match reduction.sub sum old with | ValueSome s -> sum <- s | ValueNone -> working <- false - | None -> + | ValueNone -> () if not working then @@ -127,11 +127,11 @@ module internal Reductions = if reader.State.Count <= 2 || reader.State.Count <= ops.Count then let newState = reader.State |> IndexList.mapi (fun k a -> - match IndexList.tryGet k state with - | Some (oa, b) -> + match IndexList.tryGetV k state with + | ValueSome (oa, b) -> if DefaultEquality.equals a oa then a, b else a, mapping k a - | None -> + | ValueNone -> let b = mapping k a a, b ) @@ -143,13 +143,13 @@ module internal Reductions = for (index, op) in ops do match op with | Set a -> - match IndexList.tryGet index state with - | Some (oa, _) when DefaultEquality.equals oa a -> + match IndexList.tryGetV index state with + | ValueSome (oa, _) when DefaultEquality.equals oa a -> () | _ -> - match IndexList.tryGet index state with - | Some(_,old) -> sum <- sub sum old - | None -> () + match IndexList.tryGetV index state with + | ValueSome(_,old) -> sum <- sub sum old + | ValueNone -> () let b = mapping index a @@ -157,11 +157,11 @@ module internal Reductions = state <- IndexList.set index (a,b) state | Remove -> - match IndexList.tryRemove index state with - | Some((_,old), rest) -> + match IndexList.tryRemoveV index state with + | ValueSome((_,old), rest) -> state <- rest sum <- sub sum old - | None -> + | ValueNone -> () match sum with @@ -234,15 +234,15 @@ module internal Reductions = #endif let removeIndex (x : AdaptiveReduceByValue<_,_,_,_>) (i : Index) = - match IndexList.tryRemove i state with - | Some ((_oa, ov, o), newState) -> + match IndexList.tryRemoveV i state with + | ValueSome ((_oa, ov, o), newState) -> state <- newState sum <- sub sum o let rem, newTargets = MultiSetMap.remove ov i targets targets <- newTargets if rem then ov.Outputs.Remove x |> ignore - | None -> + | ValueNone -> () override x.InputChangedObject(t, o) = @@ -275,8 +275,8 @@ module internal Reductions = let newState = reader.State |> IndexList.mapi (fun k a -> - match IndexList.tryGet k state with - | Some(oa, m,_) when DefaultEquality.equals oa a -> + match IndexList.tryGetV k state with + | ValueSome(oa, m,_) when DefaultEquality.equals oa a -> let v = m.GetValue t targets <- MultiSetMap.add m k targets (a, m, v) @@ -639,27 +639,27 @@ module internal AdaptiveIndexListImplementation = dirty <- IndexList.remove i dirty match op with | Set v -> - match IndexList.tryGet i old with - | Some ov -> + match IndexList.tryGetV i old with + | ValueSome ov -> let o = cache.Revoke(i, ov) let rem, rest = MultiSetMap.remove o i targets targets <- rest if rem then o.Outputs.Remove x |> ignore - | None -> + | ValueNone -> () let k = cache.Invoke(i,v) let v = k.GetValue t targets <- MultiSetMap.add k i targets Some (Set v) | Remove -> - match IndexList.tryGet i old with - | Some v -> + match IndexList.tryGetV i old with + | ValueSome v -> let o = cache.Revoke(i, v) let rem, r = MultiSetMap.remove o i targets targets <- r if rem then o.Outputs.Remove x |> ignore Some Remove - | None -> + | ValueNone -> None ) @@ -717,13 +717,13 @@ module internal AdaptiveIndexListImplementation = dirty <- IndexList.remove i dirty match op with | Set v -> - match IndexList.tryGet i old with - | Some ov -> + match IndexList.tryGetV i old with + | ValueSome ov -> let o = cache.Revoke(i, ov) let rem, rest = MultiSetMap.remove o i targets targets <- rest if rem then o.Outputs.Remove x |> ignore - | None -> + | ValueNone -> () let k = cache.Invoke(i,v) @@ -737,8 +737,8 @@ module internal AdaptiveIndexListImplementation = if keys.Remove i then Some Remove else None | Remove -> - match IndexList.tryGet i old with - | Some v -> + match IndexList.tryGetV i old with + | ValueSome v -> let o = cache.Revoke(i, v) let rem, rest = MultiSetMap.remove o i targets targets <- rest @@ -746,7 +746,7 @@ module internal AdaptiveIndexListImplementation = if keys.Remove i then Some Remove else None - | None -> + | ValueNone -> None ) @@ -1149,8 +1149,8 @@ module internal AdaptiveIndexListImplementation = for i, op in ops do match op with | Remove -> - match IndexList.tryGet i o with - | Some _ov -> + match IndexList.tryGetV i o with + | ValueSome _ov -> let (l, r) = neighbours i delta <- IndexListDelta.add i Remove delta match l with @@ -1162,12 +1162,12 @@ module internal AdaptiveIndexListImplementation = delta <- IndexListDelta.add li Remove delta | None -> () - | None -> + | ValueNone -> () | Set v -> let (l, r) = neighbours i - match IndexList.tryGet i o with - | Some ov when CheapEquality.cheapEqual ov v -> + match IndexList.tryGetV i o with + | ValueSome ov when CheapEquality.cheapEqual ov v -> () | _ -> match r with From 49fa2ac8ebb2346370d68c0ec013103a82e2f32b Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 00:55:13 +0200 Subject: [PATCH 09/65] alist readers: using struct tuples in internal caches --- .../AdaptiveIndexList/AdaptiveIndexList.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 89e236b..9fcb2df 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -601,7 +601,7 @@ module internal AdaptiveIndexListImplementation = let mapping = OptimizedClosures.FSharpFunc>.Adapt mapping let reader = input.GetReader() do reader.Tag <- "input" - let cache = Cache (fun (a,b) -> mapping.Invoke(a,b)) + let cache = Cache (fun struct(i,v) -> mapping.Invoke(i,v)) let mutable targets = MultiSetMap.empty, Index> let mutable dirty = IndexList.empty> @@ -679,7 +679,7 @@ module internal AdaptiveIndexListImplementation = do reader.Tag <- "input" let mapping = OptimizedClosures.FSharpFunc>>.Adapt mapping let keys = DefaultHashSet.create() - let cache = Cache (fun (a,b) -> mapping.Invoke(a,b)) + let cache = Cache (fun struct(i,v) -> mapping.Invoke(i,v)) let mutable targets = MultiSetMap.empty>, Index> let mutable dirty = IndexList.empty>> @@ -854,7 +854,7 @@ module internal AdaptiveIndexListImplementation = inherit AbstractDirtyReader, IndexListDelta<'b>>(IndexListDelta.monoid, checkTag "MultiReader") let mapping = IndexMapping() - let cache = DefaultDictionary.create>() + let cache = DefaultDictionary.create)>() let readers = DefaultDictionary.create, MultiReader<'b>>() let input = input.GetReader() From 6568e64d721d9d4e271ceae70676967e1e0b67cc Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 00:58:16 +0200 Subject: [PATCH 10/65] alist MultiReader/BindReader: avoid option --- .../AdaptiveIndexList/AdaptiveIndexList.fs | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 9fcb2df..3cc7372 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -770,15 +770,12 @@ module internal AdaptiveIndexListImplementation = let targets = DefaultHashSet.create() - let mutable reader = None + let mutable reader = Unchecked.defaultof> let getReader() = - match reader with - | Some r -> r - | None -> - let r = list.GetReader() - reader <- Some r - r + if obj.ReferenceEquals(reader, null) then + reader <- list.GetReader() + reader member x.AddTarget(oi : Index) = if targets.Add oi then @@ -789,10 +786,9 @@ module internal AdaptiveIndexListImplementation = member x.RemoveTarget(dirty : System.Collections.Generic.HashSet>, oi : Index) = if targets.Remove oi then - match reader with - | Some r -> + if not (obj.ReferenceEquals(reader, null)) then let result = - r.State.Content + reader.State.Content |> MapExt.toSeq |> Seq.choose (fun (ii, v) -> match mapping.Revoke(oi,ii) with @@ -806,26 +802,23 @@ module internal AdaptiveIndexListImplementation = x.Release() result - - | None -> + else IndexListDelta.empty else IndexListDelta.empty member x.Release() = - match reader with - | Some r -> + if not (obj.ReferenceEquals(reader, null)) then release(list) - r.Outputs.Remove x |> ignore + reader.Outputs.Remove x |> ignore x.Outputs.Clear() - reader <- None - | None -> + reader <- Unchecked.defaultof> + else () override x.Compute(token) = - match reader with - | Some r -> - let ops = r.GetChanges token + if not (obj.ReferenceEquals(reader, null)) then + let ops = reader.GetChanges token ops |> IndexListDelta.collect (fun ii op -> match op with @@ -845,7 +838,7 @@ module internal AdaptiveIndexListImplementation = ) - | None -> + else IndexListDelta.empty /// Reader for collect operations. @@ -983,7 +976,7 @@ module internal AdaptiveIndexListImplementation = inherit AbstractReader>(IndexListDelta.empty) let mutable inputChanged = 1 - let mutable reader : Option<'a * IIndexListReader<'b>> = None + let mutable reader : ValueOption<'a * IIndexListReader<'b>> = ValueNone override x.InputChangedObject(t : obj, o : IAdaptiveObject) = if System.Object.ReferenceEquals(input, o) then @@ -997,20 +990,20 @@ module internal AdaptiveIndexListImplementation = let inputChanged = System.Threading.Interlocked.Exchange(&inputChanged, 0) #endif match reader with - | Some (oldA, oldReader) when inputChanged = 0 || cheapEqual v oldA -> + | ValueSome (oldA, oldReader) when inputChanged = 0 || cheapEqual v oldA -> oldReader.GetChanges token | _ -> let newReader = mapping(v).GetReader() let deltas = let addNew = newReader.GetChanges token match reader with - | Some(_,old) -> + | ValueSome(_,old) -> let remOld = IndexList.computeDelta old.State IndexList.empty old.Outputs.Clear() IndexListDelta.combine remOld addNew - | None -> + | ValueNone -> addNew - reader <- Some (v,newReader) + reader <- ValueSome (v,newReader) deltas /// Reader for sortBy operations From 87cfa5b013002de2c37fd374e45915a3f2693ff6 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 11:49:45 +0200 Subject: [PATCH 11/65] updated workflow dotnet version (test) --- .github/workflows/dotnet.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7798ad7..4ac2c9b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -10,7 +10,9 @@ jobs: - name: Install Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.100' + dotnet-version: | + 6.0.100 + 8.0.400 - name: Restore Tools run: dotnet tool restore - name: Paket Restore From 2562208f1f32cca437ffc096f28f3609a7304bc5 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 12:09:37 +0200 Subject: [PATCH 12/65] updated workflow dotnet version --- .github/workflows/dotnet.yml | 4 ++-- .github/workflows/fable.yml | 4 +++- .github/workflows/publish.yml | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 4ac2c9b..24811e3 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -11,8 +11,8 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: | - 6.0.100 - 8.0.400 + 6.0.100 + 8.0.400 - name: Restore Tools run: dotnet tool restore - name: Paket Restore diff --git a/.github/workflows/fable.yml b/.github/workflows/fable.yml index 51ca700..f5b46b5 100644 --- a/.github/workflows/fable.yml +++ b/.github/workflows/fable.yml @@ -10,7 +10,9 @@ jobs: - name: Install Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.100' + dotnet-version: | + 6.0.100 + 8.0.400 - name: Restore Tools run: dotnet tool restore - name: Paket Restore diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7f0b117..778a0ae 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,7 +16,9 @@ jobs: - name: Install Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.100' + dotnet-version: | + 6.0.100 + 8.0.400 - name: Restore Tools run: dotnet tool restore - name: Paket Restore From 12bf18caf849b1c8eea552ff7976a06d7934033f Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 16:38:45 +0200 Subject: [PATCH 13/65] ASet.union: optimizations in case one of the input sets is constant --- .../AdaptiveHashSet/AdaptiveHashSet.fs | 39 ++++++++++++++-- src/Test/FSharp.Data.Adaptive.Tests/ASet.fs | 44 +++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index cc7949a..daa72a9 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -756,6 +756,32 @@ module AdaptiveHashSetImplementation = state <- newState HashSetDelta.ofHashMap delta + + /// Reader for unioning an aset with a constant set of elements. + [] + type UnionConstantSingleReader<'T>(constant : HashSet<'T>, input : aset<'T>) = + inherit AbstractReader>(HashSetDelta.empty) + + let mutable isInitial = true + let inputReader = + let r = input.GetReader() + r.Tag <- "InnerReader" + r + + override x.Compute(token) = + if isInitial then + isInitial <- false + // initially we need to combined the constant and the input set. + //let initial = constant |> Seq.map (fun v -> Add v) |> HashSetDelta.ofSeq // NOTE: this allocates enumerators + let mutable initial = HashSetDelta.empty + for v in constant do + initial <- initial.Add (Add v) + let otherInitial = inputReader.GetChanges(token) + HashSetDelta.combine initial otherInitial + else + // once evaluated only the input set needs to be pulled. + inputReader.GetChanges token + /// Reader for unioning a constant set of asets. [] type UnionConstantReader<'T>(input : HashSet>) = @@ -1201,13 +1227,18 @@ module ASet = let union (a : aset<'A>) (b : aset<'A>) = if a = b then a - elif a.IsConstant && b.IsConstant then + elif a.IsConstant then let va = force a + if b.IsConstant then + let vb = force b + if va.IsEmpty && vb.IsEmpty then empty + else constant (fun () -> HashSet.union va vb) + else + ofReader (fun () -> UnionConstantSingleReader(va, b)) + elif b.IsConstant then let vb = force b - if va.IsEmpty && vb.IsEmpty then empty - else constant (fun () -> HashSet.union va vb) + ofReader (fun () -> UnionConstantSingleReader(vb, a)) else - // TODO: can be optimized in case one of the two sets is constant. ofReader (fun () -> UnionConstantReader (HashSet.ofArray [| a; b |])) /// Adaptively subtracts the given sets. diff --git a/src/Test/FSharp.Data.Adaptive.Tests/ASet.fs b/src/Test/FSharp.Data.Adaptive.Tests/ASet.fs index 321c81e..d159054 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/ASet.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/ASet.fs @@ -715,3 +715,47 @@ let ``[ASet] mapA/flattenA/chooseA async``() = () + +[] +let ``[ASet] union constant``() = + let constSet = ASet.ofList [1; 2; 3] + let changeSet = cset [4;5;6] + + let union1 = ASet.union constSet changeSet + let union2 = ASet.union changeSet constSet + + let refSet = [1; 2; 3; 4; 5; 6] + union1 |> ASet.force |> should setequal refSet + union2 |> ASet.force |> should setequal refSet + + transact(fun () -> changeSet.Add(1) |> ignore) + + union1 |> ASet.force |> should setequal refSet + union2 |> ASet.force |> should setequal refSet + + transact(fun () -> changeSet.Remove(1) |> ignore) + + union1 |> ASet.force |> should setequal refSet + union2 |> ASet.force |> should setequal refSet + + transact(fun () -> changeSet.Remove(5) |> ignore) + + let refSet = [1; 2; 3; 4; 6] + union1 |> ASet.force |> should setequal refSet + union2 |> ASet.force |> should setequal refSet + + let constSet = ASet.ofList [1; 2; 3] + let changeSet = cset [3;4;5] + + let union1 = ASet.union constSet changeSet + let union2 = ASet.union changeSet constSet + + let refSet = [1; 2; 3; 4; 5] + union1 |> ASet.force |> should setequal refSet + union2 |> ASet.force |> should setequal refSet + + transact(fun () -> changeSet.Remove(5) |> ignore) + + let refSet = [1; 2; 3; 4] + union1 |> ASet.force |> should setequal refSet + union2 |> ASet.force |> should setequal refSet \ No newline at end of file From ff441124341030b99dee5480a7677bcdc6baaf45 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 17:33:28 +0200 Subject: [PATCH 14/65] use struct enumerators in ListSetReader, IndexedListSetReader, ToListReader (AList.toASet, AList.toASetIndexed, AList.ofASet) --- .../CollectionExtensions.fs | 77 ++++++++++--------- src/Test/FSharp.Data.Adaptive.Tests/AList.fs | 26 ++++++- .../FSharp.Data.Adaptive.Tests/Program.fs | 4 +- 3 files changed, 68 insertions(+), 39 deletions(-) diff --git a/src/FSharp.Data.Adaptive/CollectionExtensions.fs b/src/FSharp.Data.Adaptive/CollectionExtensions.fs index 1b63c25..d634727 100644 --- a/src/FSharp.Data.Adaptive/CollectionExtensions.fs +++ b/src/FSharp.Data.Adaptive/CollectionExtensions.fs @@ -111,21 +111,23 @@ module CollectionExtensions = override x.Compute(token: AdaptiveToken) = let old = reader.State.Content - reader.GetChanges(token).Content |> Seq.collect (fun (KeyValue(i, op)) -> + let changes = reader.GetChanges(token).Content + let mutable delta = HashSetDelta.empty + for KeyValue(i, op) in changes do match op with | Remove -> - match MapExt.tryFind i old with - | Some v -> Seq.singleton (Rem v) - | None -> Seq.empty + match MapExt.tryFindV i old with + | ValueSome v -> delta <- delta.Add (Rem v) + | ValueNone -> () | Set v -> - match MapExt.tryFind i old with - | Some ov -> - if DefaultEquality.equals v ov then Seq.empty - else [Add v; Rem ov] :> seq<_> - | None -> - Seq.singleton (Add v) - ) - |> HashSetDelta.ofSeq + match MapExt.tryFindV i old with + | ValueSome ov -> + if not (DefaultEquality.equals v ov) then + delta <- delta.Add (Add v) + delta <- delta.Add (Rem ov) + | ValueNone -> + delta <- delta.Add (Add v) + delta /// Reader for AList.toIndexedASet [] @@ -136,21 +138,23 @@ module CollectionExtensions = override x.Compute(token: AdaptiveToken) = let old = reader.State.Content - reader.GetChanges(token).Content |> Seq.collect (fun (KeyValue(i, op)) -> + let changes = reader.GetChanges(token).Content + let mutable delta = HashSetDelta.empty + for KeyValue(i, op) in changes do match op with | Remove -> - match MapExt.tryFind i old with - | Some v -> Seq.singleton (Rem(i, v)) - | None -> Seq.empty + match MapExt.tryFindV i old with + | ValueSome v -> delta <- delta.Add (Rem(i, v)) + | ValueNone -> () | Set v -> - match MapExt.tryFind i old with - | Some ov -> - if DefaultEquality.equals v ov then Seq.empty - else [Add(i,v); Rem(i,ov)] :> seq<_> - | None -> - Seq.singleton (Add(i,v)) - ) - |> HashSetDelta.ofSeq + match MapExt.tryFindV i old with + | ValueSome ov -> + if not (DefaultEquality.equals v ov) then + delta <- delta.Add (Add(i,v)) + delta <- delta.Add (Rem(i,ov)) + | ValueNone -> + delta <- delta.Add (Add(i,v)) + delta /// Reader for AList.ofASet [] @@ -167,20 +171,19 @@ module CollectionExtensions = let newIndex = Cache newIndex override x.Compute(token) = - reader.GetChanges token - |> HashSetDelta.toSeq - |> Seq.map (fun d -> + let changes = reader.GetChanges token + let mutable delta = IndexListDelta.empty + for d in changes do match d with - | Add(1,v) -> - let i = newIndex.Invoke v - i, Set v - | Rem(1,v) -> - let i = newIndex.Revoke v - i, Remove - | _ -> - unexpected() - ) - |> IndexListDelta.ofSeq + | Add(1,v) -> + let i = newIndex.Invoke v + delta <- delta.Add (i, Set v) + | Rem(1,v) -> + let i = newIndex.Revoke v + delta <- delta.Add (i, Remove) + | _ -> + unexpected() + delta [] type MapToListReader<'T>(input : amap) = diff --git a/src/Test/FSharp.Data.Adaptive.Tests/AList.fs b/src/Test/FSharp.Data.Adaptive.Tests/AList.fs index b428177..b9e1b5a 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/AList.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/AList.fs @@ -1161,4 +1161,28 @@ let ``[AList] mapA inner change``() = ) r.GetChanges AdaptiveToken.Top |> ignore - r.State |> should equal (IndexList.ofSeqIndexed [a, 2; b, -1; c, 6; d, 8; e, 10]) \ No newline at end of file + r.State |> should equal (IndexList.ofSeqIndexed [a, 2; b, -1; c, 6; d, 8; e, 10]) + + +[] +let ``[AList] toAset``() = + let list = clist [1; 1; 2; 2; 3; 3;] + let set = list |> AList.toASet + + set |> ASet.force |> should setequal [1; 2; 3] + + transact(fun () -> list.Add(2) |> ignore) + + set |> ASet.force |> should setequal [1; 2; 3] + + transact(fun () -> list.Add(4) |> ignore) + + set |> ASet.force |> should setequal [1; 2; 3; 4] + + transact(fun () -> list.RemoveAt(0) |> ignore) + + set |> ASet.force |> should setequal [1; 2; 3; 4] + + transact(fun () -> list.RemoveAt(0) |> ignore) + + set |> ASet.force |> should setequal [2; 3; 4] \ No newline at end of file diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Program.fs b/src/Test/FSharp.Data.Adaptive.Tests/Program.fs index df463ad..28173dc 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/Program.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/Program.fs @@ -49,6 +49,8 @@ let ``[AList] sub``() = let main _args = //ASet.``[ASet] mapA/flattenA/chooseA async``() + //ASet.``[ASet] union constant``() + AList.``[AList] toAset``() //``[AList] sub``(); @@ -88,6 +90,6 @@ let main _args = //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore - BenchmarkRunner.Run() |> ignore + //BenchmarkRunner.Run() |> ignore 0 From e35bbe5ba58edf09a540d27fb1b940c08ff5c38b Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 9 Sep 2024 17:39:24 +0200 Subject: [PATCH 15/65] using value options in AMap ToASetReader and ToValueASetReader --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 07daee5..2f6e2c2 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -936,18 +936,18 @@ module AdaptiveHashMapImplementation = for (k,op) in ops do match op with | Set v -> - match HashMap.tryFind k oldState with - | None -> () - | Some oldValue -> + match HashMap.tryFindV k oldState with + | ValueNone -> () + | ValueSome oldValue -> deltas <- HashSetDelta.add (Rem(k, oldValue)) deltas deltas <- HashSetDelta.add (Add(k, v)) deltas | Remove -> // NOTE: As it is not clear at what point the toASet computation has been evaluated last, it is // a valid case that something is removed that is not present in the current local state. - match HashMap.tryFind k oldState with - | None -> () - | Some ov -> + match HashMap.tryFindV k oldState with + | ValueNone -> () + | ValueSome ov -> deltas <- HashSetDelta.add (Rem (k, ov)) deltas @@ -961,18 +961,18 @@ module AdaptiveHashMapImplementation = for (k,op) in ops do match op with | Set v -> - match HashMap.tryFind k oldState with - | None -> () - | Some oldValue -> + match HashMap.tryFindV k oldState with + | ValueNone -> () + | ValueSome oldValue -> deltas <- HashSetDelta.add (Rem(k, oldValue)) deltas deltas <- HashSetDelta.add (Add(k, v)) deltas | Remove -> // NOTE: As it is not clear at what point the toASet computation has been evaluated last, it is // a valid case that something is removed that is not present in the current local state. - match HashMap.tryFind k oldState with - | None -> () - | Some ov -> + match HashMap.tryFindV k oldState with + | ValueNone -> () + | ValueSome ov -> deltas <- HashSetDelta.add (Rem (k, ov)) deltas @@ -991,18 +991,18 @@ module AdaptiveHashMapImplementation = for (k,op) in ops do match op with | Set v -> - match HashMap.tryFind k oldState with - | None -> () - | Some oldValue -> + match HashMap.tryFindV k oldState with + | ValueNone -> () + | ValueSome oldValue -> deltas <- HashSetDelta.add (Rem(oldValue)) deltas deltas <- HashSetDelta.add (Add(v)) deltas | Remove -> // NOTE: As it is not clear at what point the toASet computation has been evaluated last, it is // a valid case that something is removed that is not present in the current local state. - match HashMap.tryFind k oldState with - | None -> () - | Some ov -> + match HashMap.tryFindV k oldState with + | ValueNone -> () + | ValueSome ov -> deltas <- HashSetDelta.add (Rem (ov)) deltas @@ -1015,18 +1015,18 @@ module AdaptiveHashMapImplementation = for (k,op) in ops do match op with | Set v -> - match HashMap.tryFind k oldState with - | None -> () - | Some oldValue -> + match HashMap.tryFindV k oldState with + | ValueNone -> () + | ValueSome oldValue -> deltas <- HashSetDelta.add (Rem(oldValue)) deltas deltas <- HashSetDelta.add (Add(v)) deltas | Remove -> // NOTE: As it is not clear at what point the toASet computation has been evaluated last, it is // a valid case that something is removed that is not present in the current local state. - match HashMap.tryFind k oldState with - | None -> () - | Some ov -> + match HashMap.tryFindV k oldState with + | ValueNone -> () + | ValueSome ov -> deltas <- HashSetDelta.add (Rem (ov)) deltas deltas From c388e876457c7e11623ab18ab9efa14978926b2a Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 10 Sep 2024 12:33:43 +0200 Subject: [PATCH 16/65] revert net8 sdk install on workflows that do not depend on it --- .github/workflows/fable.yml | 4 +--- .github/workflows/publish.yml | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/fable.yml b/.github/workflows/fable.yml index f5b46b5..51ca700 100644 --- a/.github/workflows/fable.yml +++ b/.github/workflows/fable.yml @@ -10,9 +10,7 @@ jobs: - name: Install Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: | - 6.0.100 - 8.0.400 + dotnet-version: '6.0.100' - name: Restore Tools run: dotnet tool restore - name: Paket Restore diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 778a0ae..7f0b117 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,9 +16,7 @@ jobs: - name: Install Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: | - 6.0.100 - 8.0.400 + dotnet-version: '6.0.100' - name: Restore Tools run: dotnet tool restore - name: Paket Restore From ae61ada959400bedbed8f96d80ead03966bbfb14 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Thu, 12 Sep 2024 23:43:12 +0200 Subject: [PATCH 17/65] changed history input to value option --- src/FSharp.Data.Adaptive/Traceable/History.fs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Traceable/History.fs b/src/FSharp.Data.Adaptive/Traceable/History.fs index 7cbd58c..a617f27 100644 --- a/src/FSharp.Data.Adaptive/Traceable/History.fs +++ b/src/FSharp.Data.Adaptive/Traceable/History.fs @@ -125,7 +125,7 @@ type internal RelevantNode<'State, 'T> = /// History and HistoryReader are the central implementation for traceable data-types. /// The allow to construct a dependent History (by passing an input-reader) or imperatively /// performing operations on the history while keeping track of all output-versions that may exist. -type History<'State, 'Delta> private(input: option>>, t: Traceable<'State, 'Delta>, finalize: 'Delta -> unit) = +type History<'State, 'Delta> private(input: voption>>, t: Traceable<'State, 'Delta>, finalize: 'Delta -> unit) = inherit AdaptiveObject() /// The current state of the History @@ -337,10 +337,10 @@ type History<'State, 'Delta> private(input: option>>, t: member private x.Update (self: AdaptiveToken) = if x.OutOfDate then match input with - | Some c -> + | ValueSome c -> let v = c.Value.GetChanges self append v |> ignore - | None -> + | ValueNone -> () /// The current state of the history @@ -433,10 +433,10 @@ type History<'State, 'Delta> private(input: option>>, t: interface IAdaptiveValue<'State> with member x.GetValue t = x.GetValue t - new (t: Traceable<'State, 'Delta>, finalize: 'Delta -> unit) = History<'State, 'Delta>(None, t, finalize) - new (input: unit -> IOpReader<'Delta>, t: Traceable<'State, 'Delta>, finalize: 'Delta -> unit) = History<'State, 'Delta>(Some (lazy (input())), t, finalize) - new (t: Traceable<'State, 'Delta>) = History<'State, 'Delta>(None, t, ignore) - new (input: unit -> IOpReader<'Delta>, t: Traceable<'State, 'Delta>) = History<'State, 'Delta>(Some (lazy (input())), t, ignore) + new (t: Traceable<'State, 'Delta>, finalize: 'Delta -> unit) = History<'State, 'Delta>(ValueNone, t, finalize) + new (input: unit -> IOpReader<'Delta>, t: Traceable<'State, 'Delta>, finalize: 'Delta -> unit) = History<'State, 'Delta>(ValueSome (lazy (input())), t, finalize) + new (t: Traceable<'State, 'Delta>) = History<'State, 'Delta>(ValueNone, t, ignore) + new (input: unit -> IOpReader<'Delta>, t: Traceable<'State, 'Delta>) = History<'State, 'Delta>(ValueSome (lazy (input())), t, ignore) /// HistoryReader implements IOpReader<_,_> and takes care of managing versions correctly. and internal HistoryReader<'State, 'Delta>(h: History<'State, 'Delta>) = From d0cd43155c6e95c2114ea8acabfbf5fd12718e95 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Thu, 12 Sep 2024 23:46:20 +0200 Subject: [PATCH 18/65] changed IndexMapping to use value options --- .../AdaptiveIndexList/AdaptiveIndexList.fs | 28 +++++++++---------- .../CollectionExtensions.fs | 4 +-- .../Utilities/Utilities.fs | 26 ++++++++--------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 3cc7372..724213f 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -792,8 +792,8 @@ module internal AdaptiveIndexListImplementation = |> MapExt.toSeq |> Seq.choose (fun (ii, v) -> match mapping.Revoke(oi,ii) with - | Some v -> Some (v, Remove) - | None -> None + | ValueSome v -> Some (v, Remove) + | ValueNone -> None ) |> IndexListDelta.ofSeq @@ -826,8 +826,8 @@ module internal AdaptiveIndexListImplementation = targets |> Seq.choose (fun oi -> match mapping.Revoke(oi, ii) with - | Some i -> Some(i, Remove) - | None -> None + | ValueSome i -> Some(i, Remove) + | ValueNone -> None ) |> IndexListDelta.ofSeq @@ -936,9 +936,9 @@ module internal AdaptiveIndexListImplementation = | Remove -> let outIndex = mapping.Revoke(index, i) match outIndex with - | Some outIndex -> + | ValueSome outIndex -> Some (outIndex, Remove) - | None -> + | ValueNone -> None ) @@ -1024,8 +1024,8 @@ module internal AdaptiveIndexListImplementation = match cache.TryGetValue i with | (true, b) -> match idx.Revoke((b, i)) with - | Some oi -> Some (oi, Remove) - | None -> None + | ValueSome oi -> Some (oi, Remove) + | ValueNone -> None | _ -> None let b = mapping i v @@ -1039,8 +1039,8 @@ module internal AdaptiveIndexListImplementation = | (true, b) -> cache.Remove i |> ignore match idx.Revoke((b, i)) with - | Some oi -> [(oi, Remove)] - | None -> [] + | ValueSome oi -> [(oi, Remove)] + | ValueNone -> [] | _ -> [] ) @@ -1073,8 +1073,8 @@ module internal AdaptiveIndexListImplementation = match MapExt.tryFind i old with | Some ov -> match idx.Revoke(UCmp(cmp, struct(ov, i))) with - | Some oi -> Some (oi, Remove) - | None -> None + | ValueSome oi -> Some (oi, Remove) + | ValueNone -> None | _ -> None let oi = idx.Invoke(UCmp(cmp, struct(v, i))) @@ -1085,8 +1085,8 @@ module internal AdaptiveIndexListImplementation = match MapExt.tryFind i old with | Some ov -> match idx.Revoke(UCmp(cmp, struct(ov, i))) with - | Some oi -> [(oi, Remove)] - | None -> [] + | ValueSome oi -> [(oi, Remove)] + | ValueNone -> [] | _ -> [] ) diff --git a/src/FSharp.Data.Adaptive/CollectionExtensions.fs b/src/FSharp.Data.Adaptive/CollectionExtensions.fs index d634727..c348049 100644 --- a/src/FSharp.Data.Adaptive/CollectionExtensions.fs +++ b/src/FSharp.Data.Adaptive/CollectionExtensions.fs @@ -54,8 +54,8 @@ module CollectionExtensions = | Rem(_, v) -> let k = cache.Revoke v match mapping.Revoke k with - | Some idx -> Some (idx, Remove) - | None -> None + | ValueSome idx -> Some (idx, Remove) + | ValueNone -> None ) |> IndexListDelta.ofSeq diff --git a/src/FSharp.Data.Adaptive/Utilities/Utilities.fs b/src/FSharp.Data.Adaptive/Utilities/Utilities.fs index aa35888..be4b41d 100644 --- a/src/FSharp.Data.Adaptive/Utilities/Utilities.fs +++ b/src/FSharp.Data.Adaptive/Utilities/Utilities.fs @@ -176,28 +176,28 @@ module internal AdaptiveIndexListHelpers = member x.Invoke(k : 'k) = let mutable index = Index.zero - let inline ret i = index <- i; Some i + let inline ret i = index <- i; ValueSome i let newStore = - store |> MapExt.changeWithNeighbours k (fun left self right -> + store |> MapExt.changeWithNeighboursV k (fun left self right -> match self with - | Some i -> ret i - | None -> + | ValueSome i -> ret i + | ValueNone -> match left, right with - | None, None -> Index.after Index.zero |> ret - | Some(_,l), None -> Index.after l |> ret - | None, Some(_,r) -> Index.before r |> ret - | Some (_,l), Some(_,r) -> Index.between l r |> ret + | ValueNone, ValueNone -> Index.after Index.zero |> ret + | ValueSome(_,l), ValueNone -> Index.after l |> ret + | ValueNone, ValueSome(_,r) -> Index.before r |> ret + | ValueSome (_,l), ValueSome(_,r) -> Index.between l r |> ret ) store <- newStore index member x.Revoke(k : 'k) = - match MapExt.tryRemove k store with - | Some(i, rest) -> + match MapExt.tryRemoveV k store with + | ValueSome(i, rest) -> store <- rest - Some i - | None -> - None + ValueSome i + | ValueNone -> + ValueNone member x.Clear() = store <- MapExt.empty From 5f9c7f8868c0328baed20292c7c9fd0447e8b069 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sat, 14 Sep 2024 00:17:16 +0200 Subject: [PATCH 19/65] using struct enumerators --- .../AdaptiveIndexList/AdaptiveIndexList.fs | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 724213f..8175269 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -788,14 +788,12 @@ module internal AdaptiveIndexListImplementation = if targets.Remove oi then if not (obj.ReferenceEquals(reader, null)) then let result = - reader.State.Content - |> MapExt.toSeq - |> Seq.choose (fun (ii, v) -> + let mutable delta = IndexListDelta.empty + for KeyValue(ii, v) in reader.State.Content do match mapping.Revoke(oi,ii) with - | ValueSome v -> Some (v, Remove) - | ValueNone -> None - ) - |> IndexListDelta.ofSeq + | ValueSome v -> delta <- delta.Add (v, Remove) + | ValueNone -> () + delta if targets.Count = 0 then dirty.Remove x |> ignore @@ -823,19 +821,18 @@ module internal AdaptiveIndexListImplementation = ops |> IndexListDelta.collect (fun ii op -> match op with | Remove -> - targets - |> Seq.choose (fun oi -> + let mutable delta = IndexListDelta.empty + for oi in targets do match mapping.Revoke(oi, ii) with - | ValueSome i -> Some(i, Remove) - | ValueNone -> None - ) - |> IndexListDelta.ofSeq + | ValueSome i -> delta <- delta.Add (i, Remove) + | ValueNone -> () + delta | Set v -> - targets - |> Seq.map (fun oi -> mapping.Invoke(oi, ii), Set v) - |> IndexListDelta.ofSeq - + let mutable delta = IndexListDelta.empty + for oi in targets do + delta <- delta.Add (mapping.Invoke(oi, ii), Set v) + delta ) else From efd55a87d8a0129bbc62f0505cb3bed10d7a27e0 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sat, 14 Sep 2024 11:17:11 +0200 Subject: [PATCH 20/65] added IEquatable interface to Index --- .../Datastructures/Index.fs | 9 +++ .../Datastructures/Index.fsi | 1 + .../Benchmarks/IndexBenchmarks.fs | 59 +++++++++++++++++++ .../FSharp.Data.Adaptive.Tests.fsproj | 1 + .../FSharp.Data.Adaptive.Tests/Program.fs | 3 +- 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index 77720cf..60715d2 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -3,6 +3,7 @@ open System open System.Threading open System.Collections.Generic +open System.Runtime.CompilerServices /// the internal implementation of our order-maintenance structure. [] @@ -138,6 +139,10 @@ type IndexNode = | false, false -> x.CompareTo o + [] + member x.Equals (o : IndexNode) = + System.Object.ReferenceEquals(x,o) + interface IComparable with member x.CompareTo (o : obj) = match o with @@ -270,6 +275,10 @@ type Index private(real : IndexNode) = override x.ToString() = real.ToString() member private x.AsString = x.ToString() + interface IEquatable with + member x.Equals (o: Index): bool = + real.Equals o.Value + interface IComparable with member x.CompareTo(o : obj) = match o with diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fsi b/src/FSharp.Data.Adaptive/Datastructures/Index.fsi index 49f38bb..11d60b3 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fsi +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fsi @@ -9,6 +9,7 @@ open System type Index = interface IComparable #if !FABLE_COMPILER + interface IEquatable interface IComparable #endif diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs new file mode 100644 index 0000000..27a1fe1 --- /dev/null +++ b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs @@ -0,0 +1,59 @@ +namespace Benchmarks + +open BenchmarkDotNet.Attributes +open FSharp.Data.Adaptive + +//BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4894/22H2/2022Update) +//Intel Core i7-8700K CPU 3.70GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores +//.NET SDK 8.0.400 +// [Host] : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 DEBUG +// DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 + +//| Method | CreateCount | Mean | Error | StdDev | Allocated | +//|-------------- |------------ |---------:|--------:|--------:|----------:| +//| HashSetAdd | 10000 | 221.6 μs | 2.46 μs | 2.05 μs | - | +//| DictionaryAdd | 10000 | 230.2 μs | 0.59 μs | 0.55 μs | - | + +// with Index IEquatable +//| Method | CreateCount | Mean | Error | StdDev | Allocated | +//|-------------- |------------ |---------:|--------:|--------:|----------:| +//| HashSetAdd | 10000 | 204.5 us | 0.37 us | 0.34 us | - | +//| DictionaryAdd | 10000 | 215.2 us | 2.46 us | 2.18 us | - | + +// + inlined IndexNode Equals +//| Method | CreateCount | Mean | Error | StdDev | Allocated | +//|-------------- |------------ |---------:|--------:|--------:|----------:| +//| HashSetAdd | 10000 | 199.1 us | 3.85 us | 4.12 us | - | +//| DictionaryAdd | 10000 | 205.4 us | 1.43 us | 1.33 us | - | + +[] +type IndexEqualsBenchmarks() = + + [] + val mutable public Count : int + + let mutable indexSet = Unchecked.defaultof<_> + let mutable indexDict = Unchecked.defaultof<_> + let mutable indices = Array.empty + + [] + member x.Setup() = + indexSet <- System.Collections.Generic.HashSet(x.Count) + indexDict <- System.Collections.Generic.Dictionary(x.Count) + indices <- Array.zeroCreate x.Count + let mutable h = Index.zero + for i in 0..x.Count-1 do + h <- Index.after h + indices.[i] <- h + + [] + member x.HashSetAdd() = + for i in indices do + indexSet.Add(i) |> ignore + + [] + member x.DictionaryAdd() = + let mutable ii = 0 + for i in indices do + indexDict.[i] <- ii + ii <- ii + 1 diff --git a/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj b/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj index 11c51a5..c06707e 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj +++ b/src/Test/FSharp.Data.Adaptive.Tests/FSharp.Data.Adaptive.Tests.fsproj @@ -26,6 +26,7 @@ + diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Program.fs b/src/Test/FSharp.Data.Adaptive.Tests/Program.fs index 28173dc..12fdc68 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/Program.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/Program.fs @@ -50,7 +50,7 @@ let main _args = //ASet.``[ASet] mapA/flattenA/chooseA async``() //ASet.``[ASet] union constant``() - AList.``[AList] toAset``() + //AList.``[AList] toAset``() //``[AList] sub``(); @@ -91,5 +91,6 @@ let main _args = //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore + BenchmarkRunner.Run() |> ignore 0 From ad2df24f6fd4480731ff47ee163a1d94233801ce Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 17 Sep 2024 15:18:12 +0200 Subject: [PATCH 21/65] fixed race condition in Index --- src/FSharp.Data.Adaptive/Datastructures/Index.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index 60715d2..599dae0 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -82,7 +82,7 @@ type IndexNode = // put the new node in between me and the next. let key = x.Tag + (distance / 2UL) - let res = IndexNode(x.Root, Prev = x, Next = x.Next, Tag = key) + let res = IndexNode(x.Root, Prev = x, Next = next, Tag = key) // link the node next.Prev <- res @@ -95,7 +95,7 @@ type IndexNode = member x.Delete() = let prev = x.Prev Monitor.Enter prev - if prev.Next <> x then + if prev.Next <> x || prev.RefCount = 0 then // also check refCount, prev might just have been deleted Monitor.Exit prev x.Delete() else @@ -239,7 +239,7 @@ type Index private(real : IndexNode) = member x.Before() = let prev = real.Prev Monitor.Enter prev - if prev.Next <> real then + if prev.Next <> real || prev.RefCount = 0 then // also check refCount, prev might just have been deleted Monitor.Exit prev x.Before() else From 98bdd1a1c51e60a055ee920bda11b19f2fd0b147 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sat, 14 Sep 2024 23:15:02 +0200 Subject: [PATCH 22/65] separate implementation of AList.append: avoid enumerators in AList.concat --- .../AdaptiveIndexList/AdaptiveIndexList.fs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 8175269..39bdd8c 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -1516,7 +1516,10 @@ module AList = /// Adaptively concatenates the given lists. let append (l: alist<'T>) (r: alist<'T>) = - concat [l; r] + if l.IsConstant && r.IsConstant then + constant (fun () -> IndexList.append (l |> force) (r |> force)) + else // TODO: optimization for if one is constant + ofReader (fun () -> ConcatReader(IndexList.ofArray [|l; r|])) /// Creates an aval providing access to the current content of the list. let toAVal (list : alist<'T>) = From 62dbccb060df000f17888993f549d7ee2096e9df Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sat, 14 Sep 2024 23:26:13 +0200 Subject: [PATCH 23/65] using IndexList.forall instead of Seq.forall --- .../AdaptiveIndexList/AdaptiveIndexList.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 39bdd8c..dffeed7 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -1439,7 +1439,7 @@ module AList = let mapAi (mapping: Index -> 'T1 -> aval<'T2>) (list: alist<'T1>) = if list.IsConstant then let list = force list |> IndexList.mapi mapping - if list |> Seq.forall (fun v -> v.IsConstant) then + if list |> IndexList.forall (fun _ v -> v.IsConstant) then constant (fun () -> list |> IndexList.map AVal.force) else // TODO better impl possible @@ -1455,7 +1455,7 @@ module AList = let chooseAi (mapping: Index ->'T1 -> aval>) (list: alist<'T1>) = if list.IsConstant then let list = force list |> IndexList.mapi mapping - if list |> Seq.forall (fun v -> v.IsConstant) then + if list |> IndexList.forall (fun _ v -> v.IsConstant) then constant (fun () -> list |> IndexList.choose AVal.force) else // TODO better impl possible @@ -1483,7 +1483,7 @@ module AList = let collecti (mapping: Index -> 'T1 -> alist<'T2>) (list : alist<'T1>) = if list.IsConstant then let content = force list |> IndexList.mapi mapping - if content |> Seq.forall (fun l -> l.IsConstant) then + if content |> IndexList.forall (fun _ l -> l.IsConstant) then constant (fun () -> content |> IndexList.collect force) else ofReader (fun () -> ConcatReader(content)) @@ -1509,7 +1509,7 @@ module AList = let lists = IndexList.ofSeq lists if lists.IsEmpty then empty - elif lists |> Seq.forall (fun l -> l.IsConstant) then + elif lists |> IndexList.forall (fun _ l -> l.IsConstant) then constant (fun () -> lists |> IndexList.collect force) else ofReader (fun () -> ConcatReader(lists)) From 8131d268c3ede5ec58ed6eac943f3bcd0ad4b4a8 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 17 Sep 2024 23:43:12 +0200 Subject: [PATCH 24/65] reviewed Index locking and updated comments --- src/FSharp.Data.Adaptive/Datastructures/Index.fs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index 599dae0..d6497ae 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -95,7 +95,7 @@ type IndexNode = member x.Delete() = let prev = x.Prev Monitor.Enter prev - if prev.Next <> x || prev.RefCount = 0 then // also check refCount, prev might just have been deleted + if prev.Next <> x then // NOTE: prev.Next might point to an inserted node between after lock has been aquired Monitor.Exit prev x.Delete() else @@ -104,6 +104,8 @@ type IndexNode = if x.RefCount = 1 then prev.Next <- x.Next x.Next.Prev <- prev + x.Next <- Unchecked.defaultof<_> // set pointers to null so no additional RefCount=0 check is needed in Before after aquiring the lock + x.Prev <- Unchecked.defaultof<_> x.RefCount <- 0 else x.RefCount <- x.RefCount - 1 @@ -239,7 +241,7 @@ type Index private(real : IndexNode) = member x.Before() = let prev = real.Prev Monitor.Enter prev - if prev.Next <> real || prev.RefCount = 0 then // also check refCount, prev might just have been deleted + if prev.Next <> real then // NOTE: prev.Next might point to an inserted node between or be null if it just has been deleted Monitor.Exit prev x.Before() else From a868a5b3bc18bc671c7cb662e26627104021f971 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 18 Sep 2024 01:23:11 +0200 Subject: [PATCH 25/65] avoid double locking + explicit RefCount tracking --- .../Datastructures/Index.fs | 64 ++++++++++--------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index d6497ae..f012bd2 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -67,29 +67,28 @@ type IndexNode = member x.Key = x.Tag - x.Root.Tag /// insert a node directly after this one. + /// NOTE: expects node to be locked member x.InsertAfter() = - lock x (fun () -> - let next = x.Next + let next = x.Next - /// initially we increment the distance by Uint64.MaxValue/2 - let mutable distance = - if next = x then UInt64.MaxValue - else next.Tag - x.Tag - - // we're out of luck and there's no space for an additional node. - if distance = 1UL then - distance <- IndexNode.Relabel x + /// initially we increment the distance by Uint64.MaxValue/2 + let mutable distance = + if next = x then UInt64.MaxValue + else next.Tag - x.Tag + + // we're out of luck and there's no space for an additional node. + if distance = 1UL then + distance <- IndexNode.Relabel x - // put the new node in between me and the next. - let key = x.Tag + (distance / 2UL) - let res = IndexNode(x.Root, Prev = x, Next = next, Tag = key) + // put the new node in between me and the next. + let key = x.Tag + (distance / 2UL) + let res = IndexNode(x.Root, Prev = x, Next = next, Tag = key) - // link the node - next.Prev <- res - x.Next <- res + // link the node + next.Prev <- res + x.Next <- res - res - ) + res /// Delete a node from the cycle. member x.Delete() = @@ -114,11 +113,6 @@ type IndexNode = Monitor.Exit x Monitor.Exit prev - /// add a reference to the node. - member x.AddRef() = - lock x (fun () -> - x.RefCount <- x.RefCount + 1 - ) /// Compare me to another node. member x.CompareTo(o : IndexNode) = @@ -156,7 +150,7 @@ type IndexNode = override x.ToString() = sprintf "%f" (float x.Key / float UInt64.MaxValue) member private x.AsString = x.ToString() - new(root : IndexNode) = { Root = root; Prev = Unchecked.defaultof<_>; Next = Unchecked.defaultof<_>; Tag = 0UL; RefCount = 0 } + new(root : IndexNode) = { Root = root; Prev = Unchecked.defaultof<_>; Next = Unchecked.defaultof<_>; Tag = 0UL; RefCount = 1 } end #if !FABLE_COMPILER @@ -217,7 +211,6 @@ type internal AsyncBlockingCollection<'a>() = /// Note that the implementation is quite obfuscated due to concurrency. [] type Index private(real : IndexNode) = - do real.AddRef() static let queue = new AsyncBlockingCollection() @@ -234,8 +227,14 @@ type Index private(real : IndexNode) = member x.After() = lock real (fun () -> - if real.Next <> real.Root then Index real.Next - else Index (real.InsertAfter()) + let next = real.Next + if next <> real.Root then + lock next (fun () -> + next.RefCount <- next.RefCount + 1 + ) + next |> Index + else + real.InsertAfter() |> Index ) member x.Before() = @@ -249,6 +248,7 @@ type Index private(real : IndexNode) = if prev = real.Root then prev.InsertAfter() |> Index else + prev.RefCount <- prev.RefCount + 1 prev |> Index finally Monitor.Exit prev @@ -258,8 +258,14 @@ type Index private(real : IndexNode) = let r = r.Value Monitor.Enter l try - if l.Next = r then l.InsertAfter() |> Index - else l.Next |> Index + let next = l.Next + if next = r then + l.InsertAfter() |> Index + else + lock next (fun () -> + next.RefCount <- next.RefCount + 1 + ) + next |> Index finally Monitor.Exit l From db55d5483a97c8ab4efacf130751a3fadce2ae1f Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 18 Sep 2024 01:23:57 +0200 Subject: [PATCH 26/65] Index finalizer delete --- src/FSharp.Data.Adaptive/Datastructures/Index.fs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index f012bd2..b7a8775 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -212,17 +212,6 @@ type internal AsyncBlockingCollection<'a>() = [] type Index private(real : IndexNode) = - - static let queue = new AsyncBlockingCollection() - - static do - async { - while true do - let! v = queue.Take() - do! Async.SwitchToThreadPool() - v.Delete() - } |> Async.Start - member private x.Value = real member x.After() = @@ -270,7 +259,7 @@ type Index private(real : IndexNode) = Monitor.Exit l override x.Finalize() = - queue.Add real + real.Delete() member x.CompareTo (o : Index) = real.CompareTo(o.Value) From eae8294d79f374564be34d67cb307db21f813bba Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 18 Sep 2024 01:26:31 +0200 Subject: [PATCH 27/65] added Index garbage benchmarks --- .../Benchmarks/IndexBenchmarks.fs | 79 +++++++++++++++++++ .../FSharp.Data.Adaptive.Tests/Program.fs | 12 ++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs index 27a1fe1..62b5f90 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs @@ -57,3 +57,82 @@ type IndexEqualsBenchmarks() = for i in indices do indexDict.[i] <- ii ii <- ii + 1 + +// NOTE: wait benchmarked with GC.Collect + GC.WaitForPendingFinalizers + loop checking delete queue/alive count via exposed counters +// Reference: overhead 536 bytes/item (~CPU 30%) +//| Method | GarbageCount | ListCount | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +//|------------------ |------------- |---------- |---------:|---------:|---------:|------------:|------------:|------------:|----------:| +//| GarbageRnd (wait) | 1000000 | 100 | 629.6 ms | 12.59 ms | 25.71 ms | 290000.0000 | 205000.0000 | 154000.0000 | 826.82 MB | +//| GarbageRnd | 1000000 | 100 | 539.2 ms | 7.52 ms | 7.03 ms | 138000.0000 | 70000.0000 | 1000.0000 | 822.73 MB | +//| GarbageRnd | 5000000 | 100 | 2.592 s | 0.0370 s | 0.0346 s | 695000.0000 | 358000.0000 | 2000.0000 | 4.02 GB | +//| Garbage0 | 5000000 | 100 | 2.315 s | 0.0305 s | 0.0255 s | 574000.0000 | 280000.0000 | - | 3.47 GB | + +// BlockingCollection: overhead: 183 bytes/item (~CPU 27%) +//| Method | GarbageCount | ListCount | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +//|------------------ |------------- |---------- |---------:|---------:|---------:|------------:|------------:|------------:|----------:| +//| GarbageRnd (wait) | 1000000 | 100 | 624.4 ms | 12.36 ms | 28.15 ms | 285000.0000 | 270000.0000 | 207000.0000 | 490.43 MB | +//| GarbageRnd | 1000000 | 100 | 520.2 ms | 9.25 ms | 8.20 ms | 82000.0000 | 81000.0000 | - | 488.39 MB | + +// BlockingCollection + background thread delete: overhead 0 bytes (~CPU 13%) +//| Method | GarbageCount | ListCount | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +//|----------------- |------------- |---------- |---------:|--------:|--------:|-----------:|-----------:|-----------:|----------:| +//| GarbageRnd (wait) | 1000000 | 100 | 535.8 ms | 2.34 ms | 1.83 ms | 57000.0000 | 56000.0000 | 11000.0000 | 312.96 MB | +//| GarbageRnd | 1000000 | 100 | 521.8 ms | 9.87 ms | 8.75 ms | 53000.0000 | 52000.0000 | - | 318.79 MB | + +// finalizer Delete (infinite loop fastest): overhead 0 bytes (~CPU 9%) +//| Method | GarbageCount | ListCount | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +//|------------------ |------------- |---------- |---------:|---------:|---------:|------------:|------------:|----------:|-----------:| +//| GarbageRnd (wait) | 1000000 | 100 | 507.7 ms | 2.46 ms | 2.05 ms | 53000.0000 | 52000.0000 | 4000.0000 | 315.57 MB | +//| GarbageRnd | 1000000 | 100 | 471.0 ms | 3.16 ms | 2.96 ms | 52000.0000 | 51000.0000 | - | 315.67 MB | +//| GarbageRnd | 5000000 | 100 | 2.338 s | 0.0159 s | 0.0133 s | 262000.0000 | 261000.0000 | - | 1572.10 MB | +//| Garbage0 | 5000000 | 100 | 1.526 s | 0.0058 s | 0.0045 s | 169000.0000 | 168000.0000 | - | 1017.00 MB | + + +//| Method | GarbageCount | ListCount | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | +//|--------- |------------- |---------- |--------:|---------:|---------:|------------:|------------:|-----------:| +//| Garbage0 | 5000000 | 100 | 1.595 s | 0.0173 s | 0.0162 s | 170000.0000 | 169000.0000 | 1017.29 MB | + +// explicit RefCount tracking + avoid double locking +//| Method | GarbageCount | ListCount | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | +//|--------- |------------- |---------- |--------:|---------:|---------:|------------:|------------:|-----------:| +//| Garbage0 | 5000000 | 100 | 1.531 s | 0.0040 s | 0.0035 s | 169000.0000 | 168000.0000 | 1016.93 MB | + +[] +[] +type IndexGarbageBenchmarks() = + + [] + val mutable public GarbageCount : int + + [] + val mutable public ListCount : int + + let mutable list = IndexList.empty + let rnd = System.Random(12345678) + + [] + member x.GarbageRnd() = + for i in 1..x.GarbageCount do + let op = rnd.NextDouble() + if list.Count = 0 || op < 0.01 then + list <- IndexList.single i + elif list.Count < x.ListCount && op < 0.5 then // add + list <- list.InsertAt(rnd.Next(list.Count), i) + else // remove + list <- list.RemoveAt(rnd.Next(list.Count)) + + list <- IndexList.empty + + [] + member x.Garbage0() = + for i in 1..x.GarbageCount do + let op = rnd.NextDouble() + if list.Count = 0 || op < 0.01 then + list <- IndexList.single i + elif list.Count < x.ListCount && op < 0.5 then // add + list <- list.Prepend i + else // remove + list <- list.RemoveAt(0) + + list <- IndexList.empty + diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Program.fs b/src/Test/FSharp.Data.Adaptive.Tests/Program.fs index 12fdc68..1911ee9 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/Program.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/Program.fs @@ -91,6 +91,16 @@ let main _args = //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore //BenchmarkRunner.Run() |> ignore - BenchmarkRunner.Run() |> ignore + //BenchmarkRunner.Run() |> ignore + BenchmarkRunner.Run() |> ignore + + //let x = Benchmarks.IndexGarbageBenchmarks() + //x.ListCount <- 100 + //x.GarbageCount <- 1000000 + //for i in 0..1000 do + // x.GarbageRnd() + // printfn "iter %d" i + + //printfn "done" 0 From 13180681f55e8dbf977a4c1763d6226be879a4ae Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 18 Sep 2024 01:44:15 +0200 Subject: [PATCH 28/65] cleanup AsyncBlockingCollection --- .../Datastructures/Index.fs | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index b7a8775..0f9b978 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -155,56 +155,6 @@ type IndexNode = #if !FABLE_COMPILER -type internal AsyncBlockingCollection<'a>() = - let store = System.Collections.Generic.Queue<'a>() - - let mutable next : option> = None //Tasks.TaskCompletionSource() - - member x.Add(value : 'a) = - lock store (fun () -> - store.Enqueue value - match next with - | Some n -> - n.SetResult() - next <- None - | None -> - () - ) - - member x.Take() = - Async.FromContinuations (fun (success, error, cancel) -> - Monitor.Enter store - try - if store.Count > 0 then - let value = store.Dequeue() - Monitor.Exit store - success value - else - let tcs = - match next with - | Some n -> n - | None -> - let n = Tasks.TaskCompletionSource() - next <- Some n - n - Monitor.Exit store - tcs.Task.ContinueWith (fun (_t : Tasks.Task) -> - Async.StartWithContinuations(x.Take(), success, error, cancel) - ) |> ignore - with - | :? OperationCanceledException as e -> - Monitor.Exit store - cancel e - | e -> - Monitor.Exit store - error e - ) - - member x.Clear() = - lock store (fun () -> - store.Clear() - ) - /// datastructure representing an abstract index. /// supported operations are: Index.zero, Index.after(index), Index.before(index), Index.between(l, r). /// this is a 'simple' solution to the order-maintenance problem that has insert in O(log N), delete in O(1) and compare in O(1). From f69b07e85a2c9c6ae14c7e0a2d5f833c4bda6fc4 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 18 Sep 2024 14:14:09 +0200 Subject: [PATCH 29/65] removed try finally blocks in Index: there is no external code so if something fails, it must be something very broken where recovering is not possible anyway --- .../Datastructures/Index.fs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index 0f9b978..3a0161b 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -99,30 +99,28 @@ type IndexNode = x.Delete() else Monitor.Enter x - try - if x.RefCount = 1 then - prev.Next <- x.Next - x.Next.Prev <- prev - x.Next <- Unchecked.defaultof<_> // set pointers to null so no additional RefCount=0 check is needed in Before after aquiring the lock - x.Prev <- Unchecked.defaultof<_> - x.RefCount <- 0 - else - x.RefCount <- x.RefCount - 1 - finally - Monitor.Exit x - Monitor.Exit prev + if x.RefCount = 1 then + prev.Next <- x.Next + x.Next.Prev <- prev + x.Next <- Unchecked.defaultof<_> // set pointers to null so no additional RefCount=0 check is needed in Before after aquiring the lock + x.Prev <- Unchecked.defaultof<_> + x.RefCount <- 0 + else + x.RefCount <- x.RefCount - 1 + + Monitor.Exit x + Monitor.Exit prev /// Compare me to another node. member x.CompareTo(o : IndexNode) = match Monitor.TryEnter x, Monitor.TryEnter o with | true, true -> - try - compare x.Key o.Key - finally - Monitor.Exit x - Monitor.Exit o + let res = compare x.Key o.Key + Monitor.Exit x + Monitor.Exit o + res | true, false -> Monitor.Exit x @@ -183,20 +181,21 @@ type Index private(real : IndexNode) = Monitor.Exit prev x.Before() else - try + let res = if prev = real.Root then prev.InsertAfter() |> Index else prev.RefCount <- prev.RefCount + 1 prev |> Index - finally - Monitor.Exit prev + + Monitor.Exit prev + res member l.Between(r : Index) = let l = l.Value let r = r.Value Monitor.Enter l - try + let res = let next = l.Next if next = r then l.InsertAfter() |> Index @@ -205,8 +204,9 @@ type Index private(real : IndexNode) = next.RefCount <- next.RefCount + 1 ) next |> Index - finally - Monitor.Exit l + + Monitor.Exit l + res override x.Finalize() = real.Delete() From 342335abe99fde2bfd4a8f086b93cd6ee3708319 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 18 Sep 2024 14:37:02 +0200 Subject: [PATCH 30/65] Index: Monitor.Enter/Exit in all places --- .../Datastructures/Index.fs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index 3a0161b..85ec2e8 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -163,16 +163,18 @@ type Index private(real : IndexNode) = member private x.Value = real member x.After() = - lock real (fun () -> + Monitor.Enter real + let res = let next = real.Next if next <> real.Root then - lock next (fun () -> - next.RefCount <- next.RefCount + 1 - ) + Monitor.Enter next + next.RefCount <- next.RefCount + 1 + Monitor.Exit next next |> Index else real.InsertAfter() |> Index - ) + Monitor.Exit real + res member x.Before() = let prev = real.Prev @@ -200,9 +202,9 @@ type Index private(real : IndexNode) = if next = r then l.InsertAfter() |> Index else - lock next (fun () -> - next.RefCount <- next.RefCount + 1 - ) + Monitor.Enter next + next.RefCount <- next.RefCount + 1 + Monitor.Exit next next |> Index Monitor.Exit l From fa77c38859797829385a64386a55f92953e0ba35 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 18 Sep 2024 14:53:20 +0200 Subject: [PATCH 31/65] Index: inlined node equality --- src/FSharp.Data.Adaptive/Datastructures/Index.fs | 12 ++++++------ .../Benchmarks/IndexBenchmarks.fs | 5 +++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/Index.fs b/src/FSharp.Data.Adaptive/Datastructures/Index.fs index 85ec2e8..89b1a58 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/Index.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/Index.fs @@ -73,7 +73,7 @@ type IndexNode = /// initially we increment the distance by Uint64.MaxValue/2 let mutable distance = - if next = x then UInt64.MaxValue + if Object.ReferenceEquals(next, x) then UInt64.MaxValue else next.Tag - x.Tag // we're out of luck and there's no space for an additional node. @@ -94,7 +94,7 @@ type IndexNode = member x.Delete() = let prev = x.Prev Monitor.Enter prev - if prev.Next <> x then // NOTE: prev.Next might point to an inserted node between after lock has been aquired + if not (Object.ReferenceEquals(prev.Next, x)) then // NOTE: prev.Next might point to an inserted node between after lock has been aquired Monitor.Exit prev x.Delete() else @@ -166,7 +166,7 @@ type Index private(real : IndexNode) = Monitor.Enter real let res = let next = real.Next - if next <> real.Root then + if not (Object.ReferenceEquals(next, real.Root)) then Monitor.Enter next next.RefCount <- next.RefCount + 1 Monitor.Exit next @@ -179,12 +179,12 @@ type Index private(real : IndexNode) = member x.Before() = let prev = real.Prev Monitor.Enter prev - if prev.Next <> real then // NOTE: prev.Next might point to an inserted node between or be null if it just has been deleted + if not (Object.ReferenceEquals(prev.Next, real)) then // NOTE: prev.Next might point to an inserted node between or be null if it just has been deleted Monitor.Exit prev x.Before() else let res = - if prev = real.Root then + if Object.ReferenceEquals(prev, real.Root) then prev.InsertAfter() |> Index else prev.RefCount <- prev.RefCount + 1 @@ -199,7 +199,7 @@ type Index private(real : IndexNode) = Monitor.Enter l let res = let next = l.Next - if next = r then + if Object.ReferenceEquals(next, r) then l.InsertAfter() |> Index else Monitor.Enter next diff --git a/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs index 62b5f90..b635305 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/Benchmarks/IndexBenchmarks.fs @@ -97,6 +97,11 @@ type IndexEqualsBenchmarks() = //|--------- |------------- |---------- |--------:|---------:|---------:|------------:|------------:|-----------:| //| Garbage0 | 5000000 | 100 | 1.531 s | 0.0040 s | 0.0035 s | 169000.0000 | 168000.0000 | 1016.93 MB | +// + inlined node equality (and other small changes, however those do not have any significant effect) +//| Method | GarbageCount | ListCount | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | +//|--------- |------------- |---------- |--------:|---------:|---------:|------------:|------------:|-----------:| +//| Garbage0 | 5000000 | 100 | 1.479 s | 0.0052 s | 0.0044 s | 170000.0000 | 169000.0000 | 1017.98 MB | + [] [] type IndexGarbageBenchmarks() = From 8b3eaf9fde1ffd427599d000dd81201539daf5b9 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Thu, 19 Sep 2024 00:26:20 +0200 Subject: [PATCH 32/65] avoid redundant lazy creator functions in constant collections --- src/Demo/Scratch/ABag.fs | 2 +- src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs | 2 +- src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs | 2 +- src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Demo/Scratch/ABag.fs b/src/Demo/Scratch/ABag.fs index d31ca41..6fbd84d 100644 --- a/src/Demo/Scratch/ABag.fs +++ b/src/Demo/Scratch/ABag.fs @@ -125,7 +125,7 @@ module ABag = ConstantBag(Seq.delay seq) :> abag<_> let private constantMap (seq : unit -> HashMap) = - ConstantBag(lazy(seq())) :> abag<_> + ConstantBag(Lazy>(seq)) :> abag<_> let ofReader (create : unit -> #IOpReader>) = AdaptiveBag(fun () -> create() :> IOpReader<_>) :> abag<_> diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 2f6e2c2..ec77adb 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1101,7 +1101,7 @@ module AdaptiveHashMapImplementation = /// Creates a constant map using the creation function. let inline constant (content : unit -> HashMap<'Key, 'Value>) = - ConstantMap(lazy(content())) :> amap<_,_> + ConstantMap(Lazy>(content)) :> amap<_,_> /// Creates an adaptive map using the reader. let inline create (reader : unit -> #IOpReader>) = diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index daa72a9..4d085f9 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -1140,7 +1140,7 @@ module ASet = /// Creates a constant set using the creation function. let constant (value : unit -> HashSet<'T>) = - ConstantSet(lazy(value())) :> aset<_> + ConstantSet(Lazy>(value)) :> aset<_> /// Creates an aset using the given reader-creator. let ofReader (create : unit -> #IOpReader>) = diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index dffeed7..0c465ee 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -1339,7 +1339,7 @@ module AList = /// Creates an alist holding the given values. let constant (value : unit -> IndexList<'T>) = - lazy value() |> ConstantList :> alist<_> + Lazy>(value) |> ConstantList :> alist<_> /// A constant alist holding a single value. let single (value : 'T) = From 466e7159c3e63f9ac9b99a20d0af498265e9b546 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 22 Sep 2024 15:06:27 +0200 Subject: [PATCH 33/65] added AList.mapToASet --- src/CSharp.Data.Adaptive/AList.fs | 4 ++ .../CollectionExtensions.fs | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/CSharp.Data.Adaptive/AList.fs b/src/CSharp.Data.Adaptive/AList.fs index 168c85e..daaf25f 100644 --- a/src/CSharp.Data.Adaptive/AList.fs +++ b/src/CSharp.Data.Adaptive/AList.fs @@ -293,3 +293,7 @@ type AdaptiveIndexList private() = static member TryAt(this: alist<'T>, index: Index) = this |> AList.tryGet index + [] + static member ToAdaptiveHashSet(this: alist<'T1>, mapping : Func<'T1, 'T2>) = + this |> AList.mapToASet mapping.Invoke + diff --git a/src/FSharp.Data.Adaptive/CollectionExtensions.fs b/src/FSharp.Data.Adaptive/CollectionExtensions.fs index c348049..a4bcae7 100644 --- a/src/FSharp.Data.Adaptive/CollectionExtensions.fs +++ b/src/FSharp.Data.Adaptive/CollectionExtensions.fs @@ -129,6 +129,35 @@ module CollectionExtensions = delta <- delta.Add (Add v) delta + /// Reader for AList.mapToASet + [] + type ListSetMapReader<'T1, 'T2>(list: alist<'T1>, mapping : 'T1 -> 'T2) = + inherit AbstractReader>(HashSetDelta.empty) + + let reader = list.GetReader() + let cache = Cache mapping + + override x.Compute(token: AdaptiveToken) = + let old = reader.State.Content + let changes = reader.GetChanges(token).Content + let mutable delta = HashSetDelta.empty + for KeyValue(i, op) in changes do + match op with + | Remove -> + match MapExt.tryFindV i old with + | ValueSome v -> + delta <- delta.Add (Rem (cache.Revoke v)) + | ValueNone -> () + | Set v -> + match MapExt.tryFindV i old with + | ValueSome ov -> + if not (DefaultEquality.equals v ov) then + delta <- delta.Add (Add (cache.Invoke v)) + delta <- delta.Add (Rem (cache.Revoke ov)) + | ValueNone -> + delta <- delta.Add (Add (cache.Invoke v)) + delta + /// Reader for AList.toIndexedASet [] type IndexedListSetReader<'T>(list: alist<'T>) = @@ -432,6 +461,16 @@ module CollectionExtensions = /// Creates an aset holding all elements of the given list. let toASet (list: alist<'T>) = ASet.ofAList list + + /// Creates an aset holding all elements of the given list. + let mapToASet (mapping : 'T1 -> 'T2) (list: alist<'T1>) = + if list.IsConstant then + list.Content + |> AVal.force + |> Seq.map mapping + |> ASet.ofSeq + else + ASet.ofReader (fun () -> ListSetMapReader(list, mapping)) /// Creates an aset holding all index/elements pairs of the given list. let toASetIndexed (list: alist<'T>) = ASet.ofAListIndexed list From c0a8b47920577064699068102b27875c900f29ff Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 22 Sep 2024 15:14:17 +0200 Subject: [PATCH 34/65] avoid unnecessary allocations in SetReader --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index ec77adb..611c430 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1068,32 +1068,33 @@ module AdaptiveHashMapImplementation = let state = DefaultDictionary.create<'Key, HashSet<'Value>>() override x.Compute (token : AdaptiveToken) = - reader.GetChanges token |> Seq.choose (fun op -> + let mutable delta = HashMap.empty + for op in reader.GetChanges token do match op with | Add(_, (k, v)) -> match state.TryGetValue k with | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - Some (k, Set (view newSet)) + delta <- delta.Add (k, Set (view newSet)) | _ -> let newSet = HashSet.single v state.[k] <- newSet - Some (k, Set (view newSet)) + delta <- delta.Add (k, Set (view newSet)) | Rem(_, (k, v)) -> match state.TryGetValue k with | (true, set) -> let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - Some (k, Remove) + delta <- delta.Add (k, Remove) else state.[k] <- newSet - Some (k, Set (view newSet)) + delta <- delta.Add (k, Set (view newSet)) | _ -> - None - ) - |> HashMapDelta.ofSeq + () + + delta |> HashMapDelta.ofHashMap /// Gets the current content of the amap as HashMap. let inline force (map : amap<'Key, 'Value>) = From ab21ac0176040a39a91cca8973ced0e6d86b11db Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 22 Sep 2024 15:58:48 +0200 Subject: [PATCH 35/65] added HashSet.head: avoid allocation in AMap.ofASetIgnoreDuplicates --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 2 +- .../Datastructures/HashCollections.fs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 611c430..2fb88c6 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1154,7 +1154,7 @@ module AMap = result ) else - create (fun () -> SetReader(elements, Seq.head)) + create (fun () -> SetReader(elements, HashSet.head)) /// Creates an amap from the given set while keeping all duplicate values for a key in a HashSet. let ofASet (elements: aset<'Key * 'Value>) = diff --git a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs index 4f8e905..b75ba4d 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs @@ -1073,6 +1073,16 @@ module internal HashImplementation = let node = node :?> Inner<'K> iter action node.Left iter action node.Right + + let rec head (node : SetNode<'K>) = + if isNull node then + failwith "HashSet does not contain any elements" + elif node.IsLeaf then + let node = node :?> SetLeaf<'K> + node.Key + else + let node = node :?> Inner<'K> + head node.Left let rec fold (folder : OptimizedClosures.FSharpFunc<'S, 'K, 'S>) (state : 'S) (node : SetNode<'K>) = if isNull node then @@ -3425,6 +3435,10 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) member x.Filter(predicate : 'K -> bool) = HashSet(comparer, SetNode.filter predicate root) + [] + member x.First() : 'K = + SetNode.head root + // ==================================================================================== // Binary Operations: overlaps/union/computeDelta/etc. // ==================================================================================== @@ -4203,6 +4217,9 @@ module HashSet = /// Tests if an entry for the given key exists. `O(1)` let inline contains (value : 'T) (set : HashSet<'T>) = set.Contains value + /// Returns the first element in the set + let inline head (set : HashSet<'T>) : 'T = set.First() + /// Creates a seq holding all values. /// `O(N)` let inline toSeq (set : HashSet<'K>) = set :> seq<_> From 39f3657caf25a985f574e087714d99126976c1cf Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 22 Sep 2024 17:18:48 +0200 Subject: [PATCH 36/65] added AMap.ofASetMapped/ofASetMappedIgnoreDuplicates (optimized ASet.groupBy) --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 81 ++++++++++++++++++- .../AdaptiveHashMap/AdaptiveHashMap.fsi | 6 ++ .../CollectionExtensions.fs | 3 +- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 2fb88c6..828cb52 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1096,6 +1096,48 @@ module AdaptiveHashMapImplementation = delta |> HashMapDelta.ofHashMap + /// Reader used for ofASet operations. + /// It's safe to assume that the view function will only be called with non-empty HashSets. + /// Internally assumes that the view function is cheap. + [] + type MappedSetReader<'Key, 'Value, 'View>(input : aset<'Value>, getKey : 'Value -> 'Key, view : HashSet<'Value> -> 'View) = + inherit AbstractReader>(HashMapDelta.empty) + + let reader = input.GetReader() + let state = DefaultDictionary.create<'Key, HashSet<'Value>>() + let cache = Cache<'Value, 'Key> getKey + + override x.Compute (token : AdaptiveToken) = + let mutable delta = HashMap.empty + for op in reader.GetChanges token do + match op with + | Add(_, v) -> + let k = cache.Invoke v + match state.TryGetValue k with + | (true, set) -> + let newSet = HashSet.add v set + state.[k] <- newSet + delta <- delta.Add (k, Set (view newSet)) + | _ -> + let newSet = HashSet.single v + state.[k] <- newSet + delta <- delta.Add (k, Set (view newSet)) + | Rem(_, v) -> + let k = cache.Revoke v + match state.TryGetValue k with + | (true, set) -> + let newSet = HashSet.remove v set + if newSet.IsEmpty then + state.Remove k |> ignore + delta <- delta.Add (k, Remove) + else + state.[k] <- newSet + delta <- delta.Add (k, Set (view newSet)) + | _ -> + () + + delta |> HashMapDelta.ofHashMap + /// Gets the current content of the amap as HashMap. let inline force (map : amap<'Key, 'Value>) = AVal.force map.Content @@ -1155,6 +1197,20 @@ module AMap = ) else create (fun () -> SetReader(elements, HashSet.head)) + + /// Creates an amap from the given set and takes an arbitrary value for duplicate entries. + let ofASetMappedIgnoreDuplicates (getKey : 'Value -> 'Key) (elements: aset<'Value>) = + if elements.IsConstant then + constant (fun () -> + let mutable result = HashMap.empty + for v in AVal.force elements.Content do + let k = getKey v + result <- HashMap.add k v result + + result + ) + else + create (fun () -> MappedSetReader(elements, getKey, HashSet.head)) /// Creates an amap from the given set while keeping all duplicate values for a key in a HashSet. let ofASet (elements: aset<'Key * 'Value>) = @@ -1163,16 +1219,35 @@ module AMap = let mutable result = HashMap.empty for (k,v) in AVal.force elements.Content do result <- - result |> HashMap.alter k (fun o -> + result |> HashMap.alterV k (fun o -> match o with - | Some o -> HashSet.add v o |> Some - | None -> HashSet.single v |> Some + | ValueSome o -> HashSet.add v o |> ValueSome + | ValueNone -> HashSet.single v |> ValueSome ) result ) else create (fun () -> SetReader(elements, id)) + + /// Creates an amap from the given set while keeping all duplicate values for a key in a HashSet. + let ofASetMapped (getKey : 'Value -> 'Key) (elements: aset<'Value>) = + if elements.IsConstant then + constant (fun () -> + let mutable result = HashMap.empty + for v in AVal.force elements.Content do + result <- + let k = getKey v + result |> HashMap.alterV k (fun o -> + match o with + | ValueSome o -> HashSet.add v o |> ValueSome + | ValueNone -> HashSet.single v |> ValueSome + ) + + result + ) + else + create (fun () -> MappedSetReader(elements, getKey, id)) /// Creates an amap using the given reader-creator. let ofReader (creator : unit -> #IOpReader>) = diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fsi b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fsi index 0a1613c..1254cfb 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fsi +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fsi @@ -51,9 +51,15 @@ module AMap = /// Creates an amap from the given set while keeping all duplicate values for a key in a HashSet. val ofASet : elements:aset<'Key * 'Value> -> amap<'Key, HashSet<'Value>> + + /// Creates an amap from the given set while keeping all duplicate values for a key in a HashSet. + val ofASetMapped : getKey:('Value -> 'Key) -> elements:aset<'Value> -> amap<'Key, HashSet<'Value>> /// Creates an amap from the given set and takes an arbitrary value for duplicate entries. val ofASetIgnoreDuplicates : elements:aset<'Key * 'Value> -> amap<'Key, 'Value> + + /// Creates an amap from the given set and takes an arbitrary value for duplicate entries. + val ofASetMappedIgnoreDuplicates : getKey:('Value -> 'Key) -> elements:aset<'Value> -> amap<'Key, 'Value> /// Creates an amap using the given reader-creator. val ofReader : creator: (unit -> #IOpReader>) -> amap<'Key, 'Value> diff --git a/src/FSharp.Data.Adaptive/CollectionExtensions.fs b/src/FSharp.Data.Adaptive/CollectionExtensions.fs index a4bcae7..7a7cd9c 100644 --- a/src/FSharp.Data.Adaptive/CollectionExtensions.fs +++ b/src/FSharp.Data.Adaptive/CollectionExtensions.fs @@ -451,8 +451,7 @@ module CollectionExtensions = /// Groups the aset by the given mapping and returns an amap with potentially colliding entries in a HashSet<'T>. let groupBy (mapping: 'T -> 'K) (set: aset<'T>) = - // TODO: better implementation. - set |> ASet.map (fun v -> mapping v, v) |> AMap.ofASet + set |> AMap.ofASetMapped mapping /// Functional operators for alist<_> From 59bada5552386e5f92cfbde1434eb14dd7ee8bee Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 22 Sep 2024 19:50:56 +0200 Subject: [PATCH 37/65] revert SetReader optimization: HashMapDelta.ofSeq uses more efficient in-place HashMap build --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 828cb52..922ac3a 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1068,33 +1068,32 @@ module AdaptiveHashMapImplementation = let state = DefaultDictionary.create<'Key, HashSet<'Value>>() override x.Compute (token : AdaptiveToken) = - let mutable delta = HashMap.empty - for op in reader.GetChanges token do + reader.GetChanges token |> Seq.choose (fun op -> match op with | Add(_, (k, v)) -> match state.TryGetValue k with | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - delta <- delta.Add (k, Set (view newSet)) + Some (k, Set (view newSet)) | _ -> let newSet = HashSet.single v state.[k] <- newSet - delta <- delta.Add (k, Set (view newSet)) + Some (k, Set (view newSet)) | Rem(_, (k, v)) -> match state.TryGetValue k with | (true, set) -> let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - delta <- delta.Add (k, Remove) + Some (k, Remove) else state.[k] <- newSet - delta <- delta.Add (k, Set (view newSet)) + Some (k, Set (view newSet)) | _ -> - () - - delta |> HashMapDelta.ofHashMap + None + ) + |> HashMapDelta.ofSeq /// Reader used for ofASet operations. /// It's safe to assume that the view function will only be called with non-empty HashSets. @@ -1108,8 +1107,7 @@ module AdaptiveHashMapImplementation = let cache = Cache<'Value, 'Key> getKey override x.Compute (token : AdaptiveToken) = - let mutable delta = HashMap.empty - for op in reader.GetChanges token do + reader.GetChanges token |> Seq.choose (fun op -> match op with | Add(_, v) -> let k = cache.Invoke v @@ -1117,11 +1115,11 @@ module AdaptiveHashMapImplementation = | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - delta <- delta.Add (k, Set (view newSet)) + Some (k, Set (view newSet)) | _ -> let newSet = HashSet.single v state.[k] <- newSet - delta <- delta.Add (k, Set (view newSet)) + Some (k, Set (view newSet)) | Rem(_, v) -> let k = cache.Revoke v match state.TryGetValue k with @@ -1129,14 +1127,14 @@ module AdaptiveHashMapImplementation = let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - delta <- delta.Add (k, Remove) + Some (k, Remove) else state.[k] <- newSet - delta <- delta.Add (k, Set (view newSet)) + Some (k, Set (view newSet)) | _ -> - () - - delta |> HashMapDelta.ofHashMap + None + ) + |> HashMapDelta.ofSeq /// Gets the current content of the amap as HashMap. let inline force (map : amap<'Key, 'Value>) = From c01bad00ab7a189dda08e2f7fbcb76d273c2c03e Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 22 Sep 2024 20:09:26 +0200 Subject: [PATCH 38/65] using struct tuples in AMap SetReader/MappedSetReader --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 922ac3a..8df522b 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1075,25 +1075,25 @@ module AdaptiveHashMapImplementation = | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - Some (k, Set (view newSet)) + Some struct(k, Set (view newSet)) | _ -> let newSet = HashSet.single v state.[k] <- newSet - Some (k, Set (view newSet)) + Some struct(k, Set (view newSet)) | Rem(_, (k, v)) -> match state.TryGetValue k with | (true, set) -> let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - Some (k, Remove) + Some struct(k, Remove) else state.[k] <- newSet - Some (k, Set (view newSet)) + Some struct(k, Set (view newSet)) | _ -> None ) - |> HashMapDelta.ofSeq + |> HashMap.OfSeq |> HashMapDelta.ofHashMap /// Reader used for ofASet operations. /// It's safe to assume that the view function will only be called with non-empty HashSets. @@ -1115,11 +1115,11 @@ module AdaptiveHashMapImplementation = | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - Some (k, Set (view newSet)) + Some struct(k, Set (view newSet)) | _ -> let newSet = HashSet.single v state.[k] <- newSet - Some (k, Set (view newSet)) + Some struct(k, Set (view newSet)) | Rem(_, v) -> let k = cache.Revoke v match state.TryGetValue k with @@ -1127,14 +1127,14 @@ module AdaptiveHashMapImplementation = let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - Some (k, Remove) + Some struct(k, Remove) else state.[k] <- newSet - Some (k, Set (view newSet)) + Some struct(k, Set (view newSet)) | _ -> None ) - |> HashMapDelta.ofSeq + |> HashMap.OfSeq |> HashMapDelta.ofHashMap /// Gets the current content of the amap as HashMap. let inline force (map : amap<'Key, 'Value>) = From cc1a5801a283fc41267fe73a81860d06d902435f Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 22 Sep 2024 21:06:14 +0200 Subject: [PATCH 39/65] using IndexListDelta.ofSeq --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 4 +- .../AdaptiveIndexList/AdaptiveIndexList.fs | 59 ++++++++++--------- .../CollectionExtensions.fs | 37 ++++++------ .../Datastructures/HashMapDelta.fs | 4 ++ .../Datastructures/IndexListDelta.fs | 4 ++ 5 files changed, 60 insertions(+), 48 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 8df522b..187e060 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1093,7 +1093,7 @@ module AdaptiveHashMapImplementation = | _ -> None ) - |> HashMap.OfSeq |> HashMapDelta.ofHashMap + |> HashMapDelta.ofSeqV /// Reader used for ofASet operations. /// It's safe to assume that the view function will only be called with non-empty HashSets. @@ -1134,7 +1134,7 @@ module AdaptiveHashMapImplementation = | _ -> None ) - |> HashMap.OfSeq |> HashMapDelta.ofHashMap + |> HashMapDelta.ofSeqV /// Gets the current content of the amap as HashMap. let inline force (map : amap<'Key, 'Value>) = diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 0c465ee..6d29c26 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -788,12 +788,14 @@ module internal AdaptiveIndexListImplementation = if targets.Remove oi then if not (obj.ReferenceEquals(reader, null)) then let result = - let mutable delta = IndexListDelta.empty - for KeyValue(ii, v) in reader.State.Content do + reader.State.Content + |> MapExt.toSeq + |> Seq.choose (fun (ii, v) -> match mapping.Revoke(oi,ii) with - | ValueSome v -> delta <- delta.Add (v, Remove) - | ValueNone -> () - delta + | ValueSome v -> Some struct(v, Remove) + | ValueNone -> None + ) + |> IndexListDelta.ofSeqV if targets.Count = 0 then dirty.Remove x |> ignore @@ -821,18 +823,19 @@ module internal AdaptiveIndexListImplementation = ops |> IndexListDelta.collect (fun ii op -> match op with | Remove -> - let mutable delta = IndexListDelta.empty - for oi in targets do + targets + |> Seq.choose (fun oi -> match mapping.Revoke(oi, ii) with - | ValueSome i -> delta <- delta.Add (i, Remove) - | ValueNone -> () - delta + | ValueSome i -> Some struct(i, Remove) + | ValueNone -> None + ) + |> IndexListDelta.ofSeqV | Set v -> - let mutable delta = IndexListDelta.empty - for oi in targets do - delta <- delta.Add (mapping.Invoke(oi, ii), Set v) - delta + targets + |> Seq.map (fun oi -> struct(mapping.Invoke(oi, ii), Set v)) + |> IndexListDelta.ofSeqV + ) else @@ -1021,7 +1024,7 @@ module internal AdaptiveIndexListImplementation = match cache.TryGetValue i with | (true, b) -> match idx.Revoke((b, i)) with - | ValueSome oi -> Some (oi, Remove) + | ValueSome oi -> Some struct(oi, Remove) | ValueNone -> None | _ -> None @@ -1029,19 +1032,19 @@ module internal AdaptiveIndexListImplementation = cache.[i] <- b let oi = idx.Invoke((b, i)) match rem with - | Some op -> [op; (oi, Set v)] - | None -> [(oi, Set v)] + | Some op -> [op; struct(oi, Set v)] + | None -> [struct(oi, Set v)] | Remove -> match cache.TryGetValue i with | (true, b) -> cache.Remove i |> ignore match idx.Revoke((b, i)) with - | ValueSome oi -> [(oi, Remove)] + | ValueSome oi -> [struct(oi, Remove)] | ValueNone -> [] | _ -> [] ) - |> IndexListDelta.ofSeq + |> IndexListDelta.ofSeqV /// Reader for sortWith operations [] @@ -1067,27 +1070,27 @@ module internal AdaptiveIndexListImplementation = match op with | Set v -> let rem = - match MapExt.tryFind i old with - | Some ov -> + match MapExt.tryFindV i old with + | ValueSome ov -> match idx.Revoke(UCmp(cmp, struct(ov, i))) with - | ValueSome oi -> Some (oi, Remove) + | ValueSome oi -> Some struct(oi, Remove) | ValueNone -> None | _ -> None let oi = idx.Invoke(UCmp(cmp, struct(v, i))) match rem with - | Some op -> [op; (oi, Set v)] - | None -> [(oi, Set v)] + | Some op -> [op; struct(oi, Set v)] + | None -> [struct(oi, Set v)] | Remove -> - match MapExt.tryFind i old with - | Some ov -> + match MapExt.tryFindV i old with + | ValueSome ov -> match idx.Revoke(UCmp(cmp, struct(ov, i))) with - | ValueSome oi -> [(oi, Remove)] + | ValueSome oi -> [struct(oi, Remove)] | ValueNone -> [] | _ -> [] ) - |> IndexListDelta.ofSeq + |> IndexListDelta.ofSeqV /// Reader for ofAVal operations [] diff --git a/src/FSharp.Data.Adaptive/CollectionExtensions.fs b/src/FSharp.Data.Adaptive/CollectionExtensions.fs index 7a7cd9c..c714eaa 100644 --- a/src/FSharp.Data.Adaptive/CollectionExtensions.fs +++ b/src/FSharp.Data.Adaptive/CollectionExtensions.fs @@ -50,14 +50,14 @@ module CollectionExtensions = | Add(_, v) -> let k = cache.Invoke v let idx = mapping.Invoke k - Some (idx, Set v) + Some struct(idx, Set v) | Rem(_, v) -> let k = cache.Revoke v match mapping.Revoke k with - | ValueSome idx -> Some (idx, Remove) + | ValueSome idx -> Some struct(idx, Remove) | ValueNone -> None ) - |> IndexListDelta.ofSeq + |> IndexListDelta.ofSeqV /// Reader for ASet.sortWith [] @@ -72,13 +72,13 @@ module CollectionExtensions = match op with | Add(_, v) -> let idx = mapping.Invoke v - Some (idx, Set v) + Some struct(idx, Set v) | Rem(_, v) -> match mapping.Revoke v with - | Some idx -> Some (idx, Remove) + | Some idx -> Some struct(idx, Remove) | None -> None ) - |> IndexListDelta.ofSeq + |> IndexListDelta.ofSeqV /// Reader for AMap.keys [] @@ -200,19 +200,20 @@ module CollectionExtensions = let newIndex = Cache newIndex override x.Compute(token) = - let changes = reader.GetChanges token - let mutable delta = IndexListDelta.empty - for d in changes do + reader.GetChanges token + |> HashSetDelta.toSeq + |> Seq.map (fun d -> match d with - | Add(1,v) -> - let i = newIndex.Invoke v - delta <- delta.Add (i, Set v) - | Rem(1,v) -> - let i = newIndex.Revoke v - delta <- delta.Add (i, Remove) - | _ -> - unexpected() - delta + | Add(1,v) -> + let i = newIndex.Invoke v + struct(i, Set v) + | Rem(1,v) -> + let i = newIndex.Revoke v + struct(i, Remove) + | _ -> + unexpected() + ) + |> IndexListDelta.ofSeqV [] type MapToListReader<'T>(input : amap) = diff --git a/src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs b/src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs index e801131..b5ddea4 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs @@ -77,6 +77,10 @@ module HashMapDelta = /// A HashMapDelta containing all the given deltas. let ofSeq (elements : seq<'K * ElementOperation<'V>>) = HashMapDelta(HashMap.ofSeq elements) + + /// A HashMapDelta containing all the given deltas. + let ofSeqV (elements : seq)>) = + HashMapDelta(HashMap.ofSeqV elements) /// A HashMapDelta containing all the given deltas. let ofList (elements : list<'K * ElementOperation<'V>>) = diff --git a/src/FSharp.Data.Adaptive/Datastructures/IndexListDelta.fs b/src/FSharp.Data.Adaptive/Datastructures/IndexListDelta.fs index cde781d..803fa47 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/IndexListDelta.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/IndexListDelta.fs @@ -212,6 +212,10 @@ module IndexListDelta = let ofSeq (operations : seq>) = IndexListDelta(MapExt.ofSeq operations) + /// Creates an IndexListDelta containing all the given operations. + let ofSeqV (operations : seq)>) = + IndexListDelta(MapExt.ofSeqV operations) + /// Creates an IndexListDelta containing all the given operations. let ofList (operations : list>) = IndexListDelta(MapExt.ofList operations) From 4e3a75457f658a9b359351be418604adca37d216 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Sun, 22 Sep 2024 22:51:25 +0200 Subject: [PATCH 40/65] struct enumerators + inplace delta HashMap/MapExt construction --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 54 +++++++++++++------ .../AdaptiveIndexList/AdaptiveIndexList.fs | 39 +++++++------- 2 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 187e060..f28830d 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -2,6 +2,7 @@ namespace FSharp.Data.Adaptive open System open FSharp.Data.Traceable +open HashImplementation /// An adaptive reader for amap that allows to pull operations and exposes its current state. type IHashMapReader<'Key, 'Value> = IOpReader, HashMapDelta<'Key, 'Value>> @@ -1068,32 +1069,42 @@ module AdaptiveHashMapImplementation = let state = DefaultDictionary.create<'Key, HashSet<'Value>>() override x.Compute (token : AdaptiveToken) = - reader.GetChanges token |> Seq.choose (fun op -> + let cmp = DefaultEqualityComparer<'Key>.Instance + let mutable delta = null + for op in reader.GetChanges token do match op with | Add(_, (k, v)) -> match state.TryGetValue k with | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - Some struct(k, Set (view newSet)) + let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu + let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta + delta <- n | _ -> let newSet = HashSet.single v state.[k] <- newSet - Some struct(k, Set (view newSet)) + let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu + let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta + delta <- n | Rem(_, (k, v)) -> match state.TryGetValue k with | (true, set) -> let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - Some struct(k, Remove) + let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu + let struct(_, n) = MapNode.addInPlace cmp hash k Remove delta + delta <- n else state.[k] <- newSet - Some struct(k, Set (view newSet)) + let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu + let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta + delta <- n | _ -> - None - ) - |> HashMapDelta.ofSeqV + () + + HashMap(cmp, delta) |> HashMapDelta.ofHashMap /// Reader used for ofASet operations. /// It's safe to assume that the view function will only be called with non-empty HashSets. @@ -1107,7 +1118,9 @@ module AdaptiveHashMapImplementation = let cache = Cache<'Value, 'Key> getKey override x.Compute (token : AdaptiveToken) = - reader.GetChanges token |> Seq.choose (fun op -> + let cmp = DefaultEqualityComparer<'Key>.Instance + let mutable delta = null + for op in reader.GetChanges token do match op with | Add(_, v) -> let k = cache.Invoke v @@ -1115,11 +1128,15 @@ module AdaptiveHashMapImplementation = | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - Some struct(k, Set (view newSet)) + let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu + let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta + delta <- n | _ -> let newSet = HashSet.single v state.[k] <- newSet - Some struct(k, Set (view newSet)) + let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu + let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta + delta <- n | Rem(_, v) -> let k = cache.Revoke v match state.TryGetValue k with @@ -1127,14 +1144,19 @@ module AdaptiveHashMapImplementation = let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - Some struct(k, Remove) + let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu + let struct(_, n) = MapNode.addInPlace cmp hash k Remove delta + delta <- n else state.[k] <- newSet - Some struct(k, Set (view newSet)) + let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu + let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta + delta <- n | _ -> - None - ) - |> HashMapDelta.ofSeqV + () + + HashMap(cmp, delta) |> HashMapDelta.ofHashMap + /// Gets the current content of the amap as HashMap. let inline force (map : amap<'Key, 'Value>) = diff --git a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs index 6d29c26..1bfb1ca 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs @@ -768,8 +768,10 @@ module internal AdaptiveIndexListImplementation = type MultiReader<'a>(mapping : IndexMapping, list : alist<'a>, release : alist<'a> -> unit) = inherit AbstractReader>(IndexListDelta.empty) - let targets = DefaultHashSet.create() + static let cmp = LanguagePrimitives.FastGenericComparer + let targets = DefaultHashSet.create() + let mutable reader = Unchecked.defaultof> let getReader() = @@ -788,14 +790,12 @@ module internal AdaptiveIndexListImplementation = if targets.Remove oi then if not (obj.ReferenceEquals(reader, null)) then let result = - reader.State.Content - |> MapExt.toSeq - |> Seq.choose (fun (ii, v) -> + let mutable delta = null + for KeyValue(ii, v) in reader.State.Content do match mapping.Revoke(oi,ii) with - | ValueSome v -> Some struct(v, Remove) - | ValueNone -> None - ) - |> IndexListDelta.ofSeqV + | ValueSome v -> delta <- MapExtImplementation.addInPlace cmp v Remove delta + | ValueNone -> () + IndexListDelta.ofMap (MapExt(cmp, delta)) if targets.Count = 0 then dirty.Remove x |> ignore @@ -819,22 +819,25 @@ module internal AdaptiveIndexListImplementation = override x.Compute(token) = if not (obj.ReferenceEquals(reader, null)) then let ops = reader.GetChanges token - + ops |> IndexListDelta.collect (fun ii op -> match op with | Remove -> - targets - |> Seq.choose (fun oi -> + let mutable root = null + for oi in targets do match mapping.Revoke(oi, ii) with - | ValueSome i -> Some struct(i, Remove) - | ValueNone -> None - ) - |> IndexListDelta.ofSeqV + | ValueSome i -> + root <- MapExtImplementation.addInPlace cmp i Remove root + | ValueNone -> () + IndexListDelta.ofMap (MapExt(cmp, root)) | Set v -> - targets - |> Seq.map (fun oi -> struct(mapping.Invoke(oi, ii), Set v)) - |> IndexListDelta.ofSeqV + let mutable root = null + for oi in targets do + let i = mapping.Invoke(oi, ii) + let op = Set v + root <- MapExtImplementation.addInPlace cmp i op root + IndexListDelta.ofMap (MapExt(cmp, root)) ) From 0d756bdfb3472660c5210e99446e8f24dc889367 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 23 Sep 2024 00:48:38 +0200 Subject: [PATCH 41/65] using struct tuples in MapAReader/ChooseAReader --- .../AdaptiveHashSet/AdaptiveHashSet.fs | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index 4d085f9..39af881 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -984,16 +984,17 @@ module AdaptiveHashSetImplementation = let reader = input.GetReader() do reader.Tag <- "Reader" let mapping = Cache mapping - let cache = DefaultDictionary.create, ref>() + let cache = DefaultDictionary.create, ref>() member x.Invoke(token : AdaptiveToken, v : 'A) = let m = mapping.Invoke v let v = m.GetValue token match cache.TryGetValue m with | (true, r) -> - r.Value <- (fst r.Value + 1, v) + let struct(rc, _) = r.Value + r.Value <- struct(rc + 1, v) | _ -> - let r = ref (1, v) + let r = ref struct(1, v) cache.[m] <- r v @@ -1003,7 +1004,7 @@ module AdaptiveHashSetImplementation = match cache.TryGetValue m with | (true, r) -> - let (cnt, v) = r.Value + let struct(cnt, v) = r.Value if cnt = 1 then cache.Remove m |> ignore dirty.Remove m |> ignore @@ -1028,8 +1029,8 @@ module AdaptiveHashSetImplementation = match cache.TryGetValue d with | (true, r) -> let n = d.GetValue token - let (rc, o) = r.Value - r.Value <- (rc, n) + let struct(rc, o) = r.Value + r.Value <- struct(rc, n) if not (DefaultEquality.equals o n) then deltas <- HashSetDelta.combine deltas (HashSetDelta.ofArray [| Add n; Rem o |]) | _ -> () @@ -1046,8 +1047,7 @@ module AdaptiveHashSetImplementation = do r.Tag <- "Reader" let f = Cache f - let mutable initial = true - let cache = DefaultDictionary.create>, ref>>() + let cache = DefaultDictionary.create>, ref)>>() member x.Invoke(token : AdaptiveToken, v : 'A) = let m = f.Invoke v @@ -1055,9 +1055,10 @@ module AdaptiveHashSetImplementation = match cache.TryGetValue m with | (true, r) -> - r.Value <- (fst r.Value + 1, v) + let struct(rc, _) = r.Value + r.Value <- struct(rc + 1, v) | _ -> - let r = ref (1, v) + let r = ref struct(1, v) cache.[m] <- r v @@ -1066,7 +1067,7 @@ module AdaptiveHashSetImplementation = member x.Invoke2(token : AdaptiveToken, m : aval>) = match cache.TryGetValue m with | (true, r) -> - let (rc, o) = r.Value + let struct(rc, o) = r.Value let v = m.GetValue token r.Value <- (rc, v) o, v @@ -1077,7 +1078,7 @@ module AdaptiveHashSetImplementation = let m = f.Revoke v match cache.TryGetValue m with | (true, r) -> - let (rc, v) = r.Value + let struct(rc, v) = r.Value if rc = 1 then cache.Remove m |> ignore lock m (fun () -> m.Outputs.Remove x |> ignore ) @@ -1090,17 +1091,17 @@ module AdaptiveHashSetImplementation = override x.Compute(token, dirty) = let mutable deltas = - r.GetChanges token |> HashSetDelta.choose (fun d -> + r.GetChanges token |> HashSetDelta.chooseV (fun d -> match d with | Add(1,m) -> match x.Invoke(token,m) with - | Some v -> Some (Add v) - | None -> None + | Some v -> ValueSome (Add v) + | None -> ValueNone | Rem(1,m) -> match x.Revoke m with - | Some v -> Some (Rem v) - | None -> None + | Some v -> ValueSome (Rem v) + | None -> ValueNone | _ -> unexpected() From d1a6dee71d4c25c82a47e6670599cdfa61bab5e6 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 23 Sep 2024 00:49:27 +0200 Subject: [PATCH 42/65] optimized ASet.filterA: added FiliterAReader --- .../AdaptiveHashSet/AdaptiveHashSet.fs | 72 ++++++++++++++++--- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index 39af881..c093028 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -1129,6 +1129,68 @@ module AdaptiveHashSetImplementation = () deltas + + + /// Reader for filterA + [] + type FilterAReader<'A>(input : aset<'A>, predicate : 'A -> aval) = + inherit AbstractDirtyReader, HashSetDelta<'A>>(HashSetDelta.monoid, isNull) + + let r = input.GetReader() + do r.Tag <- "Reader" + + let state = DefaultDictionary.create<'A, struct(aval * bool)>() + let predicateMap = DefaultDictionary.create, 'A>() + + override x.Compute(token, dirty) = + let mutable deltas = + r.GetChanges token |> HashSetDelta.chooseV (fun d -> + match d with + | Add(1, m) -> + let v = predicate m + let p = v.GetValue token + state.[m] <- struct(v, p) + predicateMap.[v] <- m + if p then + ValueSome (Add m) + else + ValueNone + + | Rem(1, m) -> + match state.TryGetValue m with + | (true, x) -> + let struct(v, p) = x + state.Remove m |> ignore + predicateMap.Remove v |> ignore + if p then + ValueSome (Rem m) + else + ValueNone + + | _ -> unexpected() + + | _ -> + unexpected() + ) + + for d in dirty do + match predicateMap.TryGetValue d with + | (true, m) -> + match state.TryGetValue m with + | (true, x) -> + let struct(v, p) = x + let pNew = d.GetValue token + if pNew <> p then + state.[m] <- (d, pNew) + if pNew then + deltas <- deltas.Add (Add m) + else + deltas <- deltas.Add (Rem m) + () + | _ -> unexpected() + | _ -> unexpected() + + deltas /// Gets the current content of the aset as HashSet. let inline force (set : aset<'T>) = @@ -1368,14 +1430,8 @@ module ASet = /// Adaptively filters the set and also respects inner changes. let filterA (predicate : 'A -> aval) (set : aset<'A>) = - // TODO: direct implementation - ofReader (fun () -> - let mapping (a : 'A) = - predicate a - |> AVal.map (function true -> Some a | false -> None) - - ChooseAReader(set, mapping) - ) + // TODO: constants + ofReader (fun () -> FilterAReader(set, predicate)) /// Adaptively maps over the given set and disposes all removed values while active. From 13601819e8849f76b405782bc7b3b264965f1eca Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 23 Sep 2024 10:34:50 +0200 Subject: [PATCH 43/65] added NodeMap.addInPlace' for more convenient in-place construction --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 32 ++++----------- .../Datastructures/HashCollections.fs | 39 +++++++------------ 2 files changed, 22 insertions(+), 49 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index f28830d..170689a 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1078,29 +1078,21 @@ module AdaptiveHashMapImplementation = | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta - delta <- n + delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta | _ -> let newSet = HashSet.single v state.[k] <- newSet - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta - delta <- n + delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta | Rem(_, (k, v)) -> match state.TryGetValue k with | (true, set) -> let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(_, n) = MapNode.addInPlace cmp hash k Remove delta - delta <- n + delta <- MapNode.addInPlace' cmp k Remove delta else state.[k] <- newSet - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta - delta <- n + delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta | _ -> () @@ -1128,15 +1120,11 @@ module AdaptiveHashMapImplementation = | (true, set) -> let newSet = HashSet.add v set state.[k] <- newSet - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta - delta <- n + delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta | _ -> let newSet = HashSet.single v state.[k] <- newSet - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta - delta <- n + delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta | Rem(_, v) -> let k = cache.Revoke v match state.TryGetValue k with @@ -1144,14 +1132,10 @@ module AdaptiveHashMapImplementation = let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(_, n) = MapNode.addInPlace cmp hash k Remove delta - delta <- n + delta <- MapNode.addInPlace' cmp k Remove delta else state.[k] <- newSet - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(_, n) = MapNode.addInPlace cmp hash k (Set (view newSet)) delta - delta <- n + delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta | _ -> () diff --git a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs index b75ba4d..acb6123 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs @@ -2144,6 +2144,11 @@ module internal HashImplementation = node <- join inner.Prefix inner hash (MapLeaf(hash, key, value, null)) true struct(ok, node) + + let inline addInPlace' (cmp : IEqualityComparer<'K>) (key : 'K) (value : 'V) (node : SetNode<'K>) = + let hash = uint32 (cmp.GetHashCode key) &&& 0x7FFFFFFFu + let struct(_, n) = addInPlace cmp hash key value node + n let rec toValueList (acc : list<'V>) (node : SetNode<'K>) = if isNull node then acc @@ -3913,10 +3918,8 @@ and [) = let cmp = DefaultEqualityComparer<'K>.Instance let mutable root = null - for (k, v) in elements do - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(ok, n) = MapNode.addInPlace cmp hash k v root - root <- n + for (k, v) in elements do + root <- MapNode.addInPlace' cmp k v root HashMap<'K, 'V>(cmp, root) [] @@ -3924,9 +3927,7 @@ and [.Instance let mutable root = null for (k, v) in elements do - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(ok, n) = MapNode.addInPlace cmp hash k v root - root <- n + root <- MapNode.addInPlace' cmp k v root HashMap<'K, 'V>(cmp, root) [] @@ -3934,9 +3935,7 @@ and [.Instance let mutable root = null for (k, v) in elements do - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(ok, n) = MapNode.addInPlace cmp hash k v root - root <- n + root <- MapNode.addInPlace' cmp k v root HashMap<'K, 'V>(cmp, root) [] @@ -3947,9 +3946,7 @@ and [(cmp, root) @@ -3962,9 +3959,7 @@ and [(cmp, root) @@ -3973,9 +3968,7 @@ and [.Instance let mutable root = null for struct(k, v) in elements do - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(ok, n) = MapNode.addInPlace cmp hash k v root - root <- n + root <- MapNode.addInPlace' cmp k v root HashMap<'K, 'V>(cmp, root) [] @@ -3983,9 +3976,7 @@ and [.Instance let mutable root = null for struct(k, v) in elements do - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(ok, n) = MapNode.addInPlace cmp hash k v root - root <- n + root <- MapNode.addInPlace' cmp k v root HashMap<'K, 'V>(cmp, root) [] @@ -3993,9 +3984,7 @@ and [.Instance let mutable root = null for struct(k, v) in elements do - let hash = uint32 (cmp.GetHashCode k) &&& 0x7FFFFFFFu - let struct(ok, n) = MapNode.addInPlace cmp hash k v root - root <- n + root <- MapNode.addInPlace' cmp k v root HashMap<'K, 'V>(cmp, root) #endif From aa8b799d6bc3e51184f17482678af08417462128 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 23 Sep 2024 10:35:26 +0200 Subject: [PATCH 44/65] fixed duplicate predicate aval in FilterAReader + test --- .../AdaptiveHashSet/AdaptiveHashSet.fs | 64 ++++++++++++++++--- src/Test/FSharp.Data.Adaptive.Tests/ASet.fs | 28 +++++++- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index c093028..2b9a7f8 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -1140,7 +1140,7 @@ module AdaptiveHashSetImplementation = do r.Tag <- "Reader" let state = DefaultDictionary.create<'A, struct(aval * bool)>() - let predicateMap = DefaultDictionary.create, 'A>() + let predicateMap = DefaultDictionary.create, struct('A * System.Collections.Generic.HashSet<'A>)>() // NOTE: only allocate hashset if there are multiple (expected to be rare) override x.Compute(token, dirty) = let mutable deltas = @@ -1150,7 +1150,18 @@ module AdaptiveHashSetImplementation = let v = predicate m let p = v.GetValue token state.[m] <- struct(v, p) - predicateMap.[v] <- m + match predicateMap.TryGetValue v with + | (true, pp) -> + let struct(first, other) = pp + if isNull other then + let other = System.Collections.Generic.HashSet<'A>() + other.Add(m) |> ignore + predicateMap.[v] <- (first, other) + else + other.Add(m) |> ignore + | _ -> + predicateMap.[v] <- (m, Unchecked.defaultof<_>) + if p then ValueSome (Add m) else @@ -1161,7 +1172,26 @@ module AdaptiveHashSetImplementation = | (true, x) -> let struct(v, p) = x state.Remove m |> ignore - predicateMap.Remove v |> ignore + match predicateMap.TryGetValue v with + | (true, pp) -> + let struct(first, other) = pp + if DefaultEquality.equals first m then + if isNull other then + predicateMap.Remove v |> ignore + else + let next = other |> Seq.head + if other.Count > 1 then + other.Remove next |> ignore + predicateMap.[v] <- (next, other) + else + predicateMap.[v] <- (next, Unchecked.defaultof<_>) + else + if other.Count > 1 then + other.Remove m |> ignore + else + predicateMap.[v] <- (first, Unchecked.defaultof<_>) + | _ -> unexpected() + if p then ValueSome (Rem m) else @@ -1175,19 +1205,35 @@ module AdaptiveHashSetImplementation = for d in dirty do match predicateMap.TryGetValue d with - | (true, m) -> - match state.TryGetValue m with + | (true, pp) -> + let pNew = d.GetValue token + let struct(first, other) = pp + match state.TryGetValue first with | (true, x) -> let struct(v, p) = x - let pNew = d.GetValue token if pNew <> p then - state.[m] <- (d, pNew) + state.[first] <- (d, pNew) if pNew then - deltas <- deltas.Add (Add m) + deltas <- deltas.Add (Add first) else - deltas <- deltas.Add (Rem m) + deltas <- deltas.Add (Rem first) () | _ -> unexpected() + + if not (isNull other) then + for m in other do + match state.TryGetValue m with + | (true, x) -> + let struct(v, p) = x + if pNew <> p then + state.[m] <- (d, pNew) + if pNew then + deltas <- deltas.Add (Add m) + else + deltas <- deltas.Add (Rem m) + () + | _ -> unexpected() + | _ -> unexpected() deltas diff --git a/src/Test/FSharp.Data.Adaptive.Tests/ASet.fs b/src/Test/FSharp.Data.Adaptive.Tests/ASet.fs index d159054..59c2b2d 100644 --- a/src/Test/FSharp.Data.Adaptive.Tests/ASet.fs +++ b/src/Test/FSharp.Data.Adaptive.Tests/ASet.fs @@ -758,4 +758,30 @@ let ``[ASet] union constant``() = let refSet = [1; 2; 3; 4] union1 |> ASet.force |> should setequal refSet - union2 |> ASet.force |> should setequal refSet \ No newline at end of file + union2 |> ASet.force |> should setequal refSet + + +[] +let ``[ASet] filterA``() = + let takeEven = AVal.init true + let takeOdd = AVal.init true + let set = ASet.ofArray (Array.init 5 (fun i -> i)) + + let filtered = set |> ASet.filterA (fun i -> if (i % 2) = 0 then takeEven else takeOdd) + + filtered |> ASet.force |> should setequal [0; 1; 2; 3; 4] + + transact(fun () -> takeEven.Value <- false) + + filtered |> ASet.force |> should setequal [1; 3] + + transact(fun () -> takeOdd.Value <- false) + + filtered |> ASet.force |> HashSet.count |> should equal 0 + + transact(fun () -> + takeOdd.Value <- true + takeEven.Value <- true + ) + + filtered |> ASet.force |> should setequal [0; 1; 2; 3; 4] \ No newline at end of file From 916cb3cdff25158b20897c05b6f4f0ec8b9105aa Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 23 Sep 2024 15:13:39 +0200 Subject: [PATCH 45/65] avoid choice overhead --- .../AdaptiveHashMap/AdaptiveHashMap.fs | 20 +-- .../AdaptiveHashSet/AdaptiveHashSet.fs | 135 +++++++++--------- 2 files changed, 77 insertions(+), 78 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index 170689a..ff7d174 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1071,9 +1071,9 @@ module AdaptiveHashMapImplementation = override x.Compute (token : AdaptiveToken) = let cmp = DefaultEqualityComparer<'Key>.Instance let mutable delta = null - for op in reader.GetChanges token do - match op with - | Add(_, (k, v)) -> + for d in reader.GetChanges token do + let (k, v) = d.Value + if d.Count = 1 then // >= 1 ?? match state.TryGetValue k with | (true, set) -> let newSet = HashSet.add v set @@ -1083,7 +1083,7 @@ module AdaptiveHashMapImplementation = let newSet = HashSet.single v state.[k] <- newSet delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta - | Rem(_, (k, v)) -> + elif d.Count = -1 then // <= -1 ?? match state.TryGetValue k with | (true, set) -> let newSet = HashSet.remove v set @@ -1095,6 +1095,8 @@ module AdaptiveHashMapImplementation = delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta | _ -> () + else + unexpected() HashMap(cmp, delta) |> HashMapDelta.ofHashMap @@ -1112,9 +1114,9 @@ module AdaptiveHashMapImplementation = override x.Compute (token : AdaptiveToken) = let cmp = DefaultEqualityComparer<'Key>.Instance let mutable delta = null - for op in reader.GetChanges token do - match op with - | Add(_, v) -> + for d in reader.GetChanges token do + let v = d.Value + if d.Count = 1 then // >= 1 ?? let k = cache.Invoke v match state.TryGetValue k with | (true, set) -> @@ -1125,7 +1127,7 @@ module AdaptiveHashMapImplementation = let newSet = HashSet.single v state.[k] <- newSet delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta - | Rem(_, v) -> + elif d.Count = -1 then // <= -1 ?? let k = cache.Revoke v match state.TryGetValue k with | (true, set) -> @@ -1138,6 +1140,8 @@ module AdaptiveHashMapImplementation = delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta | _ -> () + else + unexpected() HashMap(cmp, delta) |> HashMapDelta.ofHashMap diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index 2b9a7f8..1a9e65b 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -65,12 +65,11 @@ module SetReductions = let mutable e = ops.GetEnumerator() while working && e.MoveNext() do let op = e.Current - match op with - | Add(_, a) -> - sum <- reduction.add sum a - - | Rem(_, old) -> - match reduction.sub sum old with + let v = op.Value + if op.Count = 1 then // >= 1 ?? + sum <- reduction.add sum v + elif op.Count = -1 then // <= -1 ?? + match reduction.sub sum v with | ValueSome s -> sum <- s | ValueNone -> working <- false @@ -414,18 +413,18 @@ module AdaptiveHashSetImplementation = static member DeltaMapping (mapping : 'A -> 'B) = let cache = Cache mapping HashSetDelta.map (fun d -> - match d with - | Add(1, v) -> Add(cache.Invoke v) - | Rem(1, v) -> Rem(cache.Revoke v) - | _ -> unexpected() + let v = d.Value + if d.Count = 1 then Add(cache.Invoke v) + elif d.Count = -1 then Rem(cache.Revoke v) + else unexpected() ) override x.Compute(token) = reader.GetChanges token |> HashSetDelta.map (fun d -> - match d with - | Add(1, v) -> Add(cache.Invoke v) - | Rem(1, v) -> Rem(cache.Revoke v) - | _ -> unexpected() + let v = d.Value + if d.Count = 1 then Add(cache.Invoke v) + elif d.Count = -1 then Rem(cache.Revoke v) + else unexpected() ) @@ -460,17 +459,17 @@ module AdaptiveHashSetImplementation = d else reader.GetChanges token |> HashSetDelta.choose (fun d -> - match d with - | Add(1, v) -> + let v = d.Value + if d.Count = 1 then Add(cache.Invoke v) |> Some - | Rem(1, v) -> + elif d.Count = -1 then match cache.RevokeAndGetDeletedTotal v with | ValueSome (del, v) -> if del then (v :> IDisposable).Dispose() Rem v |> Some | ValueNone -> None - | _ -> + else unexpected() ) @@ -511,35 +510,31 @@ module AdaptiveHashSetImplementation = static member DeltaMapping (mapping : 'A -> option<'B>) = let cache = Cache mapping HashSetDelta.choose (fun d -> - match d with - | Add(1, v) -> + let v = d.Value + if d.Count = 1 then match cache.Invoke v with | Some v -> Some (Add v) | None -> None - - | Rem(1, v) -> + elif d.Count = -1 then match cache.Revoke v with | Some v -> Some (Rem v) | None -> None - - | _ -> + else unexpected() ) override x.Compute(token) = r.GetChanges token |> HashSetDelta.choose (fun d -> - match d with - | Add(1, v) -> + let v = d.Value + if d.Count = 1 then match cache.Invoke v with | Some v -> Some (Add v) | None -> None - - | Rem(1, v) -> + elif d.Count = -1 then match cache.Revoke v with | Some v -> Some (Rem v) | None -> None - - | _ -> + else unexpected() ) @@ -554,18 +549,18 @@ module AdaptiveHashSetImplementation = static member DeltaMapping (predicate : 'T -> bool) = let cache = Cache predicate HashSetDelta.filter (fun d -> - match d with - | Add(1, v) -> cache.Invoke v - | Rem(1, v) -> cache.Revoke v - | _ -> unexpected() + let v = d.Value + if d.Count = 1 then cache.Invoke v + elif d.Count = -1 then cache.Revoke v + else unexpected() ) override x.Compute(token) = r.GetChanges token |> HashSetDelta.filter (fun d -> - match d with - | Add(1, v) -> cache.Invoke v - | Rem(1, v) -> cache.Revoke v - | _ -> unexpected() + let v = d.Value + if d.Count = 1 then cache.Invoke v + elif d.Count = -1 then cache.Revoke v + else unexpected() ) /// Reader for fully dynamic union operations. @@ -584,14 +579,14 @@ module AdaptiveHashSetImplementation = override x.Compute(token, dirty) = let mutable deltas = reader.GetChanges token |> HashSetDelta.collect (fun d -> - match d with - | Add(1, v) -> + let v = d.Value + if d.Count = 1 then // r is no longer dirty since we pull it here. let r = cache.Invoke v dirty.Remove r |> ignore r.GetChanges token - | Rem(1, v) -> + elif d.Count = -1 then // r is no longer dirty since we either pull or destroy it here. let struct(deleted, r) = cache.RevokeAndGetDeleted v dirty.Remove r |> ignore @@ -603,7 +598,8 @@ module AdaptiveHashSetImplementation = else r.GetChanges token - | _ -> unexpected() + else + unexpected() ) // finally pull all the dirty readers and accumulate the deltas. @@ -817,10 +813,10 @@ module AdaptiveHashSetImplementation = override x.Compute(token) = reader.GetChanges token |> HashSetDelta.collect (fun d -> - match d with - | Add(1, v) -> HashSet.addAll v - | Rem(1, v) -> HashSet.removeAll v - | _ -> unexpected() + let v = d.Value + if d.Count = 1 then HashSet.addAll v + elif d.Count = -1 then HashSet.removeAll v + else unexpected() ) /// Reader for collect operations. @@ -839,14 +835,14 @@ module AdaptiveHashSetImplementation = override x.Compute(token,dirty) = let mutable deltas = reader.GetChanges token |> HashSetDelta.collect (fun d -> - match d with - | Add(1, value) -> + let value = d.Value + if d.Count = 1 then // r is no longer dirty since we pull it here. let r = cache.Invoke value dirty.Remove r |> ignore r.GetChanges token - | Rem(1, value) -> + elif d.Count = -1 then match cache.RevokeAndGetDeletedTotal value with | ValueSome (deleted, r) -> // r is no longer dirty since we either pull or destroy it here. @@ -862,7 +858,8 @@ module AdaptiveHashSetImplementation = // weird HashSetDelta.empty - | _ -> unexpected() + else + unexpected() ) // finally pull all the dirty readers and accumulate the deltas. @@ -957,10 +954,10 @@ module AdaptiveHashSetImplementation = override x.Compute(token, dirty) = let mutable deltas = r.GetChanges token |> HashSetDelta.map (fun d -> - match d with - | Add(1,m) -> Add(x.Invoke(token, m)) - | Rem(1,m) -> Rem(x.Revoke(m, dirty)) - | _ -> unexpected() + let m = d.Value + if d.Count = 1 then Add(x.Invoke(token, m)) + elif d.Count = -1 then Rem(x.Revoke(m, dirty)) + else unexpected() ) for d in dirty do @@ -1019,10 +1016,10 @@ module AdaptiveHashSetImplementation = override x.Compute(token, dirty) = let mutable deltas = reader.GetChanges token |> HashSetDelta.map (fun d -> - match d with - | Add(1,m) -> Add(x.Invoke(token,m)) - | Rem(1,m) -> Rem(x.Revoke(m, dirty)) - | _ -> unexpected() + let m = d.Value + if d.Count = 1 then Add(x.Invoke(token,m)) + elif d.Count = -1 then Rem(x.Revoke(m, dirty)) + else unexpected() ) for d in dirty do @@ -1092,18 +1089,16 @@ module AdaptiveHashSetImplementation = override x.Compute(token, dirty) = let mutable deltas = r.GetChanges token |> HashSetDelta.chooseV (fun d -> - match d with - | Add(1,m) -> + let m = d.Value + if d.Count = 1 then match x.Invoke(token,m) with | Some v -> ValueSome (Add v) | None -> ValueNone - - | Rem(1,m) -> + elif (d.Count = -1) then match x.Revoke m with | Some v -> ValueSome (Rem v) | None -> ValueNone - - | _ -> + else unexpected() ) @@ -1145,8 +1140,9 @@ module AdaptiveHashSetImplementation = override x.Compute(token, dirty) = let mutable deltas = r.GetChanges token |> HashSetDelta.chooseV (fun d -> - match d with - | Add(1, m) -> + + let m = d.Value + if d.Count = 1 then let v = predicate m let p = v.GetValue token state.[m] <- struct(v, p) @@ -1166,8 +1162,7 @@ module AdaptiveHashSetImplementation = ValueSome (Add m) else ValueNone - - | Rem(1, m) -> + elif (d.Count = -1) then match state.TryGetValue m with | (true, x) -> let struct(v, p) = x @@ -1199,8 +1194,8 @@ module AdaptiveHashSetImplementation = | _ -> unexpected() - | _ -> - unexpected() + else + unexpected() // "SetOperation Count is not 1 or -1" ) for d in dirty do From e2e505110a748fbd42fe66debb85bcbdda25f350 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 23 Sep 2024 16:01:07 +0200 Subject: [PATCH 46/65] excluded HashMapDelta.ofSeqV from fable builds: it forward to HashMap.OfSeq with has two overloads and this is not supported --- src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs b/src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs index b5ddea4..07039fd 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/HashMapDelta.fs @@ -78,9 +78,11 @@ module HashMapDelta = let ofSeq (elements : seq<'K * ElementOperation<'V>>) = HashMapDelta(HashMap.ofSeq elements) + #if !FABLE_COMPILER /// A HashMapDelta containing all the given deltas. let ofSeqV (elements : seq)>) = HashMapDelta(HashMap.ofSeqV elements) + #endif /// A HashMapDelta containing all the given deltas. let ofList (elements : list<'K * ElementOperation<'V>>) = From 6a78e299f4c34e992abc5422bbbce4987ec3ebf1 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 23 Sep 2024 17:16:16 +0200 Subject: [PATCH 47/65] fixed missing type information in AMap SetReader/MappedSetReader --- src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs index ff7d174..50dd4bc 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashMap/AdaptiveHashMap.fs @@ -1089,7 +1089,7 @@ module AdaptiveHashMapImplementation = let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - delta <- MapNode.addInPlace' cmp k Remove delta + delta <- MapNode.addInPlace' cmp k (Remove : ElementOperation<'View>) delta else state.[k] <- newSet delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta @@ -1134,7 +1134,7 @@ module AdaptiveHashMapImplementation = let newSet = HashSet.remove v set if newSet.IsEmpty then state.Remove k |> ignore - delta <- MapNode.addInPlace' cmp k Remove delta + delta <- MapNode.addInPlace' cmp k (Remove : ElementOperation<'View>) delta else state.[k] <- newSet delta <- MapNode.addInPlace' cmp k (Set (view newSet)) delta From f66fd41af81802ae6fbe40be1669c235179423d3 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 24 Sep 2024 11:22:50 +0200 Subject: [PATCH 48/65] added ApplyDeltaV and ApplyDeltaNoRefCountV --- src/FSharp.Data.Adaptive/AdaptifyHelpers.fs | 2 +- .../AdaptiveHashSet/AdaptiveHashSet.fs | 6 ++-- .../Datastructures/HashCollections.fs | 8 +++++ .../Traceable/CountingHashSet.fs | 35 ++++++++++++++++--- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptifyHelpers.fs b/src/FSharp.Data.Adaptive/AdaptifyHelpers.fs index 47dc99f..b1244f8 100644 --- a/src/FSharp.Data.Adaptive/AdaptifyHelpers.fs +++ b/src/FSharp.Data.Adaptive/AdaptifyHelpers.fs @@ -38,7 +38,7 @@ type ChangeableModelMap<'K, 'V, 'C, 'A>(map : HashMap<'K, 'V>, init : 'V -> 'C, | Remove -> struct (ValueNone, ValueNone) - let s, ops = HashMap.ApplyDelta(history.State, HashMapDelta.toHashMap ops, apply) + let struct(s, ops) = HashMap.ApplyDeltaV(history.State, HashMapDelta.toHashMap ops, apply) history.PerformUnsafe(s, HashMapDelta.ofHashMap ops) |> ignore member x.GetReader() = diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index 1a9e65b..463a40c 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -652,7 +652,7 @@ module AdaptiveHashSetImplementation = struct (outRef, outDelta) - let newState, delta = HashMap.ApplyDelta(state, changes, apply) + let struct(newState, delta) = HashMap.ApplyDeltaV(state, changes, apply) state <- newState HashSetDelta.ofHashMap delta @@ -700,7 +700,7 @@ module AdaptiveHashSetImplementation = struct (outRef, outDelta) - let newState, delta = HashMap.ApplyDelta(state, changes, apply) + let struct(newState, delta) = HashMap.ApplyDeltaV(state, changes, apply) state <- newState HashSetDelta.ofHashMap delta @@ -748,7 +748,7 @@ module AdaptiveHashSetImplementation = struct (outRef, outDelta) - let newState, delta = HashMap.ApplyDelta(state, changes, apply) + let struct(newState, delta) = HashMap.ApplyDeltaV(state, changes, apply) state <- newState HashSetDelta.ofHashMap delta diff --git a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs index acb6123..ebded78 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs @@ -3846,6 +3846,14 @@ and [(a.Comparer, state) let delta = HashMap<'K, 'U>(a.Comparer, delta) state, delta + + [] + static member ApplyDeltaV(a : HashMap<'K, 'V>, b : HashMap<'K, 'T>, apply : 'K -> voption<'V> -> 'T -> struct(voption<'V> * voption<'U>)) = + let state = a.Root + let struct(delta, state) = MapNode.applyDelta a.Comparer (OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt apply) state b.Root + let state = HashMap<'K, 'V>(a.Comparer, state) + let delta = HashMap<'K, 'U>(a.Comparer, delta) + struct(state, delta) [] member x.Choose2V(other : HashMap<'K, 'T>, mapping : 'K -> voption<'V> -> voption<'T> -> voption<'U>) = diff --git a/src/FSharp.Data.Adaptive/Traceable/CountingHashSet.fs b/src/FSharp.Data.Adaptive/Traceable/CountingHashSet.fs index a9777b5..db9b839 100644 --- a/src/FSharp.Data.Adaptive/Traceable/CountingHashSet.fs +++ b/src/FSharp.Data.Adaptive/Traceable/CountingHashSet.fs @@ -213,8 +213,8 @@ type CountingHashSet<'T>(store : HashMap<'T, int>) = let mutable res = HashMap<'B, int>.Empty for (k,ro) in store do let r = mapping k - let rr, _ = - HashMap<'B, int>.ApplyDelta(res, r.Store, fun _ oldRef delta -> + let struct(rr, _) = + HashMap<'B, int>.ApplyDeltaV(res, r.Store, fun _ oldRef delta -> let oldRef = match oldRef with | ValueSome o -> o | ValueNone -> 0 struct (ValueSome (oldRef + ro * delta), ValueNone) ) @@ -309,7 +309,7 @@ type CountingHashSet<'T>(store : HashMap<'T, int>) = struct(value, delta) - let s, d = HashMap.ApplyDelta(store, deltas.Store, apply) + let struct(s, d) = HashMap.ApplyDeltaV(store, deltas.Store, apply) CountingHashSet s, HashSetDelta d @@ -333,9 +333,32 @@ type CountingHashSet<'T>(store : HashMap<'T, int>) = struct(value, delta) - let s, d = HashMap.ApplyDelta(store, deltas.Store, apply) + let struct(s, d) = HashMap.ApplyDeltaV(store, deltas.Store, apply) CountingHashSet s, HashSetDelta d + /// Integrates the given delta into the set, returns a new set and the effective deltas. + member x.ApplyDeltaNoRefCountV (deltas : HashSetDelta<'T>) = + let apply (_k : 'T) (o : voption) (d : int) = + let o = match o with | ValueSome _ -> 1 | ValueNone -> 0 + let n = + if d > 0 then 1 + elif d < 0 then 0 + else o + + let delta = + if o = 0 && n > 0 then ValueSome 1 + elif o > 0 && n = 0 then ValueSome -1 + else ValueNone + + let value = + if n <= 0 then ValueNone + else ValueSome n + + struct(value, delta) + + let struct(s, d) = HashMap.ApplyDeltaV(store, deltas.Store, apply) + struct(CountingHashSet s, HashSetDelta d) + /// Compares two sets. static member internal Compare(l : CountingHashSet<'T>, r : CountingHashSet<'T>) = let i = l.Intersect r @@ -511,6 +534,10 @@ module CountingHashSet = /// Integrates the given delta into the set without ref-counting, returns a new set and the effective deltas. let inline applyDeltaNoRefCount (set : CountingHashSet<'T>) (delta : HashSetDelta<'T>) = set.ApplyDeltaNoRefCount delta + + /// Integrates the given delta into the set without ref-counting, returns a new set and the effective deltas. + let inline applyDeltaNoRefCountV (set : CountingHashSet<'T>) (delta : HashSetDelta<'T>) = + set.ApplyDeltaNoRefCountV delta /// Compares two sets. let internal compare (l : CountingHashSet<'T>) (r : CountingHashSet<'T>) = From 55f8ca49a873e0932b04abdea47ae1fd959e6104 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 25 Sep 2024 23:09:25 +0200 Subject: [PATCH 49/65] added SetNode.addInPlace helper --- .../Datastructures/HashCollections.fs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs index ebded78..5c1006e 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs @@ -956,6 +956,11 @@ module internal HashImplementation = struct(ok, node) + let inline addInPlace' (cmp : IEqualityComparer<'K>) (key : 'K) (node : SetNode<'K>) = + let hash = uint32 (cmp.GetHashCode key) &&& 0x7FFFFFFFu + let struct(_, n) = addInPlace cmp hash key node + n + let rec tryRemove (cmp : IEqualityComparer<'K>) (hash : uint32) (key : 'K) (node : SetNode<'K>) = let mutable node : SetNode<'K> = node let ok = @@ -3589,9 +3594,7 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) let cmp = DefaultEqualityComparer<'K>.Instance let mutable root = null for e in elements do - let hash = uint32 (cmp.GetHashCode e) &&& 0x7FFFFFFFu - let struct(ok, n) = SetNode.addInPlace cmp hash e root - root <- n + root <- SetNode.addInPlace' cmp e root HashSet(cmp, root) [] @@ -3599,9 +3602,7 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) let cmp = DefaultEqualityComparer<'K>.Instance let mutable root = null for e in elements do - let hash = uint32 (cmp.GetHashCode e) &&& 0x7FFFFFFFu - let struct(ok, n) = SetNode.addInPlace cmp hash e root - root <- n + root <- SetNode.addInPlace' cmp e root HashSet(cmp, root) [] @@ -3609,9 +3610,7 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) let cmp = DefaultEqualityComparer<'K>.Instance let mutable root = null for e in elements do - let hash = uint32 (cmp.GetHashCode e) &&& 0x7FFFFFFFu - let struct(ok, n) = SetNode.addInPlace cmp hash e root - root <- n + root <- SetNode.addInPlace' cmp e root HashSet(cmp, root) [] @@ -3622,9 +3621,7 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) let ee = offset + length while i < ee do let e = elements.[i] - let hash = uint32 (cmp.GetHashCode e) &&& 0x7FFFFFFFu - let struct(ok, n) = SetNode.addInPlace cmp hash e root - root <- n + root <- SetNode.addInPlace' cmp e root i <- i + 1 HashSet(cmp, root) From d6be34bd605e91ded5fd5dcf7ffaff8d65f94f37 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 25 Sep 2024 23:10:12 +0200 Subject: [PATCH 50/65] CSharp: added AdaptiveHashMap.Values() extension --- src/CSharp.Data.Adaptive/AMap.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CSharp.Data.Adaptive/AMap.fs b/src/CSharp.Data.Adaptive/AMap.fs index c29af36..9ab5f1c 100644 --- a/src/CSharp.Data.Adaptive/AMap.fs +++ b/src/CSharp.Data.Adaptive/AMap.fs @@ -246,3 +246,7 @@ type AdaptiveHashMap private() = [] static member Keys(this: amap<'K, 'V>) = this |> AMap.keys + + [] + static member Values(this: amap<'K, 'V>) = + this |> AMap.toASetValues \ No newline at end of file From 6175b5f0cac67c54e9aacd1955546955034ea6fb Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 25 Sep 2024 23:11:10 +0200 Subject: [PATCH 51/65] using value option/tuple in ASet BindReader --- .../AdaptiveHashSet/AdaptiveHashSet.fs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index 463a40c..09ee8e9 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -887,7 +887,7 @@ module AdaptiveHashSetImplementation = inherit AbstractReader>(HashSetDelta.empty) let mutable valChanged = 0 - let mutable cache : option<'A * IHashSetReader<'B>> = None + let mutable cache : voption)> = ValueNone override x.InputChangedObject(_, i) = // check if input is different object @@ -906,23 +906,23 @@ module AdaptiveHashSetImplementation = #endif match cache with - | Some(oldValue, oldReader) when valChanged && not (cheapEqual oldValue newValue) -> + | ValueSome(oldValue, oldReader) when valChanged && not (cheapEqual oldValue newValue) -> // input changed let rem = CountingHashSet.removeAll oldReader.State oldReader.Outputs.Remove x |> ignore let newReader = (mapping newValue).GetReader() let add = newReader.GetChanges token - cache <- Some(newValue, newReader) + cache <- ValueSome(newValue, newReader) HashSetDelta.combine rem add - | Some(_, ro) -> + | ValueSome(_, ro) -> // input unchanged ro.GetChanges token - | None -> + | ValueNone -> // initial let r = (mapping newValue).GetReader() - cache <- Some(newValue, r) + cache <- ValueSome(newValue, r) r.GetChanges token /// Reader for flattenA From 34e0da8c43909acae59a73b1320b35c19ddf8e22 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 2 Oct 2024 17:58:59 +0200 Subject: [PATCH 52/65] fixed filterA --- src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs index 09ee8e9..2f86ea0 100644 --- a/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs +++ b/src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs @@ -1229,7 +1229,7 @@ module AdaptiveHashSetImplementation = () | _ -> unexpected() - | _ -> unexpected() + | _ -> () // aval expected to have been removed in this udpate deltas From 8171106cf3062733b8da29dd53b6e84e88659219 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 7 Oct 2024 09:48:01 +0200 Subject: [PATCH 53/65] added ASet.ofListTree and ASet.ofSetTree added C# extension AdaptiveHashSet.ToAdaptiveHashMapIgnoreDuplicates overload with getKey (AMap.ofASetMappedIgnoreDuplicates) --- src/CSharp.Data.Adaptive/ASet.fs | 25 +++- .../CollectionExtensions.fs | 140 ++++++++++++++++++ 2 files changed, 159 insertions(+), 6 deletions(-) diff --git a/src/CSharp.Data.Adaptive/ASet.fs b/src/CSharp.Data.Adaptive/ASet.fs index 3bd2d00..d66c765 100644 --- a/src/CSharp.Data.Adaptive/ASet.fs +++ b/src/CSharp.Data.Adaptive/ASet.fs @@ -272,14 +272,13 @@ type AdaptiveHashSet private() = this |> ASet.toAList [] - static member MapToMap(this: aset<'T>, mapping: Func<'T, 'Value>) = - this |> ASet.mapToAMap mapping.Invoke + static member MapToMap(this: aset<'T>, getValue: Func<'T, 'Value>) = + this |> ASet.mapToAMap getValue.Invoke [] - static member GroupBy(this: aset<'T>, mapping: Func<'T, 'G>) = - this |> ASet.groupBy mapping.Invoke - - + static member GroupBy(this: aset<'T>, getKey: Func<'T, 'G>) = + this |> ASet.groupBy getKey.Invoke + [] static member ToAdaptiveHashMap(this: aset<'K * 'V>) = AMap.ofASet this @@ -288,6 +287,20 @@ type AdaptiveHashSet private() = static member ToAdaptiveHashMapIgnoreDuplicates(this: aset<'K * 'V>) = AMap.ofASetIgnoreDuplicates this + [] + static member ToAdaptiveHashMapIgnoreDuplicates(this: aset<'TValue>, getKey : Func<'TValue, 'TKey>) = + this |> AMap.ofASetMappedIgnoreDuplicates getKey.Invoke + + /// Creates an AdaptiveHashSet from tree of lists + /// NOTE: does not expect duplicates -> TODO + [] + static member OfListTree(this: alist<'TNode>, getChildren : Func<'TNode, alist<'TNode>>) = + this |> ASet.ofListTree getChildren.Invoke + + /// Creates an AdaptiveHashSet from tree of sets + [] + static member OfSetTree(this: aset<'TNode>, getChildren : Func<'TNode, aset<'TNode>>) = + this |> ASet.ofSetTree getChildren.Invoke [] static member ToArray(this: aset<'T>) = diff --git a/src/FSharp.Data.Adaptive/CollectionExtensions.fs b/src/FSharp.Data.Adaptive/CollectionExtensions.fs index c714eaa..fed2c2c 100644 --- a/src/FSharp.Data.Adaptive/CollectionExtensions.fs +++ b/src/FSharp.Data.Adaptive/CollectionExtensions.fs @@ -282,6 +282,137 @@ module CollectionExtensions = IndexListDelta(res) + + /// Reader for ASet.ofListTree + [] + type ListTreeReader<'T>(list: alist<'T>, getChildren : 'T -> alist<'T>) = + inherit AbstractDirtyReader, HashSetDelta<'T>>(HashSetDelta.monoid, isNull) + + let mutable initial = true + let reader = list.GetReader() // NOTE: need to be held, otherwise it will be collected and no updates can be consumed + let cache = System.Collections.Generic.Dictionary * FSharp.Data.Adaptive.Index), struct('T * IIndexListReader<'T>)>() // TODO: refcounting + + member x.Invoke(token : AdaptiveToken, r : IIndexListReader<'T>, i : FSharp.Data.Adaptive.Index, n : 'T) = + let mutable delta = HashSetDelta.empty + + if cache.ContainsKey (struct(r, i)) then + delta <- delta.Combine (x.Revoke(r, i)) + + let subNodes = getChildren n + let subReader = subNodes.GetReader() + cache[struct(r, i)] <- (n, subReader) + + delta <- delta.Add (Add n) + let content = subReader.GetChanges token + for c in content do + match c with + | (si, Set sub) -> + delta <- delta.Combine (x.Invoke(token, subReader, si, sub)) + | _ -> unexpected() + + delta + + member x.Revoke(r : IIndexListReader<'T>, i : FSharp.Data.Adaptive.Index) = + let mutable delta = HashSetDelta.empty + match cache.TryGetValue (struct(r, i)) with + | (true, struct(n, subReader)) -> + cache.Remove (struct(r, i)) |> ignore + subReader.Outputs.Remove x |> ignore + + delta <- delta.Add (Rem n) + subReader.State |> IndexList.iteri (fun i old -> + delta <- delta.Combine (x.Revoke(subReader, i))) + + | _ -> () // possible if parent set already removed all its sub nodes + + delta + + override x.Compute(token: AdaptiveToken, dirty : System.Collections.Generic.HashSet>) = + let mutable delta = HashSetDelta.empty + if initial then + initial <- false + let changes = reader.GetChanges token + for d in changes do + match d with + | (i, Set n) -> delta <- delta.Combine (x.Invoke(token, reader, i, n)) + | _ -> unexpected() + + for d in dirty do + let inner = d.GetChanges token + for c in inner do + match c with + | (i, Set n) -> delta <- delta.Combine (x.Invoke(token, d, i, n)) + | (i, Remove) -> delta <- delta.Combine (x.Revoke(d, i)) + + delta + + + /// Reader for ASet.ofSetTree (delta combine) + [] + type SetTreeReader<'T>(set: aset<'T>, getChildren : 'T -> aset<'T>) = + inherit AbstractDirtyReader, HashSetDelta<'T>>(HashSetDelta.monoid, isNull) + + let mutable initial = true + let reader = set.GetReader() // NOTE: need to be held, otherwise it will be collected and no updates can be consumed + let cache = DefaultDictionary.create<'T, struct(IHashSetReader<'T> * ref)>() + + member x.Invoke(token : AdaptiveToken, n : 'T) = + let mutable delta = HashSetDelta.empty + match cache.TryGetValue n with + | (true, (_, refCount)) -> refCount.Value <- refCount.Value + 1 + | _ -> + let subNodes = getChildren n + let reader = subNodes.GetReader() + cache[n] <- (reader, ref 1) + + delta <- delta.Add (Add n) + let content = reader.GetChanges token + for c in content do + if c.Count <> 1 then unexpected() + delta <- delta.Combine (x.Invoke(token, c.Value)) + + delta + + member x.Revoke(n : 'T) = + let mutable delta = HashSetDelta.empty + match cache.TryGetValue n with + | (true, (reader, refCount)) -> + if refCount.Value = 1 then + cache.Remove n |> ignore + reader.Outputs.Remove x |> ignore + + delta <- delta.Add (Rem n) + for old in reader.State do + delta <- delta.Combine (x.Revoke(old)) + else + refCount.Value <- refCount.Value - 1 + + | _ -> () // possible if parent list already removed all its sub nodes + + delta + + override x.Compute(token: AdaptiveToken, dirty : System.Collections.Generic.HashSet>) = + let mutable deltas = + if initial then + initial <- false + reader.GetChanges token |> HashSetDelta.collect (fun d -> + let n = d.Value + if d.Count = 1 then x.Invoke(token, n) + else unexpected() + ) + else + HashSetDelta.empty + + for d in dirty do + let inner = d.GetChanges token |> HashSetDelta.collect (fun d -> + let n = d.Value + if d.Count = 1 then x.Invoke(token, n) + elif d.Count = -1 then x.Revoke(n) + else unexpected() + ) + deltas <- deltas.Combine inner + deltas + /// Functional operators for amap<_,_> @@ -454,6 +585,15 @@ module CollectionExtensions = let groupBy (mapping: 'T -> 'K) (set: aset<'T>) = set |> AMap.ofASetMapped mapping + /// Creates an aset from tree of alists + /// NOTE: does not expect duplicates -> TODO + let ofListTree<'T> (getChildren : 'T -> alist<'T>) (nodes : alist<'T>) = + ASet.ofReader (fun () -> ListTreeReader(nodes, getChildren)) + + /// Creates an aset from tree of sets + let ofSetTree<'T> (getChildren : 'T -> aset<'T>) (nodes : aset<'T>) = + ASet.ofReader (fun () -> SetTreeReader(nodes, getChildren)) + /// Functional operators for alist<_> [] From 34ba06d07d0294a3d74591d79c332d45093eeb35 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Mon, 7 Oct 2024 10:12:13 +0200 Subject: [PATCH 54/65] using SetNode.addInPlace' helper --- .../Datastructures/HashCollections.fs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs index 5c1006e..b2545b4 100644 --- a/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs +++ b/src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs @@ -3273,9 +3273,7 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) let e = elements.GetEnumerator() while e.MoveNext() do let item = e.Current - let hash = uint32 (cmp.GetHashCode item) &&& 0x7FFFFFFFu - let struct(ok, n) = SetNode.addInPlace cmp hash item root - root <- n + root <- SetNode.addInPlace' cmp item root e.Dispose() HashSet(cmp, root) @@ -3384,9 +3382,7 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) let mutable root = null for e in x do let n = mapping e - let hash = uint32 (cmp.GetHashCode n) &&& 0x7FFFFFFFu - let struct(ok, n) = SetNode.addInPlace cmp hash n root - root <- n + root <- SetNode.addInPlace' cmp n root HashSet<'T>(cmp, root) @@ -3419,9 +3415,7 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) for e in x do match mapping e with | ValueSome n -> - let hash = uint32 (cmp.GetHashCode n) &&& 0x7FFFFFFFu - let struct(ok, n) = SetNode.addInPlace cmp hash n root - root <- n + root <- SetNode.addInPlace' cmp n root | ValueNone -> () HashSet<'T>(cmp, root) @@ -3434,9 +3428,7 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>) for e in x do match mapping e with | Some n -> - let hash = uint32 (cmp.GetHashCode n) &&& 0x7FFFFFFFu - let struct(ok, n) = SetNode.addInPlace cmp hash n root - root <- n + root <- SetNode.addInPlace' cmp n root | None -> () HashSet<'T>(cmp, root) From c346727585854eb70e801d162bced3b7d111bbbe Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 16:27:08 +0200 Subject: [PATCH 55/65] updated release notes --- RELEASE_NOTES.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 010cc58..70ba6e3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,19 @@ +### 1.2.17-prerelease0001 +- added ASet.ofListTree and ASet.ofSetTree +- added AList.mapToASet +- added AMap.ofASetMapped/ofASetMappedIgnoreDuplicates (optimized ASet.groupBy) +- added MapNode/SetNode addInPlace helpers +- added SetNode.head +- added special implementation for ASet.filterA +- added special implementation for ASet.union with one set constant +- improved AList.append +- changed internal tuples/options to value types +- changed Index garbage collection to run in finalizer +- preferred using struct enumerators +- avoided using active patterns to match set operations +- updated Tests to net 8.0 +- fixed race condition in Index + ### 1.2.16 * avoid WPF contention-inline problems with transaction From 9de6b5a57bb73944056e6130e07975633a2b108c Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 16:33:25 +0200 Subject: [PATCH 56/65] updated publish.yml --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7f0b117..ece0ce5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,6 +3,7 @@ on: push: branches: - master + - lessGC paths: - RELEASE_NOTES.md - .github/workflows/publish.yml From dfb9b413719b27f8a144cf6c49ffcde9125eaa8e Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 16:41:15 +0200 Subject: [PATCH 57/65] updated publish.yml --- .github/workflows/publish.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ece0ce5..7be02d2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -33,7 +33,9 @@ jobs: - name: Install Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.100' + dotnet-version: | + 6.0.100 + 8.0.400 - name: Restore Tools run: dotnet tool restore - name: Paket Restore @@ -56,7 +58,9 @@ jobs: - name: Install Dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.100' + dotnet-version: | + 6.0.100 + 8.0.400 - name: Restore Tools run: dotnet tool restore - name: Paket Restore From c299c24fde8058f7fcb493a6e507574326e0c8a1 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 16:48:12 +0200 Subject: [PATCH 58/65] updated publish.yml --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7be02d2..6f7ae75 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -70,7 +70,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: dotnet aardpack FSharp.Data.Adaptive.sln --notag - name: Upload Package - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: packages path: bin\pack From a6e107de68e4a2c431b09a62dd021e4eec1e1a07 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 17:02:09 +0200 Subject: [PATCH 59/65] updated Aardvark.Build and aardpack to 2.0.2 --- .config/dotnet-tools.json | 2 +- RELEASE_NOTES.md | 1 + paket.dependencies | 2 +- paket.lock | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 35874ed..2bb48cf 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -17,7 +17,7 @@ "rollForward": false }, "aardpack": { - "version": "1.0.11", + "version": "2.0.2", "commands": [ "aardpack" ], diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 70ba6e3..5fcf6fe 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,7 @@ - preferred using struct enumerators - avoided using active patterns to match set operations - updated Tests to net 8.0 +- updated Aardvark.Build and aardpack to 2.0.2 - fixed race condition in Index ### 1.2.16 diff --git a/paket.dependencies b/paket.dependencies index db1cb4f..7e46629 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -5,7 +5,7 @@ framework: auto-detect nuget FSharp.Core >= 4.7.0 lowest_matching: true nuget System.Reflection.Emit.Lightweight >= 4.6.0 lowest_matching: true -nuget Aardvark.Build ~> 1.0.18 +nuget Aardvark.Build ~> 2.0.2 # Tests : intentionally in main group nuget FsUnit ~> 3.4.1 diff --git a/paket.lock b/paket.lock index 3336d97..a05e757 100644 --- a/paket.lock +++ b/paket.lock @@ -3,7 +3,7 @@ STORAGE: NONE RESTRICTION: || (== net5.0) (== net6.0) (== net8.0) (== netstandard2.0) NUGET remote: https://api.nuget.org/v3/index.json - Aardvark.Build (1.0.25) + Aardvark.Build (2.0.2) BenchmarkDotNet (0.14) BenchmarkDotNet.Annotations (>= 0.14) CommandLineParser (>= 2.9.1) From 98bdc03b17954a7229c5b16a06ddc0150df40f05 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 17:45:34 +0200 Subject: [PATCH 60/65] updated to net8.0 using global.json --- .github/workflows/dotnet.yml | 8 +++---- .github/workflows/fable.yml | 6 ++--- .github/workflows/publish.yml | 22 ++++++++----------- global.json | 7 ++++++ paket.lock | 18 +++++++++------ src/Demo/CSharpInterop/CSharpInterop.csproj | 2 +- src/Demo/EventInterop/EventInterop.fsproj | 2 +- src/Demo/Fable/Fable.fsproj | 2 +- src/Demo/FileSystem/FileSystem.fsproj | 2 +- .../PublishTrimmedTest.fsproj | 2 +- src/Demo/Scratch/Scratch.fsproj | 2 +- src/FSharp.Data.Adaptive/Core/Core.fs | 2 +- .../FSharp.Data.Adaptive.fsproj | 10 +++++---- 13 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 global.json diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 24811e3..815085a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -6,13 +6,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Dotnet - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: - dotnet-version: | - 6.0.100 - 8.0.400 + global-json-file: global.json - name: Restore Tools run: dotnet tool restore - name: Paket Restore diff --git a/.github/workflows/fable.yml b/.github/workflows/fable.yml index 51ca700..37684ab 100644 --- a/.github/workflows/fable.yml +++ b/.github/workflows/fable.yml @@ -6,11 +6,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Dotnet - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: - dotnet-version: '6.0.100' + global-json-file: global.json - name: Restore Tools run: dotnet tool restore - name: Paket Restore diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6f7ae75..39246c4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,11 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Dotnet - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: - dotnet-version: '6.0.100' + global-json-file: global.json - name: Restore Tools run: dotnet tool restore - name: Paket Restore @@ -29,13 +29,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Dotnet - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: - dotnet-version: | - 6.0.100 - 8.0.400 + global-json-file: global.json - name: Restore Tools run: dotnet tool restore - name: Paket Restore @@ -54,13 +52,11 @@ jobs: runs-on: windows-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Dotnet - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: - dotnet-version: | - 6.0.100 - 8.0.400 + global-json-file: global.json - name: Restore Tools run: dotnet tool restore - name: Paket Restore diff --git a/global.json b/global.json new file mode 100644 index 0000000..19f8dc2 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.0", + "rollForward": "latestFeature", + "allowPrerelease": false + } +} \ No newline at end of file diff --git a/paket.lock b/paket.lock index a05e757..7bac20b 100644 --- a/paket.lock +++ b/paket.lock @@ -58,7 +58,7 @@ NUGET Iced (1.21) Microsoft.Bcl.AsyncInterfaces (8.0) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net5.0) (>= net462)) (&& (== net5.0) (< netstandard2.1)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) - Microsoft.CodeAnalysis.Analyzers (3.3.4) + Microsoft.CodeAnalysis.Analyzers (3.11) Microsoft.CodeAnalysis.Common (4.11) Microsoft.CodeAnalysis.Analyzers (>= 3.3.4) System.Buffers (>= 4.5.1) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) @@ -81,7 +81,7 @@ NUGET System.Text.Encoding.CodePages (>= 7.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (< net7.0)) (== netstandard2.0) Microsoft.CodeCoverage (17.11.1) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) - Microsoft.Diagnostics.NETCore.Client (0.2.532401) + Microsoft.Diagnostics.NETCore.Client (0.2.547301) Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) Microsoft.Extensions.Logging.Abstractions (>= 6.0.4) System.Buffers (>= 4.5.1) - restriction: || (== net5.0) (&& (== net8.0) (< net6.0)) (== netstandard2.0) @@ -106,12 +106,13 @@ NUGET System.Runtime.InteropServices (>= 4.1) System.Runtime.InteropServices.RuntimeInformation (>= 4.0) Microsoft.DotNet.PlatformAbstractions (3.1.6) - Microsoft.Extensions.DependencyInjection.Abstractions (8.0.1) + Microsoft.Extensions.DependencyInjection.Abstractions (8.0.2) Microsoft.Bcl.AsyncInterfaces (>= 8.0) - restriction: || (&& (== net5.0) (>= net462)) (&& (== net5.0) (< netstandard2.1)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net5.0) (>= net462)) (&& (== net5.0) (< netstandard2.1)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.Logging.Abstractions (8.0.1) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.1) + Microsoft.Extensions.Logging.Abstractions (8.0.2) + Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2) System.Buffers (>= 4.5.1) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Diagnostics.DiagnosticSource (>= 8.0.1) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (== netstandard2.0) System.Memory (>= 4.5.5) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) Microsoft.NET.Test.Sdk (16.4) Microsoft.CodeCoverage (>= 16.4) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp2.1)) @@ -209,6 +210,9 @@ NUGET Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) + System.Diagnostics.DiagnosticSource (8.0.1) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (== netstandard2.0) + System.Memory (>= 4.5.5) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (== netstandard2.0) System.Diagnostics.Process (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.Win32.Primitives (>= 4.3) @@ -288,8 +292,8 @@ NUGET Microsoft.NETCore.Targets (>= 1.1) System.Reflection (>= 4.3) System.Runtime (>= 4.3) - System.Reflection.Metadata (8.0) - System.Collections.Immutable (>= 8.0) + System.Reflection.Metadata (8.0.1) + System.Collections.Immutable (>= 8.0) - restriction: || (== net5.0) (== net6.0) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (&& (== net8.0) (< net7.0)) (== netstandard2.0) System.Memory (>= 4.5.5) - restriction: || (== net5.0) (&& (== net6.0) (>= net462)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< net6.0)) (== netstandard2.0) System.Reflection.Primitives (4.3) - restriction: || (== net5.0) (== net6.0) (== net8.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (>= 1.1) diff --git a/src/Demo/CSharpInterop/CSharpInterop.csproj b/src/Demo/CSharpInterop/CSharpInterop.csproj index e5201e3..138f718 100644 --- a/src/Demo/CSharpInterop/CSharpInterop.csproj +++ b/src/Demo/CSharpInterop/CSharpInterop.csproj @@ -3,7 +3,7 @@ Exe 8 - net6.0 + net8.0 ..\..\..\bin\Release\ diff --git a/src/Demo/EventInterop/EventInterop.fsproj b/src/Demo/EventInterop/EventInterop.fsproj index dbd3261..e8cd3b9 100644 --- a/src/Demo/EventInterop/EventInterop.fsproj +++ b/src/Demo/EventInterop/EventInterop.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 true diff --git a/src/Demo/Fable/Fable.fsproj b/src/Demo/Fable/Fable.fsproj index 6089d37..6fa1bfd 100644 --- a/src/Demo/Fable/Fable.fsproj +++ b/src/Demo/Fable/Fable.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 true diff --git a/src/Demo/FileSystem/FileSystem.fsproj b/src/Demo/FileSystem/FileSystem.fsproj index 0a446e1..249bd99 100644 --- a/src/Demo/FileSystem/FileSystem.fsproj +++ b/src/Demo/FileSystem/FileSystem.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 true diff --git a/src/Demo/PublishTrimmedTest/PublishTrimmedTest.fsproj b/src/Demo/PublishTrimmedTest/PublishTrimmedTest.fsproj index 9cc16e6..b0c03fb 100644 --- a/src/Demo/PublishTrimmedTest/PublishTrimmedTest.fsproj +++ b/src/Demo/PublishTrimmedTest/PublishTrimmedTest.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 true diff --git a/src/Demo/Scratch/Scratch.fsproj b/src/Demo/Scratch/Scratch.fsproj index 243cf7a..147f90b 100644 --- a/src/Demo/Scratch/Scratch.fsproj +++ b/src/Demo/Scratch/Scratch.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 true diff --git a/src/FSharp.Data.Adaptive/Core/Core.fs b/src/FSharp.Data.Adaptive/Core/Core.fs index 6e30a5f..1a4cae9 100644 --- a/src/FSharp.Data.Adaptive/Core/Core.fs +++ b/src/FSharp.Data.Adaptive/Core/Core.fs @@ -287,7 +287,7 @@ and internal WeakOutputSet() = else // r cannot be null here (empty index would have been found) let set = HashSet>() - #if NET5_0_OR_GREATER // NOTE: could also use netstadnard2.1 + #if NET8_0_OR_GREATER set.EnsureCapacity(HashSetCapacity) |> ignore #endif for r in data.Array do diff --git a/src/FSharp.Data.Adaptive/FSharp.Data.Adaptive.fsproj b/src/FSharp.Data.Adaptive/FSharp.Data.Adaptive.fsproj index a8c0ef0..61d68d2 100644 --- a/src/FSharp.Data.Adaptive/FSharp.Data.Adaptive.fsproj +++ b/src/FSharp.Data.Adaptive/FSharp.Data.Adaptive.fsproj @@ -2,11 +2,9 @@ Library - netstandard2.0;net5.0;net6.0 + netstandard2.0;net8.0 true - - link - true + https://github.com/fsprojects/FSharp.Data.Adaptive/blob/master/LICENSE @@ -18,6 +16,10 @@ fixed-left default + + link + true + ..\..\bin\Debug\ ..\..\bin\Debug\netstandard2.0\FSharp.Data.Adaptive.XML From 8434065b6c378b5097f2a8372401f33c95a679ca Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 18:07:27 +0200 Subject: [PATCH 61/65] updated fable --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 2bb48cf..206d956 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -10,7 +10,7 @@ "rollForward": false }, "fable": { - "version": "3.7.6", + "version": "4.22.0", "commands": [ "fable" ], From 60b1b4b4baeaa7eb1c4f3e163bb540584c067ab5 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 18:12:06 +0200 Subject: [PATCH 62/65] updated RELEASE_NOTES.md --- RELEASE_NOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5fcf6fe..a4200cf 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -11,8 +11,9 @@ - changed Index garbage collection to run in finalizer - preferred using struct enumerators - avoided using active patterns to match set operations -- updated Tests to net 8.0 +- updated to net 8.0 - updated Aardvark.Build and aardpack to 2.0.2 +- updated Fable to 4.22.0 - fixed race condition in Index ### 1.2.16 From 864111208036023d8cb99e191933c306e8da5fb3 Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Tue, 8 Oct 2024 18:36:28 +0200 Subject: [PATCH 63/65] downgraded aardpack to 1.0.25 --- .config/dotnet-tools.json | 2 +- RELEASE_NOTES.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 206d956..bc45961 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -17,7 +17,7 @@ "rollForward": false }, "aardpack": { - "version": "2.0.2", + "version": "1.0.25", "commands": [ "aardpack" ], diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a4200cf..b0869f1 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,7 +12,8 @@ - preferred using struct enumerators - avoided using active patterns to match set operations - updated to net 8.0 -- updated Aardvark.Build and aardpack to 2.0.2 +- updated Aardvark.Build 2.0.2 +- updated aardpack to 1.0.25 - updated Fable to 4.22.0 - fixed race condition in Index From 0837ac27d18622c56d6fa50ea653f1ec5d3fd72b Mon Sep 17 00:00:00 2001 From: Christian Luksch Date: Wed, 9 Oct 2024 18:17:40 +0200 Subject: [PATCH 64/65] [CSharp] fixed AdaptiveHashSet.OfListTree/OfSetTree creators being extensions [FSharp] added ASet.toAMapIgnoreDuplicates --- src/CSharp.Data.Adaptive/ASet.fs | 22 +++++++++---------- .../CollectionExtensions.fs | 3 +++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/CSharp.Data.Adaptive/ASet.fs b/src/CSharp.Data.Adaptive/ASet.fs index d66c765..4677231 100644 --- a/src/CSharp.Data.Adaptive/ASet.fs +++ b/src/CSharp.Data.Adaptive/ASet.fs @@ -47,6 +47,17 @@ type AdaptiveHashSet private() = [] static member OfHashSet<'T>(set: HashSet<'T>) = ASet.ofHashSet set + + /// Creates an AdaptiveHashSet from a tree of lists + /// NOTE: does not expect duplicates -> TODO + [] + static member OfListTree(list: alist<'TNode>, getChildren : Func<'TNode, alist<'TNode>>) = + list |> ASet.ofListTree getChildren.Invoke + + /// Creates an AdaptiveHashSet from a tree of sets + [] + static member OfSetTree(set: aset<'TNode>, getChildren : Func<'TNode, aset<'TNode>>) = + set |> ASet.ofSetTree getChildren.Invoke [] static member ToAdaptiveHashSet(this: seq<'T>) = ASet.ofSeq this @@ -291,17 +302,6 @@ type AdaptiveHashSet private() = static member ToAdaptiveHashMapIgnoreDuplicates(this: aset<'TValue>, getKey : Func<'TValue, 'TKey>) = this |> AMap.ofASetMappedIgnoreDuplicates getKey.Invoke - /// Creates an AdaptiveHashSet from tree of lists - /// NOTE: does not expect duplicates -> TODO - [] - static member OfListTree(this: alist<'TNode>, getChildren : Func<'TNode, alist<'TNode>>) = - this |> ASet.ofListTree getChildren.Invoke - - /// Creates an AdaptiveHashSet from tree of sets - [] - static member OfSetTree(this: aset<'TNode>, getChildren : Func<'TNode, aset<'TNode>>) = - this |> ASet.ofSetTree getChildren.Invoke - [] static member ToArray(this: aset<'T>) = this |> ASet.force |> HashSet.toArray diff --git a/src/FSharp.Data.Adaptive/CollectionExtensions.fs b/src/FSharp.Data.Adaptive/CollectionExtensions.fs index fed2c2c..7d1e525 100644 --- a/src/FSharp.Data.Adaptive/CollectionExtensions.fs +++ b/src/FSharp.Data.Adaptive/CollectionExtensions.fs @@ -594,6 +594,9 @@ module CollectionExtensions = let ofSetTree<'T> (getChildren : 'T -> aset<'T>) (nodes : aset<'T>) = ASet.ofReader (fun () -> SetTreeReader(nodes, getChildren)) + /// maps the set to amap with the given key mapping and duplicates ignored + let toAMapIgnoreDuplicates (getKey: 'T -> 'K) (set: aset<'T>) = + set |> AMap.ofASetMappedIgnoreDuplicates getKey /// Functional operators for alist<_> [] From e5a7dd9521c4107fd051e7c9c251876a056be06a Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 17 Feb 2025 17:32:26 +0100 Subject: [PATCH 65/65] [CSharp] Revert renaming of parameters See: https://github.com/fsprojects/FSharp.Data.Adaptive/pull/118 --- src/CSharp.Data.Adaptive/ASet.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CSharp.Data.Adaptive/ASet.fs b/src/CSharp.Data.Adaptive/ASet.fs index 4677231..a8a5073 100644 --- a/src/CSharp.Data.Adaptive/ASet.fs +++ b/src/CSharp.Data.Adaptive/ASet.fs @@ -283,12 +283,12 @@ type AdaptiveHashSet private() = this |> ASet.toAList [] - static member MapToMap(this: aset<'T>, getValue: Func<'T, 'Value>) = - this |> ASet.mapToAMap getValue.Invoke + static member MapToMap(this: aset<'T>, mapping: Func<'T, 'Value>) = + this |> ASet.mapToAMap mapping.Invoke [] - static member GroupBy(this: aset<'T>, getKey: Func<'T, 'G>) = - this |> ASet.groupBy getKey.Invoke + static member GroupBy(this: aset<'T>, mapping: Func<'T, 'G>) = + this |> ASet.groupBy mapping.Invoke [] static member ToAdaptiveHashMap(this: aset<'K * 'V>) =