diff --git a/TopologicalSort/Program.fs b/TopologicalSort/Program.fs index b4074b1..dba7fff 100644 --- a/TopologicalSort/Program.fs +++ b/TopologicalSort/Program.fs @@ -177,66 +177,106 @@ module V6Data = let graph = Graph.create edges +module V7Data = + + open TopologicalSort.Version7 + + let nodes = + [| + 0 + 1 + 2 + 3 + 4 + 5 + |] + + let edges = + [| + Edge.create nodes[0] nodes[1] + Edge.create nodes[0] nodes[2] + Edge.create nodes[1] nodes[3] + Edge.create nodes[1] nodes[4] + Edge.create nodes[2] nodes[1] + Edge.create nodes[2] nodes[4] + Edge.create nodes[3] nodes[5] + Edge.create nodes[4] nodes[5] + |] + + let graph = Graph.create edges + [] [] + HardwareCounter.BranchInstructions +// HardwareCounter.CacheMisses + )>] type Benchmarks () = - [] - member _.V1 () = - - let result = Version1.Topological.sort V1Data.graph - match result with - | Some _ -> 1 - | None -> 1 - +// [] +// member _.V1 () = +// +// let result = Version1.Topological.sort V1Data.graph +// match result with +// | Some _ -> 1 +// | None -> 1 +// +// +// [] +// member _.V2 () = +// +// let result = Version2.Topological.sort V2Data.graph +// match result with +// | Some _ -> 1 +// | None -> 1 +// +// +// [] +// member _.V3 () = +// +// let result = Version3.Topological.sort V3Data.graph +// match result with +// | Some _ -> 1 +// | None -> 1 +// +// +// [] +// member _.V4 () = +// +// let result = Version4.Topological.sort V4Data.graph +// match result with +// | Some _ -> 1 +// | None -> 1 +// +// +// [] +// member _.V5 () = +// +// let result = Version5.Topological.sort V5Data.graph +// match result with +// | Some _ -> 1 +// | None -> 1 +// [] - member _.V2 () = - - let result = Version2.Topological.sort V2Data.graph - match result with - | Some _ -> 1 - | None -> 1 - - - [] - member _.V3 () = + member _.V6 () = - let result = Version3.Topological.sort V3Data.graph + let result = Version6.Topological.sort V6Data.graph match result with | Some _ -> 1 | None -> 1 - - - [] - member _.V4 () = - let result = Version4.Topological.sort V4Data.graph - match result with - | Some _ -> 1 - | None -> 1 - - [] - member _.V5 () = + member _.V7 () = - let result = Version5.Topological.sort V5Data.graph + let result = Version7.Topological.sort V7Data.graph match result with - | Some _ -> 1 - | None -> 1 - - - [] - member _.V6 () = - - let result = Version6.Topological.sort V6Data.graph - match result with - | Some _ -> 1 - | None -> 1 + | Some _ -> + 1 + | None -> + printfn "v7 res is null" + 1 let profile (version: string) loopCount = @@ -245,29 +285,32 @@ let profile (version: string) loopCount = let mutable result = 0 match version.ToLower() with - | "v1" -> - for i in 1 .. loopCount do - result <- result + b.V1 () - - | "v2" -> - for i in 1 .. loopCount do - result <- result + b.V2 () - - | "v3" -> - for i in 1 .. loopCount do - result <- result + b.V3 () - - | "v4" -> - for i in 1 .. loopCount do - result <- result + b.V4 () - - | "v5" -> - for i in 1 .. loopCount do - result <- result + b.V5 () - +// | "v1" -> +// for i in 1 .. loopCount do +// result <- result + b.V1 () +// +// | "v2" -> +// for i in 1 .. loopCount do +// result <- result + b.V2 () +// +// | "v3" -> +// for i in 1 .. loopCount do +// result <- result + b.V3 () +// +// | "v4" -> +// for i in 1 .. loopCount do +// result <- result + b.V4 () +// +// | "v5" -> +// for i in 1 .. loopCount do +// result <- result + b.V5 () +// | "v6" -> for i in 1 .. loopCount do result <- result + b.V6 () + | "v7" -> + for i in 1 .. loopCount do + result <- result + b.V7 () | unknownVersion -> failwith $"Unknown version: {unknownVersion}" diff --git a/TopologicalSort/TopologicalSort.fsproj b/TopologicalSort/TopologicalSort.fsproj index 1b90832..9174d10 100644 --- a/TopologicalSort/TopologicalSort.fsproj +++ b/TopologicalSort/TopologicalSort.fsproj @@ -12,6 +12,7 @@ + diff --git a/TopologicalSort/Version7.fs b/TopologicalSort/Version7.fs new file mode 100644 index 0000000..02751db --- /dev/null +++ b/TopologicalSort/Version7.fs @@ -0,0 +1,326 @@ +module TopologicalSort.Version7 + +open System.Collections.Generic +open TopologicalSort.Row +open System +open System.Numerics + +//| Method | Mean | Error | StdDev | BranchInstructions/Op | BranchMispredictions/Op | Gen 0 | Allocated | +//|------- |---------:|--------:|--------:|----------------------:|------------------------:|-------:|----------:| +//| V6 | 283.0 ns | 0.69 ns | 0.65 ns | 692 | 0 | 0.0086 | 72 B | +//| V7 | 278.1 ns | 1.63 ns | 1.36 ns | 692 | 0 | 0.0086 | 72 B | + +[] type Node +[] type Edge + +type Array with + static member inline SIMDFold f h (start:'T) (values : 'T[]) = + let mutable i = 0; + let mutable v = Vector<'T>(start) + while i < values.Length - Vector<'T>.Count do + v <- f v (Vector<'T>(values,i)) + i <- i + Vector<'T>.Count + i <- 0 + let mutable result = start + while i < Vector<'T>.Count do + result <- h result v.[i] + i <- i+1 + result + + /// + /// Identical to the standard map function, but you must provide + /// A Vector mapping function. + /// + /// A function that takes a Vector and returns a Vector. The returned vector + /// does not have to be the same type but must be the same width + /// A function to handle the leftover scalar elements if array is not divisible by Vector.count + /// The source array + static member inline SIMDmap + (vf : ^T Vector -> ^U Vector) (sf : ^T -> ^U) (array : ^T[]) : ^U[] = + let count = Vector< ^T>.Count + if count <> Vector< ^U>.Count then invalidArg "array" "Output type must have the same width as input type." + + let result = Array.zeroCreate array.Length + + let mutable i = 0 + while i <= array.Length-count do + (vf (Vector< ^T>(array,i ))).CopyTo(result,i) + i <- i + count + + i <- array.Length-array.Length%count + while i < result.Length do + result.[i] <- sf array.[i] + i <- i + 1 + + result + + /// + /// Identical to the standard contains, just faster + /// + /// + /// + static member inline SIMDcontains (x : ^T) (array:^T[]) : bool = + + let count = Vector< ^T>.Count + let len = array.Length + let compareVector = Vector< ^T>(x) + + let mutable found = false + let mutable i = 0 + while i <= len-count do + found <- Vector.EqualsAny(Vector< ^T>(array,i),compareVector) + if found then i <- len + else i <- i + count + + i <- len-len%count + while i < array.Length && not found do + found <- x = array.[i] + i <- i + 1 + + found + + /// + /// Iterates over the array applying f to each Vector sized chunk + /// + /// Accepts a Vector + /// + static member inline SIMDiter + ([] vf : Vector< ^T> -> unit) + ([] sf : ^T -> unit) + (array : ^T[]) : unit = + + let len = array.Length + let count = Vector< ^T>.Count + + let mutable i = 0 + while i <= len-count do + vf (Vector< ^T>(array,i )) + i <- i + count + + i <- len-len%count + while i < array.Length do + sf array.[i] + i <- i + 1 + /// + /// Checks if all Vectors satisfy the predicate. + /// + /// Takes a Vector and returns true or false + /// + static member inline SIMDforall + ( [] vf : ^T Vector -> bool) + ( [] sf : ^T -> bool) + (array: ^T[]) : bool = + + let count = Vector< ^T>.Count + let mutable found = true + let len = array.Length + + let mutable i = 0 + while i <= len-count do + found <- vf (Vector< ^T>(array,i)) + if not found then i <- len + else i <- i + count + + i <- len-len%count + while i < array.Length && found do + found <- sf array.[i] + i <- i + 1 + + found + +module Edge = + + let create (source: int) (target: int) = + (((int64 source) <<< 32) ||| (int64 target)) + |> LanguagePrimitives.Int64WithMeasure + + let inline getSource (edge: int64) = + ((int64 edge) >>> 32) + |> int + |> LanguagePrimitives.Int32WithMeasure + + let inline getTarget (edge: int64) = + int edge + |> LanguagePrimitives.Int32WithMeasure + + +type Graph = + { + Nodes : int array + Origins : int array + Edges : int64 array + Sources : ReadOnlyRow array> + Targets : ReadOnlyRow array> + } + +module Graph = + + let private getDistinctNodes (edges: int64 array) = + + let distinctNodes = HashSet() + + for edge in edges do + let source = Edge.getSource edge + let target = Edge.getTarget edge + distinctNodes.Add source |> ignore + distinctNodes.Add target |> ignore + + Array.ofSeq distinctNodes + + + let private createSourcesAndTargets (nodeCount: int) (edges: int64 array) = + let nodeCount = LanguagePrimitives.Int32WithMeasure nodeCount + 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 + |> ReadOnlyRow + + let finalTargets = + targetsAcc + |> Row.map Array.ofList + |> ReadOnlyRow + + finalSources, finalTargets + + + let create (edges: int64 array) = + let edges = Array.distinct edges + let nodes = getDistinctNodes edges + let sources, targets = createSourcesAndTargets nodes.Length edges + let originNodes = + nodes + |> Array.filter (fun node -> sources[node].Length = 0) + { + Edges = edges + Nodes = nodes + Sources = sources + Targets = targets + Origins = originNodes + } + + +[] +module Topological = + open System.Linq + let private toProcess = Stack> (16) + let private sortedNodes = Queue> (16) + let private remainingEdges = HashSet> (16) + +// let mutable private sources = ReadOnlyRow([||]) +// let inline private targetFn graph edge = +// let target = Edge.getTarget edge +// remainingEdges.Remove edge |> ignore +//// let remEdges = Vector>(remainingEdges.ToArray().AsSpan()) +//// Array.SIMDcontains +//// let remEdgesContains x = Vector>(remainingEdges.ToArray()) |> Array.SIMDcontains x +// let arr = remainingEdges.ToArray() +//// let remSrc = Vector>(graph.Sources[target].AsSpan()) +// +// let noRemainingSources = graph.Sources[target] |> Array.SIMDforall (fun vf -> Vector.EqualsAny(vf, Vector>(arr.AsSpan()))) (fun x -> Array.SIMDcontains x arr) +//// let noRemainingSources = +//// graph.Sources[target] +//// |> Array.forall (fun x -> Array.SIMDcontains x arr) +// +// if noRemainingSources then +// toProcess.Push target +// +// +// let inline private targetFn2 graph edge = +// let target = Edge.getTarget edge +// remainingEdges.Remove edge |> ignore +// let arr = remainingEdges.ToArray() +// let noRemainingSources = +// graph.Sources[target] +// |> Array.forall (fun x -> not (Array.SIMDcontains x arr)) +//// let noRemainingSources = graph.Sources[target] +//// |> Array.SIMDforall +//// (fun vf -> +//// not (Vector.EqualsAny(vf, Vector>(arr.AsSpan()))) +//// ) +//// (fun x -> not(Array.SIMDcontains x arr)) +// if noRemainingSources then +// toProcess.Push target +// +// + + let notContains item = not (remainingEdges.Contains(item)) + let inline notContains2 item arr = not(Array.SIMDcontains item arr) + let sort (graph: Graph) = + + toProcess.Clear() + sortedNodes.Clear() + remainingEdges.Clear () + + + for node in graph.Origins do + toProcess.Push node + + + for edge in graph.Edges do + remainingEdges.Add edge |> ignore + +// sources <- graph.Sources + +// let tfnGp = targetFn2 graph + while toProcess.Count > 0 do + let nextNode = toProcess.Pop() + sortedNodes.Enqueue nextNode + +// graph.Targets[nextNode] +// |> Array.iter (fun edge -> +// let target = Edge.getTarget edge +// remainingEdges.Remove edge |> ignore +//// let arr = remainingEdges.ToArray() +// let noRemainingSources = +// sources[target] +// |> Array.forall (notContains) +// if noRemainingSources then +// toProcess.Push target +// ) + + graph.Targets[nextNode] + |> Array.iter (fun edge -> //can't use Parallel.iter because it breaks remainingSources stuff + let target = Edge.getTarget edge + remainingEdges.Remove edge |> ignore + + let noRemainingSources = + graph.Sources[target] + |> Array.forall notContains + + if noRemainingSources then + toProcess.Push target + + ) + + + +// graph.Targets[nextNode] +// |> Array.iter tfnGp + +// for edge in graph.Targets[nextNode] do +// let target = Edge.getTarget edge +// remainingEdges.Remove edge |> ignore +// let arr = remainingEdges.ToArray() +// let noRemainingSources = graph.Sources[target] +// |> Array.SIMDforall +// (fun vf -> +// not (Vector.EqualsAny(vf, Vector>(arr.AsSpan()))) +// ) +// (fun x -> not(Array.SIMDcontains x arr)) +// if noRemainingSources then +// toProcess.Push target + + if remainingEdges.Count > 0 then + None + else + Some (sortedNodes.ToArray()) \ No newline at end of file