1- #I @" ../../packages/build/FAKE/tools"
1+ #r " ../../packages/build/NEST/lib/net45/Nest.dll"
2+ #r " ../../packages/build/Elasticsearch.Net/lib/net45/Elasticsearch.Net.dll"
3+ #r " ../../packages/build/Newtonsoft.Json/lib/net45/Newtonsoft.Json.dll"
4+ #r " ../../packages/build/FSharp.Data/lib/net40/FSharp.Data.dll"
5+ #I @" ../../packages/build/FAKE/tools"
26#r @" FakeLib.dll"
7+ #nowarn " 0044" //TODO sort out FAKE 5
8+
39open Fake
410
511#load @" Paths.fsx"
@@ -10,26 +16,131 @@ open System.Linq
1016open System.Diagnostics
1117open Paths
1218
19+ open FSharp.Data
20+
21+ open Nest
22+ open Elasticsearch.Net
23+ open Newtonsoft.Json
24+ open Git.Branches
25+
1326module Benchmarker =
14- let private testsProjectDirectory = Path.GetFullPath( Paths.Source( " Tests" ))
15- let private benchmarkOutput = Path.GetFullPath( Paths.Output( " benchmarks" )) |> directoryInfo
1627
17- let private copyToOutput file = CopyFile benchmarkOutput.FullName file
28+ let pipelineName = " benchmark-pipeline"
29+ let indexName = IndexName.op_ Implicit( " benchmark-reports" )
30+ let typeName = TypeName.op_ Implicit( " benchmarkreport" )
31+
32+ type Memory ( gen0Collections : int , gen1Collections : int , gen2Collections : int , totalOperations : int64 , bytesAllocatedPerOperation : int64 ) =
33+ member val Gen0Collections = gen0Collections with get, set
34+ member val Gen1Collections = gen1Collections with get, set
35+ member val Gen2Collections = gen2Collections with get, set
36+ member val TotalOperations = totalOperations with get, set
37+ member val BytesAllocatedPerOperation = bytesAllocatedPerOperation with get, set
38+
39+ type ChronometerFrequency ( hertz : double ) =
40+ member val Hertz = hertz with get, set
41+
42+ type HostEnvironmentInfo ( benchmarkDotNetCaption : string , benchmarkDotNetVersion : string , osVersion : string , processorName : string ,
43+ processorCount: int, runtimeVersion: string, architecture: string, hasAttachedDebugger: bool, hasRyuJit: bool,
44+ configuration: string, jitModules: string, dotnetCliVersion: string, chronometerFrequency: ChronometerFrequency,
45+ hardwareTimerKind: string) =
46+ member val BenchmarkDotNetCaption = benchmarkDotNetCaption with get, set
47+ member val BenchmarkDotNetVersion = benchmarkDotNetVersion with get, set
48+ member val OsVersion = osVersion with get, set
49+ member val ProcessorName = processorName with get, set
50+ member val ProcessorCount = processorCount with get, set
51+ member val RuntimeVersion = runtimeVersion with get, set
52+ member val Architecture = architecture with get, set
53+ member val HasAttachedDebugger = hasAttachedDebugger with get, set
54+ member val HasRyuJit = hasRyuJit with get, set
55+ member val Configuration = configuration with get, set
56+ member val JitModules = jitModules with get, set
57+ member val DotNetCliVersion = dotnetCliVersion with get, set
58+ member val ChronometerFrequency = chronometerFrequency with get, set
59+ member val HardwareTimerKind = hardwareTimerKind with get, set
60+
61+ type ConfidenceInterval ( n : int , mean : double , standardError : double , level : int , margin : double , lower : double , upper : double ) =
62+ member val N = n with get, set
63+ member val Mean = mean with get, set
64+ member val StandardError = standardError with get, set
65+ member val Level = level with get, set
66+ member val Margin = margin with get, set
67+ member val Lower = lower with get, set
68+ member val Upper = upper with get, set
69+
70+ type Percentiles ( p0 : double , p25 : double , p50 : double , p67 : double , p80 : double , p85 : double , p90 : double , p95 : double , p100 : double ) =
71+ member val P0 = p0 with get, set
72+ member val P25 = p25 with get, set
73+ member val P50 = p50 with get, set
74+ member val P67 = p67 with get, set
75+ member val P80 = p80 with get, set
76+ member val P85 = p85 with get, set
77+ member val P90 = p90 with get, set
78+ member val P95 = p95 with get, set
79+ member val P100 = p100 with get, set
80+
81+ type Statistics ( n : int , min : double , lowerFence : double , q1 : double , median : double , mean : double , q3 : double , upperFence : double , max : double ,
82+ interquartileRange: double, outliers: double list, standardError: double, variance: double, standardDeviation: double,
83+ skewness: double, kurtosis: double, confidenceInterval: ConfidenceInterval, percentiles: Percentiles) =
84+ member val N = n with get, set
85+ member val Min = min with get, set
86+ member val LowerFence = lowerFence with get, set
87+ member val Q1 = q1 with get, set
88+ member val Median = median with get, set
89+ member val Mean = mean with get, set
90+ member val Q3 = q3 with get, set
91+ member val UpperFence = upperFence with get, set
92+ member val Max = max with get, set
93+ member val InterquartileRange = interquartileRange with get, set
94+ member val Outliers = outliers with get, set
95+ member val StandardError = standardError with get, set
96+ member val Variance = variance with get, set
97+ member val StandardDeviation = standardDeviation with get, set
98+ member val Skewness = skewness with get, set
99+ member val Kurtosis = kurtosis with get, set
100+ member val ConfidenceInterval = confidenceInterval with get, set
101+ member val Percentiles = percentiles with get, set
102+
103+ type Benchmark ( displayInfo : string , namespyce : string , tipe : string , method : string , methodTitle : string , parameters : string ,
104+ statistics: Statistics, memory: Memory) =
105+ member val DisplayInfo = displayInfo with get, set
106+ member val Namespace = namespyce with get, set
107+ member val Type = tipe with get, set
108+ member val Method = method with get, set
109+ member val MethodTitle = methodTitle with get, set
110+ member val Parameters = parameters with get, set
111+ member val Statistics = statistics with get, set
112+ member val Memory = memory with get, set
113+
114+ type BenchmarkReport ( title : string , totalTime : TimeSpan , date : DateTime , commit : string , host : HostEnvironmentInfo , benchmarks : Benchmark list ) =
115+ member val Title = title with get, set
116+ member val TotalTime = totalTime with get, set
117+ member val Date = date with get, set
118+ member val Commit = commit with get, set
119+ member val HostEnvironmentInfo = host with get, set
120+ member val Benchmarks = benchmarks with get, set
121+
122+ let private testsProjectDirectory = Path.GetFullPath( Paths.Source( " Tests" ))
123+ let private benchmarkOutput = Path.GetFullPath( Paths.Output( " benchmarks" )) |> directoryInfo
124+ let private copyToOutput file = CopyFile benchmarkOutput.FullName file
125+
126+ let Run ( runInteractive : bool ) =
18127
19- let Run () =
20128 ensureDirExists benchmarkOutput
129+
21130 let projectJson = testsProjectDirectory @@ " project.json"
131+
22132 // running benchmarks can timeout so clean up any generated benchmark files
23133 try
24- DotNetCli.Restore( fun p ->
25- { p with
26- Project = projectJson
27- })
28-
29- DotNetCli.RunCommand( fun p ->
30- { p with
31- WorkingDir = testsProjectDirectory
32- }) " run Benchmark"
134+ if runInteractive then
135+ DotNetCli.RunCommand( fun p ->
136+ { p with
137+ WorkingDir = testsProjectDirectory
138+ }) " run -f net46 -c Release Benchmark"
139+ else
140+ DotNetCli.RunCommand( fun p ->
141+ { p with
142+ WorkingDir = testsProjectDirectory
143+ }) " run -f net46 -c Release Benchmark non-interactive"
33144 finally
34145 let benchmarkOutputFiles =
35146 let output = combinePaths testsProjectDirectory " BenchmarkDotNet.Artifacts"
@@ -38,3 +149,95 @@ module Benchmarker =
38149
39150 for file in benchmarkOutputFiles do copyToOutput file
40151 DeleteFiles benchmarkOutputFiles
152+
153+ let IndexResult ( client : ElasticClient , file : string , date : DateTime , commit : string , indexName , typeName ) =
154+
155+ trace ( sprintf " Indexing report %s into Elasticsearch" file)
156+
157+ let document = JsonConvert.DeserializeObject< BenchmarkReport>( File.ReadAllText( file))
158+ document.Date <- date
159+ document.Commit <- commit
160+
161+ let indexRequest = new IndexRequest< BenchmarkReport>( indexName, typeName)
162+ indexRequest.Document <- document
163+ indexRequest.Pipeline <- pipelineName
164+
165+ let indexResponse = client.Index( indexRequest)
166+
167+ if indexResponse.IsValid = false then
168+ raise ( Exception( " Unable to index report into Elasticsearch: " + indexResponse.ServerError.Error.ToString()))
169+
170+ let IndexResults ( url , username , password ) =
171+ if ( String.IsNullOrEmpty url = false ) then
172+ trace " Indexing benchmark reports into Elasticsearch"
173+
174+ let date = DateTime.UtcNow
175+ let commit = getSHA1 " ." " HEAD"
176+
177+ let benchmarkJsonFiles =
178+ Directory.EnumerateFiles( benchmarkOutput.FullName, " *-custom.json" , SearchOption.AllDirectories)
179+ |> Seq.toList
180+
181+ let uri = new Uri( url)
182+ let connectionSettings = new ConnectionSettings( uri);
183+
184+ if ( String.IsNullOrEmpty username = false && String.IsNullOrEmpty password = false ) then
185+ connectionSettings.BasicAuthentication( username, password) |> ignore
186+
187+ let client = new ElasticClient( connectionSettings)
188+
189+ let indexTemplateExists = client.IndexTemplateExists( Name.op_ Implicit( " benchmarks" )) .Exists
190+
191+ if indexTemplateExists |> not then
192+
193+ let typeMapping = new TypeMappingDescriptor< BenchmarkReport>()
194+ typeMapping.AutoMap() |> ignore
195+ typeMapping.Properties( fun p ->
196+ p.Nested< Benchmark>( fun n ->
197+ n.AutoMap() .Name( PropertyName.op_ Implicit( " benchmarks" )) :> INestedProperty
198+ ) :> IPromise< IProperties>
199+ ) |> ignore
200+
201+ let mappings = new Mappings()
202+ mappings.Add( typeName, typeMapping :> ITypeMapping)
203+
204+ let indexSettings = new IndexSettings()
205+ indexSettings.NumberOfShards <- Nullable 1
206+
207+ let putIndexTemplateRequest = new PutIndexTemplateRequest( Name.op_ Implicit( " benchmarks" ))
208+ putIndexTemplateRequest.Template <- " benchmark-reports-*"
209+ putIndexTemplateRequest.Mappings <- mappings
210+ putIndexTemplateRequest.Settings <- indexSettings
211+
212+ let putIndexTemplateResponse = client.PutIndexTemplate( putIndexTemplateRequest)
213+
214+ if putIndexTemplateResponse.IsValid = false then
215+ raise ( Exception( " Unable to create index template into Elasticsearch" ))
216+
217+ let processor = new GrokProcessor();
218+ processor.Field <- new Field( " _ingest._value.displayInfo" )
219+ processor.Patterns <- [ " %{WORD:_ingest._value.class}.%{DATA:_ingest._value.method}: Job-%{WORD:_ingest._value.jobName}\\ (Jit=%{WORD:_ingest._value.jit}, Runtime=%{WORD:_ingest._value.clr}, LaunchCount=%{NUMBER:_ingest._value.launchCount}, RunStrategy=%{WORD:_ingest._value.runStrategy}, TargetCount=%{NUMBER:_ingest._value.targetCount}, UnrollFactor=%{NUMBER:_ingest._value.unrollFactor}, WarmupCount=%{NUMBER:_ingest._value.warmupCount}\\ )" ]
220+
221+ let forEachProcessor = new ForeachProcessor()
222+ forEachProcessor.Field <- new Field( " benchmarks" )
223+ forEachProcessor.Processor <- processor
224+
225+ let dateIndexProcessor = new DateIndexNameProcessor();
226+ dateIndexProcessor.Field <- new Field( " date" )
227+ dateIndexProcessor.IndexNamePrefix <- " benchmark-reports-"
228+ dateIndexProcessor.DateRounding <- DateRounding.Month
229+ dateIndexProcessor.DateFormats <- [ " yyyy-MM-dd'T'HH:mm:ss.SSSSSSSZ" ]
230+
231+ let request = new PutPipelineRequest( Id.op_ Implicit( pipelineName))
232+ request.Description <- " Benchmark settings pipeline"
233+ request.Processors <- [ dateIndexProcessor; forEachProcessor]
234+
235+ let createPipeline = client.PutPipeline( request)
236+
237+ if createPipeline.IsValid = false then
238+ raise ( Exception( " Unable to create pipeline" ))
239+
240+ for file in benchmarkJsonFiles
241+ do IndexResult ( client, file, date, commit, indexName, typeName)
242+
243+ trace " Indexed benchmark reports into Elasticsearch"
0 commit comments