From e764d286631d6e37bec82e38923188d78fae49e1 Mon Sep 17 00:00:00 2001 From: Denis Bobrov Date: Fri, 3 Jun 2022 17:56:40 +0400 Subject: [PATCH] Added Version11 --- TopologicalSort/Program.fs | 42 ++++ TopologicalSort/TopologicalSort.fsproj | 1 + TopologicalSort/Version11.fs | 255 +++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 TopologicalSort/Version11.fs diff --git a/TopologicalSort/Program.fs b/TopologicalSort/Program.fs index 668919f..56f8efe 100644 --- a/TopologicalSort/Program.fs +++ b/TopologicalSort/Program.fs @@ -284,6 +284,32 @@ module Data = |> Graph.create ] + module Version11 = + + open TopologicalSort.Version11 + + // Create a new random number generator with the same seed + let rng = Random rngSeed + + // Create the list of Nodes that we will use + let nodes = + [for i in 0 .. nodeCount - 1 -> + Node.create i] + + // Generate the random Graphs we will solve + let graphs = + [for _ in 1 .. graphCount -> + [|for sourceIdx in 0 .. nodeCount - 2 do + // We use a weighted distribution for the number of edges + for _ in 1 .. randomEdgeCount[(rng.Next randomEdgeCount.Length)] do + let targetIdx = rng.Next (sourceIdx + 1, nodeCount - 1) + let source = nodes[sourceIdx] + let target = nodes[targetIdx] + Edge.create source target |] + |> Array.distinct + |> Graph.create + ] + [] [] + member _.V11 () = + let mutable result = ValueNone + + for graph in Data.Version11.graphs do + // I separate the assignment so I can set a breakpoint in debugging + let sortedOrder = Version11.Graph.GraphType.Sort &graph + result <- sortedOrder + result let profile (version: string) loopCount = @@ -471,6 +507,12 @@ let profile (version: string) loopCount = | Some order -> result <- result + 1 | None -> result <- result - 1 + | "v11" -> + for i in 1 .. loopCount do + match b.V11 () with + | ValueSome order -> result <- result + 1 + | ValueNone -> result <- result - 1 + | unknownVersion -> failwith $"Unknown version: {unknownVersion}" result diff --git a/TopologicalSort/TopologicalSort.fsproj b/TopologicalSort/TopologicalSort.fsproj index 1460597..1120dc0 100644 --- a/TopologicalSort/TopologicalSort.fsproj +++ b/TopologicalSort/TopologicalSort.fsproj @@ -16,6 +16,7 @@ + diff --git a/TopologicalSort/Version11.fs b/TopologicalSort/Version11.fs new file mode 100644 index 0000000..804d09f --- /dev/null +++ b/TopologicalSort/Version11.fs @@ -0,0 +1,255 @@ +module TopologicalSort.Version11 + +open System +open System.Collections.Generic +open System.Numerics +open System.Runtime.InteropServices +open Row + +[] +module private Units = + + [] type Node + [] type Edge + [] type Index + + +type Index = int + +module Index = + + let inline create (i: int) = + if i < 0 then + invalidArg (nameof i) "Cannot have an Index less than 0" + + LanguagePrimitives.Int32WithMeasure i + + +type Node = int + +module Node = + + let inline create (i: int) = + if i < 0 then + invalidArg (nameof i) "Cannot have a Node less than 0" + + LanguagePrimitives.Int32WithMeasure i + + +type Edge = int64 + +module Edge = + + let inline create (source: Node) (target: Node) = + (((int64 source) <<< 32) ||| (int64 target)) + |> LanguagePrimitives.Int64WithMeasure + + let inline getSource (edge: Edge) = + ((int64 edge) >>> 32) + |> int + |> LanguagePrimitives.Int32WithMeasure + + let inline getTarget (edge: Edge) = + int edge + |> LanguagePrimitives.Int32WithMeasure + + +[] +type Range = + { + Start : Index + Length : Index + } + static member Zero = + { + Start = Index.create 0 + Length = Index.create 0 + } + +module Range = + + let create start length = + { + Start = start + Length = length + } + + +type SourceRanges = Bar +type SourceEdges = Bar +type TargetRanges = Bar +type TargetEdges = Bar + +[] +type Graph = { + SourceRanges : SourceRanges + SourceEdges : SourceEdges + TargetRanges : TargetRanges + TargetEdges : TargetEdges +} + +module Graph = + + let private getNodeCount (edges: Edge[]) = + let nodes = HashSet() + + for edge in edges do + let source = Edge.getSource edge + let target = Edge.getTarget edge + nodes.Add source |> ignore + nodes.Add target |> ignore + + LanguagePrimitives.Int32WithMeasure nodes.Count + + let private createSourcesAndTargets (nodeCount: int) (edges: Edge[]) = + let sourcesAcc = Row.create nodeCount [] + let targetsAcc = Row.create nodeCount [] + + for edge in edges do + let source = Edge.getSource edge + let target = Edge.getTarget edge + + sourcesAcc[target] <- edge :: sourcesAcc[target] + targetsAcc[source] <- edge :: targetsAcc[source] + + let finalSources = + sourcesAcc + |> Row.map Array.ofList + + let finalTargets = + targetsAcc + |> Row.map Array.ofList + + finalSources.Bar, finalTargets.Bar + + + let private createIndexesAndValues (nodeData: Bar<'Measure, Edge[]>) = + let ranges = Row.create nodeData.Length Range.Zero + let mutable nextStartIndex = Index.create 0 + + nodeData + |> Bar.iteri (fun nodeId nodes -> + let length = + nodes.Length + |> int + |> Index.create + let newRange = Range.create nextStartIndex length + ranges[nodeId] <- newRange + nextStartIndex <- nextStartIndex + length + ) + + let values = + nodeData._Values + |> Array.concat + |> Bar + + ranges.Bar, values + + + let create (edges: Edge[]) = + let nodeCount = getNodeCount edges + let nodeSources, nodeTargets = createSourcesAndTargets nodeCount edges + + let sourceRanges, sourceNodes = createIndexesAndValues nodeSources + let targetRanges, targetNodes = createIndexesAndValues nodeTargets + + { + SourceRanges = sourceRanges + SourceEdges = sourceNodes + TargetRanges = targetRanges + TargetEdges = targetNodes + } + + + type GraphType = + static member inline AddToTracker(tracker: Span,nodeCount:int, edge: Edge) = + let source = Edge.getSource edge + let target = Edge.getTarget edge + let location = (int source) * nodeCount + (int target) + + let bucket = location >>> 6 + let offset = location &&& 0x3F + let mask = 1UL <<< offset + tracker[bucket] <- tracker[bucket] ||| mask + + static member inline RemoveFromTracker(tracker: Span, nodeCount: int, edge: Edge) = + let source = Edge.getSource edge + let target = Edge.getTarget edge + let location = (int source) * nodeCount + (int target) + let bucket = location >>> 6 + let offset = location &&& 0x3F + let mask = 1UL <<< offset + tracker[bucket] <- tracker[bucket] &&& ~~~mask + target + + static member inline TrackerNotContains(tracker: Span,nodeCount: int, edge: Edge) = + let source = Edge.getSource edge + let target = Edge.getTarget edge + let location = (int source) * nodeCount + (int target) + let bucket = location >>> 6 + let offset = location &&& 0x3F + ((tracker[bucket] >>> offset) &&& 1UL) <> 1UL + + static member inline TrackerCount(tracker: Span) = + let mutable count = 0 + for i = 0 to tracker.Length - 1 do + count <- count + (BitOperations.PopCount tracker[i]) + count + + + static member Sort(graph: inref) = + let sourceRanges = graph.SourceRanges + let sourceEdges = graph.SourceEdges + let targetRanges = graph.TargetRanges + let targetEdges = graph.TargetEdges + let sourceRangeLength = int sourceRanges.Length + let result = GC.AllocateUninitializedArray sourceRangeLength + let mutable nextToProcessIdx = 0 + let mutable resultCount = 0 + + let mutable nodeId = 0 + + while nodeId < sourceRanges.Length do + if sourceRanges[nodeId].Length = 0 then + result[resultCount] <- nodeId + resultCount <- resultCount + 1 + nodeId <- nodeId + 1 + + let bitsRequired = ((sourceRangeLength * sourceRangeLength) + 63) / 64 + let remainingEdges = (GC.AllocateUninitializedArray bitsRequired) + let remainingEdgesSpan = remainingEdges.AsSpan() + for edge in sourceEdges._Values do + GraphType.AddToTracker(remainingEdgesSpan, sourceRangeLength, edge) + + while nextToProcessIdx < result.Length && nextToProcessIdx < resultCount do + + let targetRange = targetRanges[result[nextToProcessIdx]] + let mutable targetIndex = targetRange.Start + let bound = targetRange.Start + targetRange.Length + while targetIndex < bound do + // Check if all of the Edges have been removed for this + // Target Node + let targetNodeId = GraphType.RemoveFromTracker(remainingEdgesSpan, sourceRangeLength, targetEdges[targetIndex]) + + let mutable noRemainingSourcesResult = true + let range = sourceRanges[targetNodeId] + let mutable sourceIndex = range.Start + let bound = range.Start + range.Length + + while sourceIndex < bound && noRemainingSourcesResult do + noRemainingSourcesResult <- GraphType.TrackerNotContains(remainingEdgesSpan, sourceRangeLength, sourceEdges[sourceIndex]) + sourceIndex <- sourceIndex + LanguagePrimitives.Int32WithMeasure 1 + + if noRemainingSourcesResult then + result[resultCount] <- targetNodeId + resultCount <- resultCount + 1 + + targetIndex <- targetIndex + 1 + + nextToProcessIdx <- nextToProcessIdx + 1 + + + if GraphType.TrackerCount(remainingEdges) > 0 then + ValueNone + else + ValueSome result \ No newline at end of file