@@ -13,89 +13,23 @@ type NodeInfo<'Item> =
1313 Dependants: 'Item []
1414 }
1515
16- type StateMeta < 'Item > =
17- {
18- Contributors: 'Item []
19- }
20-
21- static member Empty () = { Contributors = [||] }
22-
23- type StateWrapper < 'Item , 'State > =
24- {
25- Meta: StateMeta < 'Item >
26- State: 'State
27- }
28-
29- type ResultWrapper < 'Item , 'Result > = { Item: 'Item ; Result: 'Result }
30-
31- type Node < 'Item , 'State , 'Result > =
16+ type Node < 'Item , 'Result > =
3217 {
3318 Info: NodeInfo < 'Item >
3419 mutable ProcessedDepsCount: int
35- mutable Result: ( 'State * 'Result ) option
36- mutable InputState: 'State option
20+ mutable Result: 'Result option
3721 }
3822
39- // TODO Do we need to suppress some error logging if we
40- // TODO apply the same partial results multiple times?
41- // TODO Maybe we can enable logging only for the final fold
42- /// <summary>
43- /// Combine results of dependencies needed to type-check a 'higher' node in the graph
44- /// </summary>
45- /// <param name="deps">Direct dependencies of a node</param>
46- /// <param name="transitiveDeps">Transitive dependencies of a node</param>
47- /// <param name="folder">A way to fold a single result into existing state</param>
48- let combineResults
49- ( emptyState : 'State )
50- ( deps : Node < 'Item , 'State , 'Result >[])
51- ( transitiveDeps : Node < 'Item , 'State , 'Result >[])
52- ( folder : 'State -> 'Result -> 'State )
53- : 'State =
54- match deps with
55- | [||] -> emptyState
56- | _ ->
57- let biggestDep =
58- let sizeMetric node =
59- node.Info.TransitiveDeps.Length
60- deps
61- |> Array.maxBy sizeMetric
62-
63- let orFail value =
64- value |> Option.defaultWith ( fun () -> failwith " Unexpected lack of result" )
65-
66- let firstState = biggestDep.Result |> orFail |> fst
67-
68- // TODO Potential perf optimisation: Keep transDeps in a HashSet from the start,
69- // avoiding reconstructing the HashSet here
70-
71- // Add single-file results of remaining transitive deps one-by-one using folder
72- // Note: Good to preserve order here so that folding happens in file order
73- let included =
74- let set = HashSet( biggestDep.Info.TransitiveDeps)
75- set.Add biggestDep.Info.Item |> ignore
76- set
77-
78- let resultsToAdd =
79- transitiveDeps
80- |> Array.filter ( fun dep -> included.Contains dep.Info.Item = false )
81- |> Array.distinctBy ( fun dep -> dep.Info.Item)
82- |> Array.map ( fun dep -> dep.Result |> orFail |> snd)
83-
84- let state = Array.fold folder firstState resultsToAdd
85- state
86-
87- // TODO Could be replaced with a simpler recursive approach with memoised per-item results
88- let processGraph < 'Item , 'State , 'Result , 'FinalFileResult when 'Item : equality and 'Item : comparison >
23+ let processGraphSimple < 'Item , 'Result when 'Item : equality and 'Item : comparison >
8924 ( graph : Graph < 'Item >)
90- ( doWork : 'Item -> 'State -> 'Result )
91- ( folder : bool -> 'State -> 'Result -> 'FinalFileResult * 'State )
92- ( emptyState : 'State )
25+ ( doWork : IReadOnlyDictionary < 'Item , Node < 'Item , 'Result >> -> Node < 'Item , 'Result > -> 'Result )
9326 ( parallelism : int )
94- : 'FinalFileResult [] * 'State =
27+ : 'Result [] // Results in order defined in 'graph'
28+ =
9529 let transitiveDeps = graph |> Graph.transitiveOpt
9630 let dependants = graph |> Graph.reverse
9731
98- let makeNode ( item : 'Item ) : Node < 'Item , StateWrapper < 'Item , 'State >, ResultWrapper < 'Item , ' Result> > =
32+ let makeNode ( item : 'Item ) : Node < 'Item , ' Result> =
9933 let info =
10034 let exists = graph.ContainsKey item
10135
@@ -104,7 +38,7 @@ let processGraph<'Item, 'State, 'Result, 'FinalFileResult when 'Item: equality a
10438 || not ( transitiveDeps.ContainsKey item)
10539 || not ( dependants.ContainsKey item)
10640 then
107- printfn $" WHAT {item}"
41+ printfn $" Unexpected inconsistent state of the graph for item ' {item}' "
10842
10943 {
11044 Item = item
@@ -117,56 +51,26 @@ let processGraph<'Item, 'State, 'Result, 'FinalFileResult when 'Item: equality a
11751 Info = info
11852 Result = None
11953 ProcessedDepsCount = 0
120- InputState = None
12154 }
12255
123- let nodes = graph.Keys |> Seq.map ( fun item -> item, makeNode item) |> readOnlyDict
56+ let nodes =
57+ graph.Keys
58+ |> Seq.map ( fun item -> item, makeNode item)
59+ |> readOnlyDict
12460 let lookup item = nodes[ item]
12561 let lookupMany items = items |> Array.map lookup
126-
12762 let leaves =
128- nodes.Values |> Seq.filter ( fun n -> n.Info.Deps.Length = 0 ) |> Seq.toArray
129-
130- let emptyState =
131- {
132- Meta = StateMeta.Empty< 'Item>()
133- State = emptyState
134- }
135-
136- let folder ( isFinalFold : bool ) { Meta = meta ; State = state } { Item = item ; Result = result } =
137- let finalFileResult , state = folder isFinalFold state result
138-
139- let state =
140- {
141- Meta =
142- {
143- Contributors = Array.append meta.Contributors [| item |]
144- }
145- State = state
146- }
147-
148- finalFileResult, state
63+ nodes.Values
64+ |> Seq.filter ( fun n -> n.Info.Deps.Length = 0 )
65+ |> Seq.toArray
14966
15067 printfn $" Node count: {nodes.Count}"
15168
15269 let work
153- ( node : Node < 'Item , StateWrapper < 'Item , 'State >, ResultWrapper < 'Item , 'Result >>)
154- : Node < 'Item , StateWrapper < 'Item , 'State >, ResultWrapper < 'Item , 'Result >>[] =
155- let folder x y = folder false x y |> snd
156- let deps = lookupMany node.Info.Deps
157- let transitiveDeps = lookupMany node.Info.TransitiveDeps
158- let inputState = combineResults emptyState deps transitiveDeps folder
159- node.InputState <- Some inputState
160- let singleRes = doWork node.Info.Item inputState.State
161-
162- let singleRes =
163- {
164- Item = node.Info.Item
165- Result = singleRes
166- }
167-
168- let state = folder inputState singleRes
169- node.Result <- Some( state, singleRes)
70+ ( node : Node < 'Item , 'Result >)
71+ : Node < 'Item , 'Result >[] =
72+ let singleRes = doWork nodes node
73+ node.Result <- Some singleRes
17074 // Need to double-check that only one dependency schedules this dependant
17175 let unblocked =
17276 node.Info.Dependants
@@ -177,18 +81,7 @@ let processGraph<'Item, 'State, 'Result, 'FinalFileResult when 'Item: equality a
17781 lock x ( fun () ->
17882 x.ProcessedDepsCount <- x.ProcessedDepsCount + 1
17983 x.ProcessedDepsCount)
180-
18184 pdc = x.Info.Deps.Length)
182- // printfn $"State after {node.Info.Item}"
183- // nodes
184- // |> Seq.map (fun (KeyValue(_, v)) ->
185- // let x = v.Info.Deps.Length - v.ProcessedDepsCount
186- // $"{v.Info.Item} - {x} deps left"
187- // )
188- // |> Seq.iter (fun x -> printfn $"{x}")
189- // let c = cnt
190- // cnt <- cnt+1
191- // printfn $"Finished processing node. {unblocked.Length} nodes unblocked"
19285 unblocked
19386
19487 use cts = new CancellationTokenSource()
@@ -201,15 +94,9 @@ let processGraph<'Item, 'State, 'Result, 'FinalFileResult when 'Item: equality a
20194 cts.Token
20295 ( fun x -> x.Info.Item.ToString())
20396
204- let nodesArray = nodes.Values |> Seq.toArray
205-
206- let finals , { State = state }: 'FinalFileResult [] * StateWrapper < 'Item , 'State > =
207- nodesArray
208- |> Array.sortBy ( fun node -> node.Info.Item)
209- |> Array.fold
210- ( fun ( fileResults , state ) node ->
211- let fileResult , state = folder true state ( node.Result.Value |> snd)
212- Array.append fileResults [| fileResult |], state)
213- ([||], emptyState)
214-
215- finals, state
97+ nodes.Values
98+ |> Seq.map ( fun node ->
99+ node.Result
100+ |> Option.defaultWith ( fun () -> failwith $" Unexpected lack of result for item '{node.Info.Item}'" )
101+ )
102+ |> Seq.toArray
0 commit comments