1+ using System . Diagnostics ;
2+ using System . Runtime . InteropServices ;
3+ using BenchmarkDotNet . Attributes ;
4+ using BenchmarkDotNet . Configs ;
5+ using BenchmarkDotNet . Jobs ;
6+ using BenchmarkDotNet . Running ;
7+
8+ var artifactsDir = Path . GetFullPath ( Path . Combine (
9+ AppContext . BaseDirectory , ".." , ".." , ".." , "BenchmarkDotNet.Artifacts" ) ) ;
10+
11+ var config = DefaultConfig . Instance
12+ . WithArtifactsPath ( artifactsDir ) ;
13+
14+ BenchmarkRunner . Run < GoBenchmarks > ( config ) ;
15+
16+ public record Sample ( string Name , string App )
17+ {
18+ public override string ToString ( ) => Name ;
19+ }
20+
21+ [ Config ( typeof ( BenchmarkConfig ) ) ]
22+ public class GoBenchmarks
23+ {
24+ class BenchmarkConfig : ManualConfig
25+ {
26+ public BenchmarkConfig ( )
27+ {
28+ // Use fewer iterations because these are high-level process invocations (hundreds of ms to seconds)
29+ AddJob ( Job . Default
30+ . WithLaunchCount ( 1 )
31+ . WithWarmupCount ( 1 )
32+ . WithIterationCount ( 3 )
33+ . WithInvocationCount ( 1 )
34+ . WithUnrollFactor ( 1 ) ) ;
35+ }
36+ }
37+
38+ string _repoRoot = null ! ;
39+ const string _dotnet = "dotnet" ;
40+
41+ [ GlobalSetup ]
42+ public void GlobalSetup ( )
43+ {
44+ _repoRoot = FindRepoRoot ( ) ;
45+
46+ // Require that `dotnet go` works (the tool must be installed in a way that `dotnet go --version` succeeds).
47+ // This avoids any dnx overhead in the measurements.
48+ var ( exit , stdout , stderr ) = RunProcessCapture ( _dotnet , [ "go" , "--version" ] ) ;
49+ if ( exit != 0 )
50+ {
51+ throw new InvalidOperationException (
52+ $ "`dotnet go --version` must succeed (exit { exit } ).\n " +
53+ $ "stdout: { stdout } \n stderr: { stderr } \n " +
54+ "Install the 'go' tool (e.g. from local package) so that `dotnet go` is available." ) ;
55+ }
56+
57+ Console . WriteLine ( $ "[setup] dotnet go --version: { stdout . Trim ( ) } ") ;
58+ Console . WriteLine ( $ "[setup] samples root: { _repoRoot } ") ;
59+ }
60+
61+ static string FindRepoRoot ( )
62+ {
63+ var dir = AppContext . BaseDirectory ;
64+ while ( ! string . IsNullOrEmpty ( dir ) && dir . Length > 3 )
65+ {
66+ if ( File . Exists ( Path . Combine ( dir , "go.slnx" ) ) )
67+ return dir ;
68+ // Also accept if we see the samples folder as we walk up
69+ if ( Directory . Exists ( Path . Combine ( dir , "samples" ) ) && File . Exists ( Path . Combine ( dir , "readme.md" ) ) )
70+ return dir ;
71+ dir = Path . GetDirectoryName ( dir ) ;
72+ }
73+
74+ // Fallback: assume running from inside the repo tree
75+ var fallback = Path . GetFullPath ( Path . Combine ( AppContext . BaseDirectory , ".." , ".." , ".." , ".." , ".." ) ) ;
76+ return fallback ;
77+ }
78+
79+ [ ParamsSource ( nameof ( GetSamples ) ) ]
80+ public Sample Sample { get ; set ; } = default ! ;
81+
82+ public IEnumerable < Sample > GetSamples ( )
83+ {
84+ var root = _repoRoot ?? FindRepoRoot ( ) ;
85+ var samplesDir = Path . Combine ( root , "samples" ) ;
86+ if ( ! Directory . Exists ( samplesDir ) )
87+ yield break ;
88+
89+ foreach ( var full in Directory . GetFiles ( samplesDir , "app.cs" , SearchOption . AllDirectories ) . OrderBy ( p => p ) )
90+ {
91+ var name = Path . GetFileName ( Path . GetDirectoryName ( full ) ) ! ;
92+ yield return new Sample ( name , full ) ;
93+ }
94+ }
95+
96+ string GetFullSamplePath ( ) => Sample . App ;
97+
98+ string GetSampleDirectory ( ) => Path . GetDirectoryName ( GetFullSamplePath ( ) ) ! ;
99+
100+ string GetPublishedExePath ( )
101+ {
102+ var dir = GetSampleDirectory ( ) ;
103+ var baseName = Path . Combine ( dir , "artifacts" , "app" , "app" ) ;
104+ return RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? baseName + ".exe" : baseName ;
105+ }
106+
107+ [ Benchmark ( Description = "dnx go" ) ]
108+ public void DnxGo ( )
109+ {
110+ var ( exit , _, err ) = RunProcessCapture ( _dotnet , [ "go" , GetFullSamplePath ( ) ] ) ;
111+ if ( exit != 0 ) throw new Exception ( $ "dnx go failed: { err } ") ;
112+ }
113+
114+ [ Benchmark ( Description = "dotnet publish" ) ]
115+ public void DotnetPublish ( )
116+ {
117+ // Run publish (this measures the full publish cost + run of resulting exe)
118+ var sample = GetFullSamplePath ( ) ;
119+ var ( pubExit , _, pubErr ) = RunProcessCapture ( _dotnet , [ "publish" , sample , "--nologo" , "-v" , "q" ] ) ;
120+ if ( pubExit != 0 ) throw new Exception ( $ "dotnet publish failed: { pubErr } ") ;
121+
122+ var exe = GetPublishedExePath ( ) ;
123+ if ( ! File . Exists ( exe ) )
124+ throw new FileNotFoundException ( $ "Expected published executable not found at { exe } ") ;
125+
126+ var ( runExit , _, runErr ) = RunProcessCapture ( exe , [ ] ) ;
127+ if ( runExit != 0 ) throw new Exception ( $ "published exe failed: { runErr } ") ;
128+ }
129+
130+ [ Benchmark ( Description = "dnx go dev" ) ]
131+ public void DnxGoDev ( )
132+ {
133+ var ( exit , _, err ) = RunProcessCapture ( _dotnet , [ "go" , "dev" , GetFullSamplePath ( ) ] ) ;
134+ if ( exit != 0 ) throw new Exception ( $ "dnx go dev failed: { err } ") ;
135+ }
136+
137+ [ Benchmark ( Description = "dotnet run" ) ]
138+ public void DotnetRun ( )
139+ {
140+ var ( exit , _, err ) = RunProcessCapture ( _dotnet , [ "run" , GetFullSamplePath ( ) ] ) ;
141+ if ( exit != 0 ) throw new Exception ( $ "dotnet run failed: { err } ") ;
142+ }
143+
144+ static ( int ExitCode , string StdOut , string StdErr ) RunProcessCapture ( string fileName , string [ ] args )
145+ {
146+ var psi = new ProcessStartInfo
147+ {
148+ FileName = fileName ,
149+ RedirectStandardOutput = true ,
150+ RedirectStandardError = true ,
151+ UseShellExecute = false ,
152+ CreateNoWindow = true ,
153+ } ;
154+
155+ foreach ( var a in args )
156+ psi . ArgumentList . Add ( a ) ;
157+
158+ using var process = Process . Start ( psi ) ?? throw new InvalidOperationException ( $ "Could not start process: { fileName } ") ;
159+
160+ var stdout = process . StandardOutput . ReadToEnd ( ) ;
161+ var stderr = process . StandardError . ReadToEnd ( ) ;
162+ process . WaitForExit ( ) ;
163+
164+ return ( process . ExitCode , stdout , stderr ) ;
165+ }
166+ }
0 commit comments