Skip to content

Instantly share code, notes, and snippets.

@antonio-leonardo
Created July 21, 2020 01:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antonio-leonardo/e3ccdde2144a94433a01f160132d6cf9 to your computer and use it in GitHub Desktop.
Save antonio-leonardo/e3ccdde2144a94433a01f160132d6cf9 to your computer and use it in GitHub Desktop.
Simple benchmark class extension
/// <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