Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions TopologicalSort/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
]


[<MemoryDiagnoser>]
[<HardwareCounters(
Expand Down Expand Up @@ -401,6 +427,16 @@ type Benchmarks () =
let sortedOrder = Version10.sort graph
result <- sortedOrder

result
// [<Benchmark>]
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 =
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions TopologicalSort/TopologicalSort.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<Compile Include="Version8.fs" />
<Compile Include="Version9.fs" />
<Compile Include="Version10.fs" />
<Compile Include="Version11.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
Expand Down
255 changes: 255 additions & 0 deletions TopologicalSort/Version11.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
module TopologicalSort.Version11

open System
open System.Collections.Generic
open System.Numerics
open System.Runtime.InteropServices
open Row

[<RequireQualifiedAccess>]
module private Units =

[<Measure>] type Node
[<Measure>] type Edge
[<Measure>] type Index


type Index = int<Units.Index>

module Index =

let inline create (i: int) =
if i < 0 then
invalidArg (nameof i) "Cannot have an Index less than 0"

LanguagePrimitives.Int32WithMeasure<Units.Index> i


type Node = int<Units.Node>

module Node =

let inline create (i: int) =
if i < 0 then
invalidArg (nameof i) "Cannot have a Node less than 0"

LanguagePrimitives.Int32WithMeasure<Units.Node> i


type Edge = int64<Units.Edge>

module Edge =

let inline create (source: Node) (target: Node) =
(((int64 source) <<< 32) ||| (int64 target))
|> LanguagePrimitives.Int64WithMeasure<Units.Edge>

let inline getSource (edge: Edge) =
((int64 edge) >>> 32)
|> int
|> LanguagePrimitives.Int32WithMeasure<Units.Node>

let inline getTarget (edge: Edge) =
int edge
|> LanguagePrimitives.Int32WithMeasure<Units.Node>


[<Struct>]
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<Units.Node, Range>
type SourceEdges = Bar<Units.Index, Edge>
type TargetRanges = Bar<Units.Node, Range>
type TargetEdges = Bar<Units.Index, Edge>

[<Struct;StructLayout(LayoutKind.Sequential, Size=32)>]
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<Units.Node> nodes.Count

let private createSourcesAndTargets (nodeCount: int<Units.Node>) (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<Units.Index, _>

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<uint64>,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<uint64>, 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<uint64>,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<uint64>) =
let mutable count = 0
for i = 0 to tracker.Length - 1 do
count <- count + (BitOperations.PopCount tracker[i])
count


static member Sort(graph: inref<Graph>) =
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<Units.Node>

while nodeId < sourceRanges.Length do
if sourceRanges[nodeId].Length = 0<Units.Index> then
result[resultCount] <- nodeId
resultCount <- resultCount + 1
nodeId <- nodeId + 1<Units.Node>

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<Units.Index> 1

if noRemainingSourcesResult then
result[resultCount] <- targetNodeId
resultCount <- resultCount + 1

targetIndex <- targetIndex + 1<Units.Index>

nextToProcessIdx <- nextToProcessIdx + 1


if GraphType.TrackerCount(remainingEdges) > 0 then
ValueNone
else
ValueSome result