11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT License.
33
4+ using Azure . Test . PerfStress ;
45using CommandLine ;
56using System ;
67using System . Collections . Generic ;
78using System . Diagnostics ;
89using System . Linq ;
910using System . Reflection ;
10- using System . Reflection . Emit ;
1111using System . Runtime ;
1212using System . Text . Json ;
1313using System . Threading ;
@@ -31,12 +31,12 @@ public static async Task Main(Assembly assembly, string[] args)
3131
3232 if ( testTypes . Any ( ) )
3333 {
34- var optionTypes = GetOptionTypes ( testTypes ) ;
35- await Parser . Default . ParseArguments ( args , optionTypes ) . MapResult < PerfOptions , Task > (
34+ var optionTypes = PerfStressUtilities . GetOptionTypes ( testTypes ) ;
35+ await PerfStressUtilities . Parser . ParseArguments ( args , optionTypes ) . MapResult < PerfOptions , Task > (
3636 async o =>
3737 {
3838 var verbName = o . GetType ( ) . GetCustomAttribute < VerbAttribute > ( ) . Name ;
39- var testType = testTypes . Where ( t => GetVerbName ( t . Name ) == verbName ) . Single ( ) ;
39+ var testType = testTypes . Where ( t => PerfStressUtilities . GetVerbName ( t . Name ) == verbName ) . Single ( ) ;
4040 await Run ( testType , o ) ;
4141 } ,
4242 errors => Task . CompletedTask
@@ -83,7 +83,7 @@ private static async Task Run(Type testType, PerfOptions options)
8383 Console . WriteLine ( ) ;
8484
8585 using var setupStatusCts = new CancellationTokenSource ( ) ;
86- var setupStatusThread = PrintStatus ( "=== Setup ===" , ( ) => "." , newLine : false , setupStatusCts . Token ) ;
86+ var setupStatusThread = PerfStressUtilities . PrintStatus ( "=== Setup ===" , ( ) => "." , newLine : false , setupStatusCts . Token ) ;
8787
8888 using var cleanupStatusCts = new CancellationTokenSource ( ) ;
8989 Thread cleanupStatusThread = null ;
@@ -109,7 +109,8 @@ private static async Task Run(Type testType, PerfOptions options)
109109
110110 if ( options . Warmup > 0 )
111111 {
112- await RunTestsAsync ( tests , options . Sync , options . Parallel , options . Rate , options . Warmup , "Warmup" ) ;
112+ await RunTestsAsync ( tests , options . Sync , options . Parallel , options . Rate , options . Warmup , options . StatusInterval ,
113+ "Warmup" ) ;
113114 }
114115
115116 for ( var i = 0 ; i < options . Iterations ; i ++ )
@@ -119,7 +120,8 @@ private static async Task Run(Type testType, PerfOptions options)
119120 {
120121 title += " " + ( i + 1 ) ;
121122 }
122- await RunTestsAsync ( tests , options . Sync , options . Parallel , options . Rate , options . Duration , title , options . JobStatistics , options . Latency ) ;
123+ await RunTestsAsync ( tests , options . Sync , options . Parallel , options . Rate , options . Duration , options . StatusInterval ,
124+ title , options . JobStatistics , options . Latency ) ;
123125 }
124126 }
125127 finally
@@ -128,7 +130,7 @@ private static async Task Run(Type testType, PerfOptions options)
128130 {
129131 if ( cleanupStatusThread == null )
130132 {
131- cleanupStatusThread = PrintStatus ( "=== Cleanup ===" , ( ) => "." , newLine : false , cleanupStatusCts . Token ) ;
133+ cleanupStatusThread = PerfStressUtilities . PrintStatus ( "=== Cleanup ===" , ( ) => "." , newLine : false , cleanupStatusCts . Token ) ;
132134 }
133135
134136 await Task . WhenAll ( tests . Select ( t => t . CleanupAsync ( ) ) ) ;
@@ -141,7 +143,7 @@ private static async Task Run(Type testType, PerfOptions options)
141143 {
142144 if ( cleanupStatusThread == null )
143145 {
144- cleanupStatusThread = PrintStatus ( "=== Cleanup ===" , ( ) => "." , newLine : false , cleanupStatusCts . Token ) ;
146+ cleanupStatusThread = PerfStressUtilities . PrintStatus ( "=== Cleanup ===" , ( ) => "." , newLine : false , cleanupStatusCts . Token ) ;
145147 }
146148
147149 await tests [ 0 ] . GlobalCleanupAsync ( ) ;
@@ -161,7 +163,7 @@ private static async Task Run(Type testType, PerfOptions options)
161163 }
162164
163165 private static async Task RunTestsAsync ( IPerfTest [ ] tests , bool sync , int parallel , int ? rate ,
164- int durationSeconds , string title , bool jobStatistics = false , bool latency = false )
166+ int durationSeconds , int statusIntervalSeconds , string title , bool jobStatistics = false , bool latency = false )
165167 {
166168 _completedOperations = new int [ parallel ] ;
167169 _lastCompletionTimes = new TimeSpan [ parallel ] ;
@@ -191,7 +193,7 @@ private static async Task RunTestsAsync(IPerfTest[] tests, bool sync, int parall
191193 var lastCompleted = 0 ;
192194
193195 using var progressStatusCts = new CancellationTokenSource ( ) ;
194- var progressStatusThread = PrintStatus (
196+ var progressStatusThread = PerfStressUtilities . PrintStatus (
195197 $ "=== { title } ===" + Environment . NewLine +
196198 "Current\t \t Total" ,
197199 ( ) =>
@@ -202,7 +204,9 @@ private static async Task RunTestsAsync(IPerfTest[] tests, bool sync, int parall
202204 return currentCompleted + "\t \t " + totalCompleted ;
203205 } ,
204206 newLine : true ,
205- progressStatusCts . Token ) ;
207+ progressStatusCts . Token ,
208+ statusIntervalSeconds
209+ ) ;
206210
207211 Thread pendingOperationsThread = null ;
208212 if ( rate . HasValue )
@@ -375,7 +379,7 @@ private static async Task RunLoopAsync(IPerfTest test, int index, bool latency,
375379 catch ( Exception e )
376380 {
377381 // Ignore if any part of the exception chain is type OperationCanceledException
378- if ( ! ContainsOperationCanceledException ( e ) )
382+ if ( ! PerfStressUtilities . ContainsOperationCanceledException ( e ) )
379383 {
380384 throw ;
381385 }
@@ -406,112 +410,5 @@ private static Thread WritePendingOperations(int rate, CancellationToken token)
406410
407411 return thread ;
408412 }
409-
410- // Run in dedicated thread instead of using async/await in ThreadPool, to ensure this thread has priority
411- // and never fails to run to due ThreadPool starvation.
412- private static Thread PrintStatus ( string header , Func < object > status , bool newLine , CancellationToken token )
413- {
414- var thread = new Thread ( ( ) =>
415- {
416- Console . WriteLine ( header ) ;
417-
418- bool needsExtraNewline = false ;
419-
420- while ( ! token . IsCancellationRequested )
421- {
422- try
423- {
424- Sleep ( TimeSpan . FromSeconds ( 1 ) , token ) ;
425- }
426- catch ( OperationCanceledException )
427- {
428- }
429-
430- var obj = status ( ) ;
431-
432- if ( newLine )
433- {
434- Console . WriteLine ( obj ) ;
435- }
436- else
437- {
438- Console . Write ( obj ) ;
439- needsExtraNewline = true ;
440- }
441- }
442-
443- if ( needsExtraNewline )
444- {
445- Console . WriteLine ( ) ;
446- }
447-
448- Console . WriteLine ( ) ;
449- } ) ;
450-
451- thread . Start ( ) ;
452-
453- return thread ;
454- }
455-
456- private static void Sleep ( TimeSpan timeout , CancellationToken token )
457- {
458- var sw = Stopwatch . StartNew ( ) ;
459- while ( sw . Elapsed < timeout )
460- {
461- if ( token . IsCancellationRequested )
462- {
463- // Simulate behavior of Task.Delay(TimeSpan, CancellationToken)
464- throw new OperationCanceledException ( ) ;
465- }
466-
467- Thread . Sleep ( TimeSpan . FromMilliseconds ( 10 ) ) ;
468- }
469- }
470-
471- private static bool ContainsOperationCanceledException ( Exception e )
472- {
473- if ( e is OperationCanceledException )
474- {
475- return true ;
476- }
477- else if ( e . InnerException != null )
478- {
479- return ContainsOperationCanceledException ( e . InnerException ) ;
480- }
481- else
482- {
483- return false ;
484- }
485- }
486-
487- // Dynamically create option types with a "Verb" attribute
488- private static Type [ ] GetOptionTypes ( IEnumerable < Type > testTypes )
489- {
490- var optionTypes = new List < Type > ( ) ;
491-
492- var ab = AssemblyBuilder . DefineDynamicAssembly ( new AssemblyName ( "Options" ) , AssemblyBuilderAccess . Run ) ;
493- var mb = ab . DefineDynamicModule ( "Options" ) ;
494-
495- foreach ( var t in testTypes )
496- {
497- var baseOptionsType = t . GetConstructors ( ) . First ( ) . GetParameters ( ) [ 0 ] . ParameterType ;
498- var tb = mb . DefineType ( t . Name + "Options" , TypeAttributes . Public , baseOptionsType ) ;
499-
500- var attrCtor = typeof ( VerbAttribute ) . GetConstructor ( new Type [ ] { typeof ( string ) , typeof ( bool ) } ) ;
501- var verbName = GetVerbName ( t . Name ) ;
502- tb . SetCustomAttribute ( new CustomAttributeBuilder ( attrCtor ,
503- new object [ ] { verbName , attrCtor . GetParameters ( ) [ 1 ] . DefaultValue } ) ) ;
504-
505- optionTypes . Add ( tb . CreateType ( ) ) ;
506- }
507-
508- return optionTypes . ToArray ( ) ;
509- }
510-
511- private static string GetVerbName ( string testName )
512- {
513- var lower = testName . ToLowerInvariant ( ) ;
514- return lower . EndsWith ( "test" ) ? lower . Substring ( 0 , lower . Length - 4 ) : lower ;
515- }
516413 }
517414}
0 commit comments