11open System
22open System.IO
3- open BenchmarkDotNet.Attributes
4- open BenchmarkDotNet.Running
3+ open System.Text
54open Microsoft.FSharp .Compiler .ErrorLogger
65open Microsoft.FSharp .Compiler .SourceCodeServices
7- open System.Text
6+ open Microsoft.FSharp .Compiler .Text
7+ open Microsoft.FSharp .Compiler .AbstractIL
8+ open Microsoft.FSharp .Compiler .AbstractIL .IL
9+ open Microsoft.FSharp .Compiler .AbstractIL .ILBinaryReader
10+ open Microsoft.CodeAnalysis .Text
11+ open BenchmarkDotNet.Attributes
12+ open BenchmarkDotNet.Running
13+
14+ module private SourceText =
15+
16+ open System.Runtime .CompilerServices
17+
18+ let weakTable = ConditionalWeakTable< SourceText, ISourceText>()
19+
20+ let create ( sourceText : SourceText ) =
21+
22+ let sourceText =
23+ { new ISourceText with
24+
25+ member __.Item with get index = sourceText.[ index]
26+
27+ member __.GetLineString ( lineIndex ) =
28+ sourceText.Lines.[ lineIndex]. ToString()
29+
30+ member __.GetLineCount () =
31+ sourceText.Lines.Count
32+
33+ member __.GetLastCharacterPosition () =
34+ if sourceText.Lines.Count > 0 then
35+ ( sourceText.Lines.Count, sourceText.Lines.[ sourceText.Lines.Count - 1 ]. Span.Length)
36+ else
37+ ( 0 , 0 )
38+
39+ member __.GetSubTextString ( start , length ) =
40+ sourceText.GetSubText( TextSpan( start, length)). ToString()
41+
42+ member __.SubTextEquals ( target , startIndex ) =
43+ if startIndex < 0 || startIndex >= sourceText.Length then
44+ raise ( ArgumentOutOfRangeException( " startIndex" ))
45+
46+ if String.IsNullOrEmpty( target) then
47+ raise ( ArgumentException( " Target is null or empty." , " target" ))
48+
49+ let lastIndex = startIndex + target.Length
50+ if lastIndex <= startIndex || lastIndex >= sourceText.Length then
51+ raise ( ArgumentException( " Target is too big." , " target" ))
52+
53+ let mutable finished = false
54+ let mutable didEqual = true
55+ let mutable i = 0
56+ while not finished && i < target.Length do
57+ if target.[ i] <> sourceText.[ startIndex + i] then
58+ didEqual <- false
59+ finished <- true // bail out early
60+ else
61+ i <- i + 1
62+
63+ didEqual
64+
65+ member __.ContentEquals ( sourceText ) =
66+ match sourceText with
67+ | : ? SourceText as sourceText -> sourceText .ContentEquals ( sourceText )
68+ | _ -> false
869
9- [<ClrJob( baseline = true ) >]
10- type CompilerServiceParsing () =
70+ member __.Length = sourceText.Length
71+
72+ member __.CopyTo ( sourceIndex , destination , destinationIndex , count ) =
73+ sourceText.CopyTo( sourceIndex, destination, destinationIndex, count)
74+ }
75+
76+ sourceText
77+
78+ type SourceText with
79+
80+ member this.ToFSharpSourceText () =
81+ SourceText.weakTable.GetValue( this, Runtime.CompilerServices.ConditionalWeakTable<_,_>. CreateValueCallback( SourceText.create))
82+
83+ [<MemoryDiagnoser>]
84+ type CompilerService () =
1185
1286 let mutable checkerOpt = None
1387
@@ -24,6 +98,17 @@ type CompilerServiceParsing() =
2498 IsExe = false
2599 }
26100
101+ let mutable assembliesOpt = None
102+
103+ let readerOptions =
104+ {
105+ pdbDirPath = None
106+ ilGlobals = mkILGlobals ILScopeRef.Local
107+ reduceMemoryUsage = ReduceMemoryFlag.No
108+ metadataOnly = MetadataOnlyFlag.Yes
109+ tryGetMetadataSnapshot = fun _ -> None
110+ }
111+
27112 [<GlobalSetup>]
28113 member __.Setup () =
29114 match checkerOpt with
@@ -32,29 +117,88 @@ type CompilerServiceParsing() =
32117
33118 match sourceOpt with
34119 | None ->
35- let source = File.ReadAllText( """ ..\..\..\..\..\src\fsharp\TypeChecker.fs""" )
36- sourceOpt <- Some( source)
120+ sourceOpt <- Some <| SourceText.From( File.OpenRead( """ ..\..\..\..\..\src\fsharp\TypeChecker.fs""" ), Encoding.Default, SourceHashAlgorithm.Sha1, true )
121+ | _ -> ()
122+
123+ match assembliesOpt with
124+ | None ->
125+ assembliesOpt <-
126+ System.AppDomain.CurrentDomain.GetAssemblies()
127+ |> Array.map ( fun x -> ( x.Location))
128+ |> Some
37129 | _ -> ()
38130
39- [<IterationSetup>]
131+ [<IterationSetup( Target = " Parsing " ) >]
40132 member __.ParsingSetup () =
41133 match checkerOpt with
42134 | None -> failwith " no checker"
43135 | Some( checker) ->
44136 checker.InvalidateAll()
45137 checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients()
46- checker.ParseFile( " dummy.fs" , " dummy" , parsingOptions) |> Async.RunSynchronously |> ignore
138+ checker.ParseFile( " dummy.fs" , SourceText.ofString " dummy" , parsingOptions) |> Async.RunSynchronously |> ignore
47139
48140 [<Benchmark>]
49141 member __.Parsing () =
50142 match checkerOpt, sourceOpt with
51143 | None, _ -> failwith " no checker"
52144 | _, None -> failwith " no source"
53145 | Some( checker), Some( source) ->
54- let results = checker.ParseFile( " TypeChecker.fs" , source, parsingOptions) |> Async.RunSynchronously
146+ let results = checker.ParseFile( " TypeChecker.fs" , source.ToFSharpSourceText () , parsingOptions) |> Async.RunSynchronously
55147 if results.ParseHadErrors then failwithf " parse had errors: %A " results.Errors
56148
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()
154+
155+ [<Benchmark>]
156+ member __.ILReading () =
157+ match assembliesOpt with
158+ | None -> failwith " no assemblies"
159+ | Some( assemblies) ->
160+ // We try to read most of everything in the assembly that matter, mainly types with their properties, methods, and fields.
161+ // CustomAttrs and SecurityDecls are lazy until you call them, so we call them here for benchmarking.
162+ assemblies
163+ |> Array.iter ( fun ( fileName ) ->
164+ let reader = OpenILModuleReader fileName readerOptions
165+
166+ let ilModuleDef = reader.ILModuleDef
167+
168+ let ilAssemblyManifest = ilModuleDef.Manifest.Value
169+
170+ ilAssemblyManifest.CustomAttrs |> ignore
171+ ilAssemblyManifest.SecurityDecls |> ignore
172+ ilAssemblyManifest.ExportedTypes.AsList
173+ |> List.iter ( fun x ->
174+ x.CustomAttrs |> ignore
175+ )
176+
177+ ilModuleDef.CustomAttrs |> ignore
178+ ilModuleDef.TypeDefs.AsArray
179+ |> Array.iter ( fun ilTypeDef ->
180+ ilTypeDef.CustomAttrs |> ignore
181+ ilTypeDef.SecurityDecls |> ignore
182+
183+ ilTypeDef.Methods.AsArray
184+ |> Array.iter ( fun ilMethodDef ->
185+ ilMethodDef.CustomAttrs |> ignore
186+ ilMethodDef.SecurityDecls |> ignore
187+ )
188+
189+ ilTypeDef.Fields.AsList
190+ |> List.iter ( fun ilFieldDef ->
191+ ilFieldDef.CustomAttrs |> ignore
192+ )
193+
194+ ilTypeDef.Properties.AsList
195+ |> List.iter ( fun ilPropertyDef ->
196+ ilPropertyDef.CustomAttrs |> ignore
197+ )
198+ )
199+ )
200+
57201[<EntryPoint>]
58202let main argv =
59- let _ = BenchmarkRunner.Run< CompilerServiceParsing >()
203+ let _ = BenchmarkRunner.Run< CompilerService >()
60204 0
0 commit comments