Skip to content

Commit b818aa0

Browse files
TIHanKevinRansom
authored andcommitted
Fixed benchmark solution and added project type check test (#6566)
* Trying to build benchmarks * Benchmark testing 100 referenced projects in the compiler service * Fixed project reference bit * Change stamp * Fixed build
1 parent 983e162 commit b818aa0

3 files changed

Lines changed: 134 additions & 23 deletions

File tree

benchmarks/CompilerServiceBenchmarks/Program.fs

Lines changed: 134 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ open FSharp.Compiler.Text
77
open FSharp.Compiler.AbstractIL
88
open FSharp.Compiler.AbstractIL.IL
99
open FSharp.Compiler.AbstractIL.ILBinaryReader
10-
open CodeAnalysis.Text
10+
open Microsoft.CodeAnalysis.Text
1111
open BenchmarkDotNet.Attributes
1212
open BenchmarkDotNet.Running
1313

@@ -80,6 +80,49 @@ type SourceText with
8080
member this.ToFSharpSourceText() =
8181
SourceText.weakTable.GetValue(this, Runtime.CompilerServices.ConditionalWeakTable<_,_>.CreateValueCallback(SourceText.create))
8282

83+
[<AutoOpen>]
84+
module Helpers =
85+
86+
let createProject name referencedProjects =
87+
let tmpPath = Path.GetTempPath()
88+
let file = Path.Combine(tmpPath, Path.ChangeExtension(name, ".fs"))
89+
{
90+
ProjectFileName = Path.Combine(tmpPath, Path.ChangeExtension(name, ".dll"))
91+
ProjectId = None
92+
SourceFiles = [|file|]
93+
OtherOptions =
94+
Array.append [|"--optimize+"; "--target:library" |] (referencedProjects |> Array.ofList |> Array.map (fun x -> "-r:" + x.ProjectFileName))
95+
ReferencedProjects =
96+
referencedProjects
97+
|> List.map (fun x -> (x.ProjectFileName, x))
98+
|> Array.ofList
99+
IsIncompleteTypeCheckEnvironment = false
100+
UseScriptResolutionRules = false
101+
LoadTime = DateTime()
102+
UnresolvedReferences = None
103+
OriginalLoadReferences = []
104+
ExtraProjectInfo = None
105+
Stamp = Some 0L (* set the stamp to 0L on each run so we don't evaluate the whole project again *)
106+
}
107+
108+
let generateSourceCode moduleName =
109+
sprintf """
110+
module Benchmark.%s
111+
112+
type %s =
113+
114+
val X : int
115+
116+
val Y : int
117+
118+
val Z : int
119+
120+
let function%s (x: %s) =
121+
let x = 1
122+
let y = 2
123+
let z = x + y
124+
z""" moduleName moduleName moduleName moduleName
125+
83126
[<MemoryDiagnoser>]
84127
type CompilerService() =
85128

@@ -112,7 +155,7 @@ type CompilerService() =
112155
[<GlobalSetup>]
113156
member __.Setup() =
114157
match checkerOpt with
115-
| None -> checkerOpt <- Some(FSharpChecker.Create())
158+
| None -> checkerOpt <- Some(FSharpChecker.Create(projectCacheSize = 200))
116159
| _ -> ()
117160

118161
match sourceOpt with
@@ -127,30 +170,25 @@ type CompilerService() =
127170
|> Array.map (fun x -> (x.Location))
128171
|> Some
129172
| _ -> ()
130-
131-
[<IterationSetup(Target = "Parsing")>]
132-
member __.ParsingSetup() =
133-
match checkerOpt with
134-
| None -> failwith "no checker"
135-
| Some(checker) ->
136-
checker.InvalidateAll()
137-
checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients()
138-
checker.ParseFile("dummy.fs", SourceText.ofString "dummy", parsingOptions) |> Async.RunSynchronously |> ignore
139173

140174
[<Benchmark>]
141-
member __.Parsing() =
175+
member __.ParsingTypeCheckerFs() =
142176
match checkerOpt, sourceOpt with
143177
| None, _ -> failwith "no checker"
144178
| _, None -> failwith "no source"
145179
| Some(checker), Some(source) ->
146180
let results = checker.ParseFile("TypeChecker.fs", source.ToFSharpSourceText(), parsingOptions) |> Async.RunSynchronously
147181
if results.ParseHadErrors then failwithf "parse had errors: %A" results.Errors
148182

149-
[<IterationSetup(Target = "ILReading")>]
150-
member __.ILReadingSetup() =
151-
// With caching, performance increases an order of magnitude when re-reading an ILModuleReader.
152-
// Clear it for benchmarking.
153-
ClearAllILModuleReaderCache()
183+
[<IterationCleanup(Target = "ParsingTypeCheckerFs")>]
184+
member __.ParsingTypeCheckerFsSetup() =
185+
match checkerOpt with
186+
| None -> failwith "no checker"
187+
| Some(checker) ->
188+
checker.InvalidateAll()
189+
checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients()
190+
checker.ParseFile("dummy.fs", SourceText.ofString "dummy", parsingOptions) |> Async.RunSynchronously |> ignore
191+
ClearAllILModuleReaderCache()
154192

155193
[<Benchmark>]
156194
member __.ILReading() =
@@ -198,6 +236,85 @@ type CompilerService() =
198236
)
199237
)
200238

239+
[<IterationCleanup(Target = "ILReading")>]
240+
member __.ILReadingSetup() =
241+
// With caching, performance increases an order of magnitude when re-reading an ILModuleReader.
242+
// Clear it for benchmarking.
243+
ClearAllILModuleReaderCache()
244+
245+
member val TypeCheckFileWith100ReferencedProjectsOptions =
246+
createProject "MainProject"
247+
[ for i = 1 to 100 do
248+
yield
249+
createProject ("ReferencedProject" + string i) []
250+
]
251+
252+
member this.TypeCheckFileWith100ReferencedProjectsRun() =
253+
let options = this.TypeCheckFileWith100ReferencedProjectsOptions
254+
let file = options.SourceFiles.[0]
255+
256+
match checkerOpt with
257+
| None -> failwith "no checker"
258+
| Some checker ->
259+
let parseResult, checkResult =
260+
checker.ParseAndCheckFileInProject(file, 0, SourceText.ofString (File.ReadAllText(file)), options)
261+
|> Async.RunSynchronously
262+
263+
if parseResult.Errors.Length > 0 then
264+
failwithf "%A" parseResult.Errors
265+
266+
match checkResult with
267+
| FSharpCheckFileAnswer.Aborted -> failwith "aborted"
268+
| FSharpCheckFileAnswer.Succeeded checkFileResult ->
269+
270+
if checkFileResult.Errors.Length > 0 then
271+
failwithf "%A" checkFileResult.Errors
272+
273+
[<IterationSetup(Target = "TypeCheckFileWith100ReferencedProjects")>]
274+
member this.TypeCheckFileWith100ReferencedProjectsSetup() =
275+
this.TypeCheckFileWith100ReferencedProjectsOptions.SourceFiles
276+
|> Seq.iter (fun file ->
277+
File.WriteAllText(file, generateSourceCode (Path.GetFileNameWithoutExtension(file)))
278+
)
279+
280+
this.TypeCheckFileWith100ReferencedProjectsOptions.ReferencedProjects
281+
|> Seq.iter (fun (_, referencedProjectOptions) ->
282+
referencedProjectOptions.SourceFiles
283+
|> Seq.iter (fun file ->
284+
File.WriteAllText(file, generateSourceCode (Path.GetFileNameWithoutExtension(file)))
285+
)
286+
)
287+
288+
this.TypeCheckFileWith100ReferencedProjectsRun()
289+
290+
[<Benchmark>]
291+
member this.TypeCheckFileWith100ReferencedProjects() =
292+
// Because the checker's projectcachesize is set to 200, this should be fast.
293+
// If set to 3, it will be almost as slow as re-evaluating all project and it's projects references on setup; this could be a bug or not what we want.
294+
this.TypeCheckFileWith100ReferencedProjectsRun()
295+
296+
[<IterationCleanup(Target = "TypeCheckFileWith100ReferencedProjects")>]
297+
member this.TypeCheckFileWith100ReferencedProjectsCleanup() =
298+
this.TypeCheckFileWith100ReferencedProjectsOptions.SourceFiles
299+
|> Seq.iter (fun file ->
300+
try File.Delete(file) with | _ -> ()
301+
)
302+
303+
this.TypeCheckFileWith100ReferencedProjectsOptions.ReferencedProjects
304+
|> Seq.iter (fun (_, referencedProjectOptions) ->
305+
referencedProjectOptions.SourceFiles
306+
|> Seq.iter (fun file ->
307+
try File.Delete(file) with | _ -> ()
308+
)
309+
)
310+
311+
match checkerOpt with
312+
| None -> failwith "no checker"
313+
| Some(checker) ->
314+
checker.InvalidateAll()
315+
checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients()
316+
ClearAllILModuleReaderCache()
317+
201318
[<EntryPoint>]
202319
let main argv =
203320
let _ = BenchmarkRunner.Run<CompilerService>()

benchmarks/Directory.Build.props

Lines changed: 0 additions & 3 deletions
This file was deleted.

benchmarks/Directory.Build.targets

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)