Skip to content

Commit 66c747e

Browse files
committed
changes
1 parent b00ed8a commit 66c747e

5 files changed

Lines changed: 239 additions & 28 deletions

File tree

tests/FSharp.Compiler.Service.Tests2/FSharp.Compiler.Service.Tests2.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<Compile Include="..\service\Common.fs">
2727
<Link>Common.fs</Link>
2828
</Compile>
29+
<Compile Include="Utils.fs" />
2930
<Compile Include="ASTVisit.fs" />
3031
<Compile Include="TestASTVisit.fs" />
3132
<Compile Include="DepResolving.fs" />
@@ -37,6 +38,7 @@
3738
</None>
3839
<Content Include="Docs.md" />
3940
<Compile Include="Program.fs" />
41+
<Compile Include="code.fs" />
4042
</ItemGroup>
4143

4244
<ItemGroup>

tests/FSharp.Compiler.Service.Tests2/FileGraph.fs

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,20 @@
22

33
#nowarn "40"
44

5-
open System.Collections.Concurrent
65
open System.Collections.Generic
7-
8-
type FileIdx =
9-
FileIdx of int
10-
with
11-
member this.Idx = match this with FileIdx idx -> idx
12-
override this.ToString() = this.Idx.ToString()
13-
static member make (idx : int) = FileIdx idx
6+
open FSharp.Compiler.Service.Tests.Utils
147

158
/// <summary> DAG of files </summary>
16-
type FileGraph = IReadOnlyDictionary<FileIdx, FileIdx[]>
17-
18-
let memoize<'a, 'b when 'a : equality> f : ('a -> 'b) =
19-
let y = HashIdentity.Structural<'a>
20-
let d = new ConcurrentDictionary<'a, 'b>(y)
21-
fun x -> d.GetOrAdd(x, fun r -> f r)
9+
type Graph<'Node> = IReadOnlyDictionary<'Node, 'Node[]>
2210

23-
module FileGraph =
11+
module Graph =
2412

25-
let calcTransitiveGraph (graph : FileGraph) : FileGraph =
26-
let transitiveGraph = Dictionary<FileIdx, FileIdx[]>()
13+
let transitive<'Node when 'Node : equality> (graph : Graph<'Node>) : Graph<'Node> =
14+
let transitiveGraph = Dictionary<'Node, 'Node[]>()
2715

2816
let rec calcTransitiveEdges =
29-
fun (idx : FileIdx) ->
30-
let edgeTargets = graph[idx]
17+
fun (node : 'Node) ->
18+
let edgeTargets = graph[node]
3119
edgeTargets
3220
|> Array.collect calcTransitiveEdges
3321
|> Array.append edgeTargets
@@ -39,10 +27,7 @@ module FileGraph =
3927

4028
transitiveGraph :> IReadOnlyDictionary<_,_>
4129

42-
let collectEdges (graph : FileGraph) =
43-
graph
44-
45-
let reverse (graph : FileGraph) : FileGraph =
30+
let reverse (graph : Graph<'Node>) : Graph<'Node> =
4631
graph
4732
// Collect all edges
4833
|> Seq.collect (fun (KeyValue(idx, deps)) -> deps |> Array.map (fun dep -> idx, dep))

tests/FSharp.Compiler.Service.Tests2/RunCompiler.fs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ let runGrapher () =
161161
// System.IO.File.ReadAllLines(@"C:\projekty\fsharp\heuristic\tests\FSharp.Compiler.Service.Tests2\args.txt") |> Array.skip 1
162162
// FSharp.Compiler.CommandLineMain.main args |> ignore
163163

164-
let deps : FileGraph =
164+
let deps : Graph =
165165
[|
166166
0, [||] // A
167167
1, [|0|] // B1 -> A
@@ -173,10 +173,10 @@ let runGrapher () =
173173
|> Array.map (fun (a, deps) -> FileIdx.make a, deps |> Array.map FileIdx.make)
174174
|> readOnlyDict
175175

176-
let dependants = deps |> FileGraph.reverse
176+
let dependants = deps |> Graph.reverse
177177

178-
let transitiveDeps = deps |> FileGraph.calcTransitiveGraph
179-
let transitiveDependants = transitiveDeps |> FileGraph.reverse
178+
let transitiveDeps = deps |> Graph.transitive
179+
let transitiveDependants = transitiveDeps |> Graph.reverse
180180

181181
let nodes =
182182
deps.Keys
@@ -193,6 +193,7 @@ let runGrapher () =
193193
node.Dependants <- processs dependants[idx]
194194
node.UnprocessedDepsCount <- node.Deps.Length
195195
)
196-
nodes.Values |> Seq.toArray
196+
nodes.Values
197+
|> Seq.toArray
197198

198199
processGraph graph
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module FSharp.Compiler.Service.Tests.Utils
2+
3+
#nowarn "40"
4+
5+
open System.Collections.Concurrent
6+
open System.Collections.Generic
7+
8+
let memoize<'a, 'b when 'a : equality> f : ('a -> 'b) =
9+
let y = HashIdentity.Structural<'a>
10+
let d = new ConcurrentDictionary<'a, 'b>(y)
11+
fun x -> d.GetOrAdd(x, fun r -> f r)
12+
13+
type FileIdx =
14+
FileIdx of int
15+
with
16+
member this.Idx = match this with FileIdx idx -> idx
17+
override this.ToString() = this.Idx.ToString()
18+
static member make (idx : int) = FileIdx idx
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
module FSharp.Compiler.Service.Tests.code
2+
3+
open System.Collections.Generic
4+
open FSharp.Compiler.Service.Tests.Graph
5+
open FSharp.Compiler.Syntax
6+
type AST = FSharp.Compiler.Syntax.ParsedInput
7+
8+
type FileName = FileName of string
9+
with member this.FileName = match this with FileName fileName -> fileName
10+
11+
/// Input from the compiler after parsing
12+
[<CustomEquality; NoComparison>]
13+
type SourceFile =
14+
{
15+
Name : string
16+
AST : AST
17+
}
18+
// custom check - compare against CustomerId only
19+
override this.Equals other =
20+
match other with
21+
| :? SourceFile as p -> p.Name.Equals this.Name
22+
| _ -> false
23+
// custom hash check
24+
override this.GetHashCode () = this.Name.GetHashCode()
25+
26+
type SourceFiles = SourceFile[]
27+
type FsiInfo =
28+
| FsiBacked
29+
| NotBacked
30+
with member this.IsFsiBacked =
31+
match this with
32+
| FsiBacked -> true
33+
| NotBacked -> false
34+
35+
type File =
36+
{
37+
File : SourceFile
38+
FsiInfo : FsiInfo
39+
}
40+
type Files = File[]
41+
type FileGraph = Graph<File>
42+
43+
let gatherBackingInfo (files : SourceFiles) : Files =
44+
let seenSigFiles = HashSet<string>()
45+
files
46+
|> Array.map (fun f ->
47+
let fsiInfo =
48+
match f.AST with
49+
| ParsedInput.SigFile _ ->
50+
NotBacked
51+
| ParsedInput.ImplFile _ ->
52+
let fsiName = System.IO.Path.ChangeExtension(f.Name, "fsi")
53+
match seenSigFiles.Contains fsiName with
54+
| true -> FsiBacked
55+
| false -> NotBacked
56+
{
57+
File = f
58+
FsiInfo = fsiInfo
59+
}
60+
)
61+
62+
type EdgeTrimmer = File -> File -> bool
63+
64+
type FileData =
65+
{
66+
ModuleRefs : ModuleRef[]
67+
Structure : FileStructure
68+
}
69+
70+
let gatherFileData (file : File) =
71+
72+
let calcFileGraph (files : SourceFiles) : FileGraph =
73+
let fsFsiTrimmer =
74+
let files =
75+
gatherBackingInfo files
76+
... to dict
77+
fun file dep -> not files[dep].FsiInfo.IsFsiBacked
78+
let
79+
80+
/// Used for processing
81+
type NodeInfo<'Item> =
82+
{
83+
Item : 'Item
84+
Deps : 'Item[]
85+
TransitiveDeps : 'Item[]
86+
Dependants : 'Item[]
87+
ProcessedDepsCount : int
88+
}
89+
type Node<'Item, 'State, 'Result> =
90+
{
91+
Info : NodeInfo<'Item>
92+
Result : ('State * 'Result) option
93+
}
94+
95+
// Do we need to suppress some error logging if we
96+
// apply the same partial results multiple times?
97+
// Maybe we can enable logging only for the final fold
98+
let combineResults
99+
(deps : Node<_,_,_>[])
100+
(transitiveDeps : Node<_,_,_>[])
101+
(folder : 'State -> 'Result -> 'State)
102+
: 'State
103+
=
104+
let biggestDep =
105+
let sizeMetric node =
106+
// Could also use eg. total file size/AST size
107+
node.Info.TransitiveDeps.Length
108+
deps
109+
|> Array.maxBy sizeMetrix
110+
let firstState = snd biggestDep.Result
111+
// Perf: Keep transDeps in a HashSet from the start
112+
let included = HashSet(firstState.Info.TransitiveDeps)
113+
let toAdd =
114+
transitiveDeps
115+
|> Array.filter (fun dep -> included.Add dep)
116+
let state = Array.fold folder firstState toAdd
117+
state
118+
119+
let processInParallel
120+
(firstItems : 'Item[])
121+
(work : 'Item -> 'Item[])
122+
(parallelism : int)
123+
(stop : int -> bool)
124+
(ct)
125+
: unit async
126+
=
127+
let bc = BlockingCollection(firstItems)
128+
let mutable processedCount = 0
129+
let processItem item =
130+
let toSchedule = work item
131+
lock processedCount (fun () -> processedCount++)
132+
toSchedule |> Array.iter bc.Add
133+
// Could avoid workers with some semaphores
134+
let workerWork () =
135+
for node in bc.Get... do
136+
if not ct.Cancelled then // improve
137+
processNode node
138+
if stop () then
139+
bc.CompleteAdding() // avoid doing multiple times?
140+
141+
Array.Parallel.map
142+
parallelism workerWork // use cancellation
143+
144+
let processGraph
145+
(graph : FileGraph)
146+
(doWork : 'Item -> 'State -> 'Result * 'State)
147+
(folder : 'State -> 'Result -> 'State)
148+
(parallelism : int)
149+
: 'State
150+
=
151+
let transitiveDeps = graph |> calcTransitiveGraph
152+
let dependants = graph |> reverseGraph
153+
let nodes = graph.Keys |> Seq.map ...
154+
let leaves = nodes |> Seq.filter ...
155+
let work
156+
(node : Node<'Item, 'State, 'Result>)
157+
: Node<'Item, 'State, 'Result>[]
158+
=
159+
let inputState = combineResults node.Deps node.TransitiveDeps folder
160+
let res = doWork node.Info.Item
161+
node.Result <- res
162+
let unblocked =
163+
node.Info.Dependants
164+
|> Array.filter (fun x ->
165+
let pdc =
166+
lock x (fun () ->
167+
x.Info.ProcessedDepsCount++
168+
x.Info.PrcessedDepsCount
169+
)
170+
pdc = node.Info.Deps.Length
171+
)
172+
|> Array.map (fun x -> nodes[x])
173+
unblocked
174+
175+
processInParallel
176+
leaves
177+
work
178+
parallelism
179+
(fun processedCount -> processedCount = nodes.Length)
180+
181+
let state = combineResults nodes nodes addCheckResultsToTcState
182+
state
183+
184+
type TcState
185+
type SingleResult
186+
187+
let typeCheckFile (file : File) (state : TcState)
188+
: SingleResult * TcState
189+
=
190+
...
191+
192+
let typeCheckGraph (graph : FileGraph) : TcState =
193+
let parallelism = 4 // cpu count?
194+
let state =
195+
processGraph
196+
graph
197+
typeCheckFile
198+
addCheckResultsToTcState
199+
parallelism
200+
state
201+
202+
let typeCheck (files : SourceFiles) : TcState =
203+
let graph = calcFileGraph files
204+
let state = typeCheckGraph graph
205+
state

0 commit comments

Comments
 (0)