Created
July 21, 2020 01:47
-
-
Save antonio-leonardo/e3ccdde2144a94433a01f160132d6cf9 to your computer and use it in GitHub Desktop.
Simple benchmark class extension
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Class to provide benchmark performance tests. | |
/// Dependency: This class needs to create .Config file | |
/// </summary> | |
public static class BenchmarkExtension | |
{ | |
/// <summary> | |
/// Query machine constants | |
/// </summary> | |
private const string QUERY_MACHINE = "SELECT * FROM Win32_Processor", | |
MACHINE_NAME_KEY = "MachineName", | |
CORE_PROCESSOR_KEY = "CoreProcessor"; | |
/// <summary> | |
/// Machine name | |
/// </summary> | |
private static string MachineName { get; set; } | |
/// <summary> | |
/// Core processor quantity | |
/// </summary> | |
private static int NumberOfProcessorCores { get; set; } | |
/// <summary> | |
/// Choiced core processor priority | |
/// </summary> | |
private static int CoreProcessorPriority { get; set; } | |
/// <summary> | |
/// Change App.Config values | |
/// in this case, specialized | |
/// on AppSettings, in Runtime | |
/// </summary> | |
/// <param name="key"></param> | |
/// <param name="value"></param> | |
internal static void SetAppSettingsInRuntime(string key, string value) | |
{ | |
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); | |
KeyValueConfigurationCollection settings = configuration.AppSettings.Settings; | |
if (null == settings[key]) | |
{ | |
settings.Add(key, value); | |
} | |
else | |
{ | |
settings[key].Value = value; | |
} | |
configuration.Save(ConfigurationSaveMode.Modified, true); | |
ConfigurationManager.RefreshSection(configuration.AppSettings.SectionInformation.Name); | |
} | |
/// <summary> | |
/// Get machine data like | |
/// core processor quantity | |
/// and machine name | |
/// </summary> | |
private static void GetComnputerInfo() | |
{ | |
if (string.IsNullOrWhiteSpace(MachineName) || MachineName != Environment.MachineName || NumberOfProcessorCores == 0) | |
{ | |
using (ManagementObjectSearcher seacher = new ManagementObjectSearcher(QUERY_MACHINE)) | |
{ | |
foreach (ManagementObject proc in seacher.Get()) | |
{ | |
NumberOfProcessorCores += int.Parse(proc["NumberOfCores"].ToString()); | |
} | |
} | |
MachineName = Environment.MachineName; | |
if (string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings[MACHINE_NAME_KEY]) || ConfigurationManager.AppSettings[MACHINE_NAME_KEY] != MachineName) | |
{ | |
SetAppSettingsInRuntime(MACHINE_NAME_KEY, MachineName); | |
} | |
if (int.Parse(ConfigurationManager.AppSettings[CORE_PROCESSOR_KEY]) == 0) | |
{ | |
SetAppSettingsInRuntime(CORE_PROCESSOR_KEY, NumberOfProcessorCores.ToString()); | |
} | |
} | |
} | |
/// <summary> | |
/// Define the core processor priority to execute benchmark | |
/// if machine contains more than one core, preferred core is not the first | |
/// </summary> | |
private static void DefineCoreProcessorPriority() | |
{ | |
CoreProcessorPriority = int.Parse(ConfigurationManager.AppSettings[CORE_PROCESSOR_KEY]); | |
if (CoreProcessorPriority > 1) | |
{ | |
CoreProcessorPriority = CoreProcessorPriority - 1; | |
} | |
if (CoreProcessorPriority == 0 || CoreProcessorPriority == 1) | |
{ | |
if (NumberOfProcessorCores == 1 || NumberOfProcessorCores == 2) | |
{ | |
CoreProcessorPriority = NumberOfProcessorCores; | |
} | |
else | |
{ | |
if (CoreProcessorPriority == 1) | |
{ | |
CoreProcessorPriority = NumberOfProcessorCores; | |
} | |
else | |
{ | |
CoreProcessorPriority = CoreProcessorPriority--; | |
} | |
} | |
} | |
SetAppSettingsInRuntime(CORE_PROCESSOR_KEY, CoreProcessorPriority.ToString()); | |
} | |
/// <summary> | |
/// Configure the current thread giving priority to other concurrencies | |
/// and define the core processor where this benchark will be executed. | |
/// </summary> | |
private static void MainThreadConfiguration(int coreProcessorPriority) | |
{ | |
// Uses the second Core or Processor for the Test | |
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(coreProcessorPriority); | |
// Prevents "Normal" processes from interrupting Threads | |
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; | |
// Prevents "Normal" Threads from interrupting this thread | |
Thread.CurrentThread.Priority = ThreadPriority.Highest; | |
} | |
/// <summary> | |
/// Forçara a limpeza do Common Languange Runtime | |
/// </summary> | |
private static void ForceGarbageCollector() | |
{ | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
/// <param name="action"></param> | |
/// <param name="name"></param> | |
/// <param name="iterations"></param> | |
public static void Benchmark(this Action action, string name, int iterations) | |
{ | |
try | |
{ | |
GetComnputerInfo(); | |
DefineCoreProcessorPriority(); | |
MainThreadConfiguration(CoreProcessorPriority); | |
ForceGarbageCollector(); | |
// Force JIT compilation of the method. | |
action.Invoke(); | |
// Run the benchmark. | |
Stopwatch stopwatch = new Stopwatch(); | |
//Prevents the JIT Compiler from optimizing Fkt calls away | |
long seed = Environment.TickCount; | |
Console.WriteLine($"Running benchmark '{name}' for {iterations} iterations... "); | |
Console.WriteLine($"{iterations} tests without correct preparation"); | |
Console.WriteLine($"Selected Processor Core: {CoreProcessorPriority}"); | |
Console.WriteLine("Warmup"); | |
for (int repeat = 0; repeat < 20; ++repeat) | |
{ | |
stopwatch.Reset(); | |
stopwatch.Start(); | |
action.Invoke(); | |
stopwatch.Stop(); | |
Console.WriteLine("Ticks: " + stopwatch.ElapsedTicks + | |
" mS: " + stopwatch.ElapsedMilliseconds); | |
} | |
// Uses the second Core or Processor for the Test | |
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(CoreProcessorPriority); | |
// Prevents "Normal" processes from interrupting Threads | |
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; | |
// Prevents "Normal" Threads from interrupting this thread | |
Thread.CurrentThread.Priority = ThreadPriority.Highest; | |
Console.WriteLine(); | |
Console.WriteLine(); | |
Console.WriteLine($"{iterations} Tests with correct preparation"); | |
Console.WriteLine("Warmup"); | |
stopwatch.Reset(); | |
stopwatch.Start(); | |
// A Warmup of 1000-1500 mS stabilizes the CPU cache and pipeline. | |
while (stopwatch.ElapsedMilliseconds < 1200) | |
{ | |
action.Invoke();// Warmup | |
} | |
stopwatch.Stop(); | |
long[] elasedMiliseconds = new long[iterations]; | |
for (int i = 0; i < iterations; i++) | |
{ | |
stopwatch.Reset(); | |
stopwatch.Start(); | |
action.Invoke(); | |
stopwatch.Stop(); | |
Console.WriteLine("Ticks: " + stopwatch.ElapsedTicks + " mS: " + stopwatch.ElapsedMilliseconds); | |
elasedMiliseconds[i] = stopwatch.ElapsedMilliseconds; | |
} | |
// Output results. | |
Console.WriteLine($"Elapsed time AVG: {elasedMiliseconds.Sum() / iterations} ms."); | |
} | |
catch (OutOfMemoryException) | |
{ | |
Console.WriteLine($"Out of memory!"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment