Create nanoFramework.Benchmark with sample.

This commit is contained in:
Marcin Torba 2022-08-22 00:36:37 +02:00
Родитель 266203fa6c
Коммит 0f48e0aa23
31 изменённых файлов: 1338 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,46 @@
using System;
using System.Collections;
using nanoFramework.Benchmark.Attributes;
using nanoFramework.Logging.Debug;
namespace nanoFramework.Benchmark.Sample
{
[ItterationCount(11)]
[DebugLogger]
[CsvParser]
[ConsoleParser]
public class CreateRadomCollectionBenchmark
{
public int NumbersToCreateCount { get; set; } = 1000;
private Random random;
[Setup]
public void Setup()
{
random = new Random();
}
[Benchmark]
public void CreateRandomArray()
{
var arrayOfInts = new int[NumbersToCreateCount];
for (int i = 0; i < NumbersToCreateCount; i++)
{
var value = random.Next();
arrayOfInts[i] = value;
}
}
[Benchmark]
public void CreateRandomList()
{
var listOfInts = new ArrayList();
for (int i = 0; i < NumbersToCreateCount; i++)
{
var value = random.Next();
listOfInts.Add(value);
}
}
}
}

Просмотреть файл

@ -0,0 +1,8 @@
using System;
namespace nanoFramework.Benchmark.Sample
{
internal interface IAssemblyHandler
{
}
}

Просмотреть файл

@ -0,0 +1,18 @@
using System;
using System.Diagnostics;
using System.Threading;
namespace nanoFramework.Benchmark.Sample
{
public class Program
{
public static void Main()
{
Debug.WriteLine("Hello from nanoFramework.Benchmark!");
BenchmarkRunner.Run(typeof(IAssemblyHandler).Assembly);
Thread.Sleep(Timeout.Infinite);
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CSharp.BlankApplication")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CSharp.BlankApplication")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Просмотреть файл

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<NanoFrameworkProjectSystemPath>$(MSBuildExtensionsPath)\nanoFramework\v1.0\</NanoFrameworkProjectSystemPath>
</PropertyGroup>
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectTypeGuids>{11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<ProjectGuid>2f7d544d-0538-427d-a2d0-54b77c3f5f0a</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<FileAlignment>512</FileAlignment>
<RootNamespace>nanoFramework.Benchmark.Sample</RootNamespace>
<AssemblyName>nanoFramework.Benchmark.Sample</AssemblyName>
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
</PropertyGroup>
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
<ItemGroup>
<Compile Include="CreateRadomCollectionBenchmark.cs" />
<Compile Include="IAssemblyHandler.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="mscorlib">
<HintPath>..\packages\nanoFramework.CoreLibrary.1.12.0\lib\mscorlib.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.Logging">
<HintPath>..\packages\nanoFramework.Logging.1.1.2\lib\nanoFramework.Logging.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.Logging.Serial">
<HintPath>..\packages\nanoFramework.Logging.Serial.1.1.2\lib\nanoFramework.Logging.Serial.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.Runtime.Events">
<HintPath>..\packages\nanoFramework.Runtime.Events.1.10.0\lib\nanoFramework.Runtime.Events.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.System.Collections">
<HintPath>..\packages\nanoFramework.System.Collections.1.4.0\lib\nanoFramework.System.Collections.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.System.Text">
<HintPath>..\packages\nanoFramework.System.Text.1.2.3\lib\nanoFramework.System.Text.dll</HintPath>
</Reference>
<Reference Include="System.IO.Ports">
<HintPath>..\packages\nanoFramework.System.IO.Ports.1.1.6\lib\System.IO.Ports.dll</HintPath>
</Reference>
<Reference Include="System.IO.Streams">
<HintPath>..\packages\nanoFramework.System.IO.Streams.1.1.9\lib\System.IO.Streams.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\nanoFramework.Benchmark\nanoFramework.Benchmark.nfproj" />
</ItemGroup>
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
<ProjectExtensions>
<ProjectCapabilities>
<ProjectConfigurationsDeclaredAsItems />
</ProjectCapabilities>
</ProjectExtensions>
</Project>

Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="nanoFramework.CoreLibrary" version="1.12.0" targetFramework="netnano1.0" />
<package id="nanoFramework.Logging" version="1.1.2" targetFramework="netnano1.0" />
<package id="nanoFramework.Logging.Serial" version="1.1.2" targetFramework="netnano1.0" />
<package id="nanoFramework.Runtime.Events" version="1.10.0" targetFramework="netnano1.0" />
<package id="nanoFramework.System.Collections" version="1.4.0" targetFramework="netnano1.0" />
<package id="nanoFramework.System.IO.Ports" version="1.1.6" targetFramework="netnano1.0" />
<package id="nanoFramework.System.IO.Streams" version="1.1.9" targetFramework="netnano1.0" />
<package id="nanoFramework.System.Text" version="1.2.3" targetFramework="netnano1.0" />
</packages>

Просмотреть файл

@ -0,0 +1,35 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32630.192
MinimumVisualStudioVersion = 10.0.40219.1
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.Benchmark", "nanoFramework.Benchmark\nanoFramework.Benchmark.nfproj", "{B2827CB3-4BCD-40C0-A82D-5AB476E84CE4}"
EndProject
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.Benchmark.Sample", "nanoFramework.Benchmark.Sample\nanoFramework.Benchmark.Sample.nfproj", "{2F7D544D-0538-427D-A2D0-54B77C3F5F0A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B2827CB3-4BCD-40C0-A82D-5AB476E84CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2827CB3-4BCD-40C0-A82D-5AB476E84CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2827CB3-4BCD-40C0-A82D-5AB476E84CE4}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{B2827CB3-4BCD-40C0-A82D-5AB476E84CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2827CB3-4BCD-40C0-A82D-5AB476E84CE4}.Release|Any CPU.Build.0 = Release|Any CPU
{B2827CB3-4BCD-40C0-A82D-5AB476E84CE4}.Release|Any CPU.Deploy.0 = Release|Any CPU
{2F7D544D-0538-427D-A2D0-54B77C3F5F0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F7D544D-0538-427D-A2D0-54B77C3F5F0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F7D544D-0538-427D-A2D0-54B77C3F5F0A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{2F7D544D-0538-427D-A2D0-54B77C3F5F0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F7D544D-0538-427D-A2D0-54B77C3F5F0A}.Release|Any CPU.Build.0 = Release|Any CPU
{2F7D544D-0538-427D-A2D0-54B77C3F5F0A}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {84ADC838-8FF6-4A3F-A697-E9A5A5C63DC7}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,11 @@
using Microsoft.Extensions.Logging;
using System;
namespace nanoFramework.Benchmark.Attributes.Abstract
{
[AttributeUsage(AttributeTargets.Class)]
public abstract class LoggerAttribute : Attribute
{
public abstract ILogger Logger { get; }
}
}

Просмотреть файл

@ -0,0 +1,25 @@
using nanoFramework.Benchmark.Parser;
using System;
namespace nanoFramework.Benchmark.Attributes.Abstract
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public abstract class ParserAttribute : Attribute
{
private static IResultParser parser;
public IResultParser ResultParser
{
get
{
if (parser == null)
{
parser = CreateNewResultParser;
}
return parser;
}
}
public abstract IResultParser CreateNewResultParser { get; }
}
}

Просмотреть файл

@ -0,0 +1,12 @@
using nanoFramework.Benchmark.Attributes.Abstract;
using nanoFramework.Benchmark.Parser;
using System;
namespace nanoFramework.Benchmark.Attributes
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class ConsoleParserAttribute : ParserAttribute
{
public override IResultParser CreateNewResultParser => new ConsoleParser();
}
}

Просмотреть файл

@ -0,0 +1,12 @@
using nanoFramework.Benchmark.Attributes.Abstract;
using nanoFramework.Benchmark.Parser;
using System;
namespace nanoFramework.Benchmark.Attributes
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class CsvParserAttribute : ParserAttribute
{
public override IResultParser CreateNewResultParser => new CsvParser();
}
}

Просмотреть файл

@ -0,0 +1,27 @@
using Microsoft.Extensions.Logging;
using nanoFramework.Benchmark.Attributes.Abstract;
using nanoFramework.Logging.Debug;
using System;
namespace nanoFramework.Benchmark.Attributes
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class DebugLoggerAttribute : LoggerAttribute
{
const string DebugLoggerCategory = "DebugLogger";
private static ILogger logger;
public override ILogger Logger
{
get
{
if (logger == null)
{
logger = new DebugLoggerFactory().CreateLogger(DebugLoggerCategory);
}
return logger;
}
}
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using System;
namespace nanoFramework.Benchmark.Attributes
{
[AttributeUsage(AttributeTargets.Class)]
public sealed class ItterationCountAttribute : Attribute
{
public const ushort DefaultItterationCount = 10;
public int Count { get; }
public ItterationCountAttribute(int count)
{
Count = count;
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
using System;
namespace nanoFramework.Benchmark
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class BenchmarkAttribute : Attribute
{
}
}

Просмотреть файл

@ -0,0 +1,118 @@
using Microsoft.Extensions.Logging;
using nanoFramework.Benchmark.Helpers;
using nanoFramework.Benchmark.Parser;
using nanoFramework.Benchmark.Result;
using System;
using System.Collections;
using System.Reflection;
namespace nanoFramework.Benchmark
{
public static class BenchmarkRunner
{
private static string[] ExcludedClassNamesFromBenchmarks =
{
"Program"
};
public static void Run(Assembly assembly)
{
var classes = assembly.GetTypes();
foreach (var type in classes)
{
if (!type.IsClass)
{
continue;
}
if (type.IsInterface)
{
continue;
}
if (type.IsAbstract)
{
continue;
}
if (ExcludedClassNamesFromBenchmarks.Contains(type.Name))
{
continue;
}
RunClass(type);
}
}
public static void RunClass(Type classType)
{
if (!classType.IsClass)
throw new InvalidOperationException();
if (classType.IsAbstract)
throw new InvalidOperationException();
#region SetupRunner
var itterationCount = SettingsHelper.GetItterationCount(classType);
var logger = SettingsHelper.GetLoggerIfExists(classType);
var parsers = SettingsHelper.GetResultParser(classType);
#endregion
var classToInvokeMethodOn = ReflectionHelpers.CreateObjectViaReflection(classType);
if (classToInvokeMethodOn == null)
{
logger?.LogError($"Unable to create instance of {classType.FullName} class. Make sure that class contains parameterless constructor.");
return;
}
var allMethods = classType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
var setupMethod = (MethodInfo)ReflectionHelpers.GetSetupMethodIfExists(allMethods);
setupMethod?.Invoke(classToInvokeMethodOn, null);
var methodResults = new ArrayList();
foreach (MethodInfo method in ReflectionHelpers.GetBenchmarkMethods(allMethods, logger))
{
var result = Run(classToInvokeMethodOn, method, itterationCount);
methodResults.Add(new MethodResult(method.Name, result));
}
var benchmarkResult = new SingleBenchmarkResult(
ArrayListHelper.ConvertFromArrayListToMethodResultArray(methodResults),
classType.Name,
itterationCount);
InvokeParses(benchmarkResult, parsers, logger);
}
private static SingleTestResult[] Run(object classToInvokeMethodOn, MethodInfo method, int itterationCount)
{
var resultCollection = new SingleTestResult[itterationCount];
// There is a check if method has no parameters before
var parameters = new object[0];
for (var i = 0; i < itterationCount; i++)
{
var startTime = Environment.TickCount64;
method.Invoke(classToInvokeMethodOn, parameters);
var endTime = Environment.TickCount64;
resultCollection[i] = new SingleTestResult(TimeSpan.FromMilliseconds(endTime - startTime));
}
return resultCollection;
}
private static void InvokeParses(SingleBenchmarkResult benchmarkResult, IResultParser[] Parsers, ILogger logger)
{
if (benchmarkResult.MethodResults.Length == 0)
{
logger?.LogWarning($"No result found from {benchmarkResult.ClassName} class.");
}
foreach (var parser in Parsers)
{
parser.Parse(benchmarkResult);
}
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
using nanoFramework.Benchmark.Parser;
using nanoFramework.Benchmark.Result;
using System;
using System.Collections;
using System.Reflection;
namespace nanoFramework.Benchmark.Helpers
{
internal static class ArrayListHelper
{
internal static MethodResult[] ConvertFromArrayListToMethodResultArray(ArrayList arrayList)
{
var result = new MethodResult[arrayList.Count];
for (int i = 0; i < arrayList.Count; i++)
{
result[i] = (MethodResult)arrayList[i];
}
return result;
}
internal static MethodInfo[] ConvertFromArrayListToMethodInfoArray(ArrayList arrayList)
{
var result = new MethodInfo[arrayList.Count];
for (int i = 0; i < arrayList.Count; i++)
{
result[i] = (MethodInfo)arrayList[i];
}
return result;
}
internal static IResultParser[] ConvertFromArrayListToIResultParserArray(ArrayList arrayList)
{
var result = new IResultParser[arrayList.Count];
for (int i = 0; i < arrayList.Count; i++)
{
result[i] = (IResultParser)arrayList[i];
}
return result;
}
}
}

Просмотреть файл

@ -0,0 +1,209 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections;
using System.Reflection;
namespace nanoFramework.Benchmark.Helpers
{
internal static class ReflectionHelpers
{
private static string[] ExcludedMethodsFromBenchmarks =
{
"ToString",
"GetType",
"GetHashCode"
};
internal static object CreateObjectViaReflection(Type typeToCreate)
{
// Empty array of Types - GetConstructor didn't work unless given an empty array of Type[]
Type[] types = { };
var ci = typeToCreate.GetConstructor(types);
if (ci == null)
{
// failed to create target instance
return null;
}
return ci.Invoke(null);
}
internal static object GetSetupMethodIfExists(MethodInfo[] methods)
{
foreach (var item in methods)
{
if (GetFirstOrDefaultAttribute(item, typeof(SetupAttribute)) != null)
{
return item;
}
}
return null;
}
internal static object GetFirstAttribute(MemberInfo member, Type attributeType)
{
var attribute = GetFirstOrDefaultAttribute(member, attributeType);
if (attribute == null)
{
throw new InvalidOperationException();
}
return attribute;
}
internal static object GetFirstOrDefaultAttribute(MemberInfo member, Type attributeType)
{
if (attributeType == null)
{
throw new ArgumentNullException();
}
if (!attributeType.IsSubclassOf(typeof(Attribute)))
{
throw new ArgumentException();
}
if (member == null)
{
throw new ArgumentNullException();
}
var attribs = member.GetCustomAttributes(false);
foreach (var item in attribs)
{
var itemType = item.GetType();
if (itemType == attributeType)
{
return item;
}
if (itemType.IsSubclassOf(attributeType))
{
return item;
}
}
return null;
}
internal static bool IsMethodProperty(MethodInfo method)
{
if (method.ReturnType != null && method.Name.StartsWith("get_"))
{
return true;
}
if (method.ReturnType == null && method.Name.StartsWith("set_"))
{
return true;
}
return false;
}
private static bool IsMethodExcluded(MethodInfo method)
{
if (IsMethodProperty(method))
{
return true;
}
if (ExcludedMethodsFromBenchmarks.Contains(method.Name))
{
return true;
}
var setupAttribute = GetFirstOrDefaultAttribute(method, typeof(SetupAttribute));
if (setupAttribute != null)
{
return true;
}
return false;
}
internal static MethodInfo[] GetBenchmarkMethods(MethodInfo[] methodInfos, ILogger logger)
{
var returnList = new ArrayList();
foreach (var method in methodInfos)
{
if (IsMethodExcluded(method))
{
continue;
}
var benchmarkAttrib = GetFirstOrDefaultAttribute(method, typeof(BenchmarkAttribute));
if (benchmarkAttrib == null)
{
continue;
}
var parameters = method.GetParameters();
if (parameters.Length != 0)
{
logger?.LogWarning($"Skippping {method.Name} due to invalid parameters count. Benchmark methods should contains 0 parameters.");
continue;
}
returnList.Add(method);
}
return ArrayListHelper.ConvertFromArrayListToMethodInfoArray(returnList);
}
internal static object GetObjectInstanceFromPropertyIfExists(object classObject, Type typeToGet)
{
var allMethods = classObject.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
foreach (var method in allMethods)
{
if (!method.Name.StartsWith("get_"))
{
continue;
}
if (method.ReturnType != typeToGet)
{
continue;
}
return method.Invoke(classObject, new object[0]);
}
return null;
}
internal static object GetObjectInstanceFromAttributesIfExists(Type classType, Type typeToGet)
{
var attribs = classType.GetCustomAttributes(false);
foreach (var attrib in attribs)
{
var foundObject = GetObjectInstanceFromPropertyIfExists(attrib, typeToGet);
if (foundObject != null)
{
return foundObject;
}
}
return null;
}
internal static ArrayList GetObjectsInstanceFromAttributesIfExists(Type classType, Type typeToGet)
{
var attribs = classType.GetCustomAttributes(false);
var returnList = new ArrayList();
foreach (var attrib in attribs)
{
var foundObject = GetObjectInstanceFromPropertyIfExists(attrib, typeToGet);
if (foundObject != null)
{
returnList.Add(foundObject);
}
}
return returnList;
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
using Microsoft.Extensions.Logging;
using nanoFramework.Benchmark.Attributes;
using nanoFramework.Benchmark.Parser;
using System;
using System.Reflection;
namespace nanoFramework.Benchmark.Helpers
{
internal class SettingsHelper
{
private static IResultParser[] defaultParsers;
private static IResultParser[] DefaultParsers
{
get
{
if (defaultParsers == null)
{
defaultParsers = new IResultParser[]
{
new ConsoleParser()
};
}
return defaultParsers;
}
}
internal static ILogger GetLoggerIfExists(Type classType)
{
return (ILogger)ReflectionHelpers.GetObjectInstanceFromAttributesIfExists(classType, typeof(ILogger));
}
internal static int GetItterationCount(Type benchmarkClass)
{
var attribute = ReflectionHelpers.GetFirstOrDefaultAttribute(benchmarkClass, typeof(ItterationCountAttribute));
if (attribute != null && attribute is ItterationCountAttribute itterationCountAttribute)
{
return itterationCountAttribute.Count;
}
return ItterationCountAttribute.DefaultItterationCount;
}
internal static IResultParser[] GetResultParser(Type classType)
{
var parsers = ReflectionHelpers.GetObjectsInstanceFromAttributesIfExists(classType, typeof(IResultParser));
if (parsers != null && parsers.Count != 0)
{
return ArrayListHelper.ConvertFromArrayListToIResultParserArray(parsers);
}
return DefaultParsers;
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
using System;
namespace nanoFramework.Benchmark.Helpers
{
internal static class StringArrayExtensions
{
internal static bool Contains(this string[] array, string item)
{
foreach (var arrayItem in array)
{
if (arrayItem == item)
{
return true;
}
}
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,75 @@
using nanoFramework.Benchmark.Helpers;
using nanoFramework.Benchmark.Result;
using nanoFramework.Benchmark.Result.Attributes;
using System;
using System.Reflection;
using System.Text;
namespace nanoFramework.Benchmark.Parser.Abstract
{
internal abstract class BaseTextParser : IResultParser
{
protected abstract string GetParserName(SingleBenchmarkResult benchmarkResult);
protected abstract string GetHeader(MethodInfo[] dataToDisplay);
protected abstract string GetRow(SingleBenchmarkResult item, MethodInfo[] dataToDisplay, int rowIndex);
protected virtual void PrintLine(string value)
{
Console.WriteLine(value);
}
protected static string GetHeaderFromAttribute(MethodInfo methodInfo)
{
// At this point all passed MethodsInfo should be properties and should have Display attribute
return ((DisplayAttribute)ReflectionHelpers.GetFirstAttribute(methodInfo, typeof(DisplayAttribute))).Header;
}
protected static string PadRight(string value, int toLenght)
{
var builder = new StringBuilder(value);
for (int j = 0; j < toLenght - value.Length; j++)
{
builder.Append(" ");
}
return builder.ToString();
}
protected string GetDataFromDataMethod(SingleBenchmarkResult item, MethodInfo dataMethod, int rowIndex)
{
var parameters = GetParametersForDataMethod(dataMethod, rowIndex);
return (string)dataMethod.Invoke(item, parameters);
}
protected static object[] GetParametersForDataMethod(MethodInfo dataMethod, int rowIndex)
{
var parametersCount = dataMethod.GetParameters().Length;
if (parametersCount == 0)
{
return new object[0];
}
if (parametersCount == 1)
{
return new object[] { rowIndex };
}
throw new InvalidOperationException();
}
public virtual void Parse(SingleBenchmarkResult benchmarkResult)
{
PrintLine(string.Empty);
PrintLine(string.Empty);
PrintLine(GetParserName(benchmarkResult));
var dataToDisplay = SingleBenchmarkResult.GetDataToDisplay();
PrintLine(GetHeader(dataToDisplay));
for (int i = 0; i < benchmarkResult.MethodResults.Length; i++)
{
PrintLine(GetRow(benchmarkResult, dataToDisplay, i));
}
}
}
}

Просмотреть файл

@ -0,0 +1,101 @@
using nanoFramework.Benchmark.Parser.Abstract;
using nanoFramework.Benchmark.Result;
using System;
using System.Reflection;
using System.Text;
namespace nanoFramework.Benchmark.Parser
{
internal class ConsoleParser : BaseTextParser, IResultParser
{
private const string ConsoleTableSeparator = "|";
private object lockObject = new();
private int[] columnsSize;
public override void Parse(SingleBenchmarkResult benchmarkResult)
{
lock (lockObject)
{
columnsSize = CalculateColumnSize(benchmarkResult);
base.Parse(benchmarkResult);
}
}
private int[] CalculateColumnSize(SingleBenchmarkResult benchmarkResult)
{
var dataToDisplay = SingleBenchmarkResult.GetDataToDisplay();
int[] columnSizes = new int[dataToDisplay.Length];
for (int i = 0; i < dataToDisplay.Length; i++)
{
var column = dataToDisplay[i];
var header = GetHeaderFromAttribute(column);
columnSizes[i] = CalculateColumnSize(column, header, benchmarkResult);
}
return columnSizes;
}
private static int CalculateColumnSize(MethodInfo method, string header, SingleBenchmarkResult benchmarkResult)
{
var size = header.Length;
for (int i = 0; i < benchmarkResult.MethodResults.Length; i++)
{
var parameters = GetParametersForDataMethod(method, i);
var value = (string)method.Invoke(benchmarkResult, parameters);
if (value.Length > size)
{
size = value.Length;
}
}
return size;
}
protected override string GetParserName(SingleBenchmarkResult benchmarkResult)
{
return $"Console export: {benchmarkResult.ClassName} benchmark class.";
}
protected override string GetHeader(MethodInfo[] dataToDisplay)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append($"{ConsoleTableSeparator}");
for (int i = 0; i < dataToDisplay.Length; i++)
{
var item = dataToDisplay[i];
var header = GetHeaderFromAttribute(item);
stringBuilder.Append(" ");
stringBuilder.Append(PadRight(header, columnsSize[i]));
stringBuilder.Append($" {ConsoleTableSeparator}");
}
// Create row after header, seperating rows and headers
var headerLength = stringBuilder.Length;
stringBuilder.AppendLine();
stringBuilder.Append($"{ConsoleTableSeparator} ");
// -4 characters = '| ' + ' |'
for (int i = 0; i < headerLength - 4; i++)
{
stringBuilder.Append("-");
}
stringBuilder.Append($" {ConsoleTableSeparator}");
return stringBuilder.ToString();
}
protected override string GetRow(SingleBenchmarkResult item, MethodInfo[] dataToDisplay, int rowIndex)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append($"{ConsoleTableSeparator}");
for (int i = 0; i < dataToDisplay.Length; i++)
{
var dataMethod = dataToDisplay[i];
var rowData = GetDataFromDataMethod(item, dataMethod, rowIndex);
stringBuilder.Append($" ");
stringBuilder.Append(PadRight(rowData, columnsSize[i]));
stringBuilder.Append($" {ConsoleTableSeparator}");
}
return stringBuilder.ToString();
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
using nanoFramework.Benchmark.Parser.Abstract;
using nanoFramework.Benchmark.Result;
using System.Reflection;
using System.Text;
namespace nanoFramework.Benchmark.Parser
{
internal class CsvParser : BaseTextParser, IResultParser
{
private const char ValuesSeparator = ';';
protected override string GetParserName(SingleBenchmarkResult benchmarkResult)
{
return $"CSV export: {benchmarkResult.ClassName} benchmark class.";
}
protected override string GetHeader(MethodInfo[] dataToDisplay)
{
var stringBuilder = new StringBuilder();
for (int i = 0; i < dataToDisplay.Length; i++)
{
var item = dataToDisplay[i];
var header = GetHeaderFromAttribute(item);
stringBuilder.Append(header);
if (i < dataToDisplay.Length - 1)
{
stringBuilder.Append($"{ValuesSeparator}");
}
}
return stringBuilder.ToString();
}
protected override string GetRow(SingleBenchmarkResult item, MethodInfo[] dataToDisplay, int rowIndex)
{
var stringBuilder = new StringBuilder();
for (int i = 0; i < dataToDisplay.Length; i++)
{
var dataMethod = dataToDisplay[i];
var rowData = GetDataFromDataMethod(item, dataMethod, rowIndex);
stringBuilder.Append(rowData);
if (i < dataToDisplay.Length - 1)
{
stringBuilder.Append($"{ValuesSeparator}");
}
}
return stringBuilder.ToString();
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
using nanoFramework.Benchmark.Result;
using System;
namespace nanoFramework.Benchmark.Parser
{
public interface IResultParser
{
void Parse(SingleBenchmarkResult benchmarkResult);
}
}

Просмотреть файл

@ -0,0 +1,39 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CSharp.TestApplication")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CSharp.TestApplication")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
/////////////////////////////////////////////////////////////////
// This attribute is mandatory when building Interop libraries //
// update this whenever the native assembly signature changes //
[assembly: AssemblyNativeVersion("1.0.0.0")]
/////////////////////////////////////////////////////////////////

Просмотреть файл

@ -0,0 +1,15 @@
using System;
namespace nanoFramework.Benchmark.Result.Attributes
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class DisplayAttribute : Attribute
{
public string Header { get; }
public DisplayAttribute(string header)
{
Header = header;
}
}
}

Просмотреть файл

@ -0,0 +1,101 @@
using nanoFramework.Benchmark.Helpers;
using nanoFramework.Benchmark.Result.Attributes;
using System;
using System.Collections;
using System.Reflection;
namespace nanoFramework.Benchmark.Result
{
public class MethodResult
{
private string _methodName;
private string _meanExecutionTime;
private string _maxExecutionTime;
private string _minExecutionTime;
public SingleTestResult[] SingleTestResults { get; }
public MethodResult(string methodName, SingleTestResult[] singleTestResults)
{
_methodName = methodName;
SingleTestResults = singleTestResults;
}
internal string GetMethodName()
{
return _methodName;
}
internal string GetMeanExecutionTime()
{
if (string.IsNullOrEmpty(_meanExecutionTime))
{
long sumTicks = 0;
int elemetsCount = 0;
foreach (var item in SingleTestResults)
{
if (item == null)
{
continue;
}
sumTicks += item.ElapsedTime.Ticks;
elemetsCount++;
}
_meanExecutionTime = $"{TimeSpan.FromTicks(sumTicks / elemetsCount).TotalMilliseconds} ms";
}
return _meanExecutionTime;
}
internal string GetMinExecutionTime()
{
if (string.IsNullOrEmpty(_minExecutionTime))
{
double minTime = double.MaxValue;
foreach (var item in SingleTestResults)
{
if (item == null)
{
continue;
}
if (item.ElapsedTime.TotalMilliseconds < minTime)
{
minTime = item.ElapsedTime.TotalMilliseconds;
}
}
_minExecutionTime = $"{minTime:N0} ms";
}
return _minExecutionTime;
}
internal string GetMaxExecutionTime()
{
if (string.IsNullOrEmpty(_maxExecutionTime))
{
double maxTime = 0;
foreach (var item in SingleTestResults)
{
if (item == null)
{
continue;
}
if (item.ElapsedTime.TotalMilliseconds > maxTime)
{
maxTime = item.ElapsedTime.TotalMilliseconds;
}
}
_maxExecutionTime = $"{maxTime:N0} ms";
}
return _maxExecutionTime;
}
}
}

Просмотреть файл

@ -0,0 +1,70 @@
using nanoFramework.Benchmark.Helpers;
using nanoFramework.Benchmark.Result.Attributes;
using System.Collections;
using System.Reflection;
namespace nanoFramework.Benchmark.Result
{
public class SingleBenchmarkResult
{
public MethodResult[] MethodResults { get; }
public string ClassName { get; }
public int ItterationCount { get; }
public SingleBenchmarkResult(MethodResult[] methodResults, string className, int itterationCount)
{
MethodResults = methodResults;
ClassName = className;
ItterationCount = itterationCount;
}
[Display("MethodName")]
public string MethodName(int index)
{
return MethodResults[index].GetMethodName();
}
[Display("ItterationCount")]
public string ItterationCountMethod()
{
return ItterationCount.ToString();
}
[Display("Mean")]
public string MeanExecutionTime(int index)
{
return MethodResults[index].GetMeanExecutionTime();
}
[Display("Max")]
public string MaxExecutionTime(int index)
{
return MethodResults[index].GetMaxExecutionTime();
}
[Display("Min")]
public string MinExecutionTime(int index)
{
return MethodResults[index].GetMinExecutionTime();
}
// TODO: Move to helper class
internal static MethodInfo[] GetDataToDisplay()
{
var tempList = new ArrayList();
var allMethods = typeof(SingleBenchmarkResult).GetMethods(BindingFlags.Public | BindingFlags.Instance);
foreach (var method in allMethods)
{
var displayAttrib = ReflectionHelpers.GetFirstOrDefaultAttribute(method, typeof(DisplayAttribute));
if (displayAttrib == null)
{
continue;
}
tempList.Add(method);
}
return ArrayListHelper.ConvertFromArrayListToMethodInfoArray(tempList);
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@
using System;
namespace nanoFramework.Benchmark.Result
{
public sealed class SingleTestResult
{
public TimeSpan ElapsedTime { get; }
public SingleTestResult(TimeSpan elapsedTime)
{
ElapsedTime = elapsedTime;
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
using System;
namespace nanoFramework.Benchmark
{
[AttributeUsage(AttributeTargets.Method)]
public class SetupAttribute : Attribute
{
}
}

Просмотреть файл

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<NanoFrameworkProjectSystemPath>$(MSBuildExtensionsPath)\nanoFramework\v1.0\</NanoFrameworkProjectSystemPath>
</PropertyGroup>
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectTypeGuids>{11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<ProjectGuid>b2827cb3-4bcd-40c0-a82d-5ab476e84ce4</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<FileAlignment>512</FileAlignment>
<RootNamespace>nanoFramework.Benchmark</RootNamespace>
<AssemblyName>nanoFramework.Benchmark</AssemblyName>
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
</PropertyGroup>
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
<ItemGroup>
<Compile Include="Attributes\Abstract\ParserAttribute.cs" />
<Compile Include="Attributes\CsvParserAttribute.cs" />
<Compile Include="Attributes\ConsoleParserAttribute.cs" />
<Compile Include="BenchmarkAttribute.cs" />
<Compile Include="BenchmarkRunner.cs" />
<Compile Include="Attributes\DebugLoggerAttribute.cs" />
<Compile Include="Helpers\ArrayListHelper.cs" />
<Compile Include="Helpers\SettingsHelper.cs" />
<Compile Include="Helpers\StringArrayExtensions.cs" />
<Compile Include="Attributes\ItterationCountAttribute.cs" />
<Compile Include="Attributes\Abstract\LoggerAttribute.cs" />
<Compile Include="Parser\Abstract\BaseTextParser.cs" />
<Compile Include="Parser\ConsoleParser.cs" />
<Compile Include="Parser\CsvParser.cs" />
<Compile Include="Parser\IResultParser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Result\Attributes\DisplayAttribute.cs" />
<Compile Include="Result\MethodResult.cs" />
<Compile Include="Result\SingleBenchmarkResult.cs" />
<Compile Include="Result\SingleTestResult.cs" />
<Compile Include="SetupAttribute.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="mscorlib">
<HintPath>..\packages\nanoFramework.CoreLibrary.1.12.0\lib\mscorlib.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.Logging">
<HintPath>..\packages\nanoFramework.Logging.1.1.2\lib\nanoFramework.Logging.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.Runtime.Native">
<HintPath>..\packages\nanoFramework.Runtime.Native.1.5.4\lib\nanoFramework.Runtime.Native.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.System.Collections">
<HintPath>..\packages\nanoFramework.System.Collections.1.4.0\lib\nanoFramework.System.Collections.dll</HintPath>
</Reference>
<Reference Include="nanoFramework.System.Text">
<HintPath>..\packages\nanoFramework.System.Text.1.2.3\lib\nanoFramework.System.Text.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
<ProjectExtensions>
<ProjectCapabilities>
<ProjectConfigurationsDeclaredAsItems />
</ProjectCapabilities>
</ProjectExtensions>
</Project>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="nanoFramework.CoreLibrary" version="1.12.0" targetFramework="netnano1.0" />
<package id="nanoFramework.Logging" version="1.1.2" targetFramework="netnano1.0" />
<package id="nanoFramework.Runtime.Native" version="1.5.4" targetFramework="netnano1.0" />
<package id="nanoFramework.System.Collections" version="1.4.0" targetFramework="netnano1.0" />
<package id="nanoFramework.System.Text" version="1.2.3" targetFramework="netnano1.0" />
</packages>