#region License Information
/* HeuristicLab
* Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using HeuristicLab.Collections;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HEAL.Attic;
using HeuristicLab.PluginInfrastructure;
namespace HeuristicLab.Algorithms.Benchmarks {
[Item("Benchmark Algorithm", "An algorithm to execute performance benchmarks (Linpack, Dhrystone, Whetstone, etc.).")]
[Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 130)]
[StorableType("8E76490C-E5DA-4F09-8E94-10009FC4124D")]
public sealed class BenchmarkAlgorithm : IAlgorithm, IStorableContent {
private CancellationTokenSource cancellationTokenSource;
public string Filename { get; set; }
public string ItemName {
get { return ItemAttribute.GetName(this.GetType()); }
}
public string ItemDescription {
get { return ItemAttribute.GetDescription(this.GetType()); }
}
public Version ItemVersion {
get { return ItemAttribute.GetVersion(this.GetType()); }
}
public static Image StaticItemImage {
get { return HeuristicLab.Common.Resources.VSImageLibrary.Event; }
}
public Image ItemImage {
get {
if (ExecutionState == ExecutionState.Prepared) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutablePrepared;
else if (ExecutionState == ExecutionState.Started) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutableStarted;
else if (ExecutionState == ExecutionState.Paused) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutablePaused;
else if (ExecutionState == ExecutionState.Stopped) return HeuristicLab.Common.Resources.VSImageLibrary.ExecutableStopped;
else return ItemAttribute.GetImage(this.GetType());
}
}
[Storable]
private DateTime lastUpdateTime;
[Storable]
private IBenchmark benchmark;
public IBenchmark Benchmark {
get { return benchmark; }
set {
if (value == null) throw new ArgumentNullException();
benchmark = value;
}
}
[Storable]
private ExecutionState executionState;
public ExecutionState ExecutionState {
get { return executionState; }
private set {
if (executionState != value) {
executionState = value;
OnExecutionStateChanged();
OnItemImageChanged();
}
}
}
[Storable]
private TimeSpan executionTime;
public TimeSpan ExecutionTime {
get { return executionTime; }
private set {
executionTime = value;
OnExecutionTimeChanged();
}
}
[Storable]
private bool storeAlgorithmInEachRun;
public bool StoreAlgorithmInEachRun {
get { return storeAlgorithmInEachRun; }
set {
if (storeAlgorithmInEachRun != value) {
storeAlgorithmInEachRun = value;
OnStoreAlgorithmInEachRunChanged();
}
}
}
[Storable]
private int runsCounter;
[Storable]
private RunCollection runs = new RunCollection();
public RunCollection Runs {
get { return runs; }
private set {
if (value == null) throw new ArgumentNullException();
if (runs != value) {
if (runs != null) DeregisterRunsEvents();
runs = value;
if (runs != null) RegisterRunsEvents();
}
}
}
[Storable]
private ResultCollection results;
public ResultCollection Results {
get { return results; }
}
public Type ProblemType {
get {
// BenchmarkAlgorithm does not have a problem, so return a type which is no problem for sure
return typeof(BenchmarkAlgorithm);
}
}
public IProblem Problem {
get { return null; }
set { throw new NotImplementedException("BenchmarkAlgorithm does not have a problem."); }
}
[Storable]
private string name;
public string Name {
get { return name; }
set {
if (!CanChangeName) throw new NotSupportedException("Name cannot be changed.");
if (!(name.Equals(value) || (value == null) && (name == string.Empty))) {
CancelEventArgs e = value == null ? new CancelEventArgs(string.Empty) : new CancelEventArgs(value);
OnNameChanging(e);
if (!e.Cancel) {
name = value == null ? string.Empty : value;
OnNameChanged();
runs.OptimizerName = name;
}
}
}
}
public bool CanChangeName {
get { return true; }
}
[Storable]
private string description;
public string Description {
get { return description; }
set {
if (!CanChangeDescription) throw new NotSupportedException("Description cannot be changed.");
if (!(description.Equals(value) || (value == null) && (description == string.Empty))) {
description = value == null ? string.Empty : value;
OnDescriptionChanged();
}
}
}
public bool CanChangeDescription {
get { return true; }
}
[Storable]
private ParameterCollection parameters = new ParameterCollection();
public IKeyedItemCollection Parameters {
get { return parameters; }
}
private ReadOnlyKeyedItemCollection readOnlyParameters;
IKeyedItemCollection IParameterizedItem.Parameters {
get {
if (readOnlyParameters == null) readOnlyParameters = parameters.AsReadOnly();
return readOnlyParameters;
}
}
public IEnumerable NestedOptimizers {
get { return Enumerable.Empty(); }
}
#region Parameter Properties
public IConstrainedValueParameter BenchmarkParameter {
get { return (IConstrainedValueParameter)Parameters["Benchmark"]; }
}
private ValueParameter ChunkSizeParameter {
get { return (ValueParameter)Parameters["ChunkSize"]; }
}
private ValueParameter TimeLimitParameter {
get { return (ValueParameter)Parameters["TimeLimit"]; }
}
#endregion
#region Constructors
[StorableConstructor]
private BenchmarkAlgorithm(StorableConstructorFlag _) { }
private BenchmarkAlgorithm(BenchmarkAlgorithm original, Cloner cloner) {
if (original.ExecutionState == ExecutionState.Started) throw new InvalidOperationException(string.Format("Clone not allowed in execution state \"{0}\".", ExecutionState));
cloner.RegisterClonedObject(original, this);
name = original.name;
description = original.description;
parameters = cloner.Clone(original.parameters);
readOnlyParameters = null;
executionState = original.executionState;
executionTime = original.executionTime;
storeAlgorithmInEachRun = original.storeAlgorithmInEachRun;
runsCounter = original.runsCounter;
Runs = cloner.Clone(original.runs);
results = cloner.Clone(original.results);
}
public BenchmarkAlgorithm() {
name = ItemName;
description = ItemDescription;
parameters = new ParameterCollection();
readOnlyParameters = null;
executionState = ExecutionState.Stopped;
executionTime = TimeSpan.Zero;
storeAlgorithmInEachRun = false;
runsCounter = 0;
Runs = new RunCollection() { OptimizerName = name };
results = new ResultCollection();
CreateParameters();
DiscoverBenchmarks();
Prepare();
}
#endregion
private void CreateParameters() {
Parameters.Add(new ValueParameter("ChunkSize", "The size in MB of the chunk data array that is generated.", new IntValue(0)));
Parameters.Add(new ValueParameter("TimeLimit", "The time limit in minutes for a benchmark run (zero means a fixed number of iterations).", new DoubleValue(0)));
}
private void DiscoverBenchmarks() {
var benchmarks = from t in ApplicationManager.Manager.GetTypes(typeof(IBenchmark))
select t;
ItemSet values = new ItemSet();
foreach (var benchmark in benchmarks) {
IBenchmark b = (IBenchmark)Activator.CreateInstance(benchmark);
values.Add(b);
}
string paramName = "Benchmark";
if (!Parameters.ContainsKey(paramName)) {
if (values.Count > 0) {
Parameters.Add(new ConstrainedValueParameter(paramName, "The benchmark which should be executed.", values, values.First(a => a is IBenchmark)));
} else {
Parameters.Add(new ConstrainedValueParameter(paramName, "The benchmark which should be executed.", values));
}
}
}
public IDeepCloneable Clone(Cloner cloner) {
return new BenchmarkAlgorithm(this, cloner);
}
public object Clone() {
return Clone(new Cloner());
}
public override string ToString() {
return Name;
}
public void Prepare() {
if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
results.Clear();
OnPrepared();
}
public void Prepare(bool clearRuns) {
if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
if (clearRuns) runs.Clear();
Prepare();
}
public void Pause() {
if (ExecutionState != ExecutionState.Started)
throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
}
public void Stop() {
if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
cancellationTokenSource.Cancel();
}
public void Start() {
Start(CancellationToken.None);
}
public void Start(CancellationToken cancellationToken) {
cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
OnStarted();
try {
Run(cancellationTokenSource.Token);
} catch (OperationCanceledException) {
} catch (AggregateException ae) {
ae.FlattenAndHandle(new[] { typeof(OperationCanceledException) }, e => OnExceptionOccurred(e));
} catch (Exception e) {
OnExceptionOccurred(e);
}
cancellationTokenSource.Dispose();
cancellationTokenSource = null;
OnStopped();
}
public async Task StartAsync() { await StartAsync(CancellationToken.None); }
public async Task StartAsync(CancellationToken cancellationToken) {
await AsyncHelper.DoAsync(Start, cancellationToken);
}
private void Run(object state) {
CancellationToken cancellationToken = (CancellationToken)state;
lastUpdateTime = DateTime.UtcNow;
System.Timers.Timer timer = new System.Timers.Timer(250);
timer.AutoReset = true;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Start();
try {
Benchmark = (IBenchmark)BenchmarkParameter.ActualValue;
int chunkSize = ((IntValue)ChunkSizeParameter.ActualValue).Value;
if (chunkSize > 0) {
Benchmark.ChunkData = CreateChunkData(chunkSize);
} else if (chunkSize < 0) {
throw new ArgumentException("ChunkSize must not be negativ.");
}
TimeSpan timelimit = TimeSpan.FromMinutes(((DoubleValue)TimeLimitParameter.ActualValue).Value);
if (timelimit.TotalMilliseconds < 0) {
throw new ArgumentException("TimeLimit must not be negativ. ");
}
Benchmark.TimeLimit = timelimit;
Benchmark.Run(cancellationToken, results);
} catch (OperationCanceledException) {
} finally {
timer.Elapsed -= new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Stop();
ExecutionTime += DateTime.UtcNow - lastUpdateTime;
}
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
System.Timers.Timer timer = (System.Timers.Timer)sender;
timer.Enabled = false;
DateTime now = DateTime.UtcNow;
ExecutionTime += now - lastUpdateTime;
lastUpdateTime = now;
timer.Enabled = true;
}
public void CollectResultValues(IDictionary values) {
values.Add("Execution Time", new TimeSpanValue(ExecutionTime));
CollectResultsRecursively("", Results, values);
}
private void CollectResultsRecursively(string path, ResultCollection results, IDictionary values) {
foreach (IResult result in results) {
values.Add(path + result.Name, result.Value);
ResultCollection childCollection = result.Value as ResultCollection;
if (childCollection != null) {
CollectResultsRecursively(path + result.Name + ".", childCollection, values);
}
}
}
public void CollectParameterValues(IDictionary values) {
foreach (IValueParameter param in parameters.OfType()) {
if (param.GetsCollected && param.Value != null) values.Add(param.Name, param.Value);
if (param.Value is IParameterizedItem) {
Dictionary children = new Dictionary();
((IParameterizedItem)param.Value).CollectParameterValues(children);
foreach (string key in children.Keys)
values.Add(param.Name + "." + key, children[key]);
}
}
}
private byte[][] CreateChunkData(int megaBytes) {
if (megaBytes <= 0) {
throw new ArgumentException("MegaBytes must be greater than zero", "megaBytes");
}
Random random = new Random();
byte[][] chunk = new byte[megaBytes][];
for (int i = 0; i < chunk.Length; i++) {
chunk[i] = new byte[1024 * 1024];
random.NextBytes(chunk[i]);
}
return chunk;
}
#region Events
public event EventHandler ExecutionStateChanged;
private void OnExecutionStateChanged() {
EventHandler handler = ExecutionStateChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler ExecutionTimeChanged;
private void OnExecutionTimeChanged() {
EventHandler handler = ExecutionTimeChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler ProblemChanged { add { } remove { } }
public event EventHandler StoreAlgorithmInEachRunChanged;
private void OnStoreAlgorithmInEachRunChanged() {
EventHandler handler = StoreAlgorithmInEachRunChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler Prepared;
private void OnPrepared() {
ExecutionState = ExecutionState.Prepared;
ExecutionTime = TimeSpan.Zero;
foreach (IStatefulItem statefulObject in this.GetObjectGraphObjects().OfType()) {
statefulObject.InitializeState();
}
EventHandler handler = Prepared;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler Started;
private void OnStarted() {
ExecutionState = ExecutionState.Started;
EventHandler handler = Started;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler Paused;
private void OnPaused() {
ExecutionState = ExecutionState.Paused;
EventHandler handler = Paused;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler Stopped;
private void OnStopped() {
ExecutionState = ExecutionState.Stopped;
foreach (IStatefulItem statefulObject in this.GetObjectGraphObjects().OfType()) {
statefulObject.ClearState();
}
runsCounter++;
runs.Add(new Run(string.Format("{0} Run {1}", Name, runsCounter), this));
EventHandler handler = Stopped;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler> ExceptionOccurred;
private void OnExceptionOccurred(Exception exception) {
EventHandler> handler = ExceptionOccurred;
if (handler != null) handler(this, new EventArgs(exception));
}
public event EventHandler> NameChanging;
private void OnNameChanging(CancelEventArgs e) {
var handler = NameChanging;
if (handler != null) handler(this, e);
}
public event EventHandler NameChanged;
private void OnNameChanged() {
var handler = NameChanged;
if (handler != null) handler(this, EventArgs.Empty);
OnToStringChanged();
}
public event EventHandler DescriptionChanged;
private void OnDescriptionChanged() {
var handler = DescriptionChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler ItemImageChanged;
private void OnItemImageChanged() {
EventHandler handler = ItemImageChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler ToStringChanged;
private void OnToStringChanged() {
EventHandler handler = ToStringChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
private void DeregisterRunsEvents() {
runs.CollectionReset -= new CollectionItemsChangedEventHandler(Runs_CollectionReset);
}
private void RegisterRunsEvents() {
runs.CollectionReset += new CollectionItemsChangedEventHandler(Runs_CollectionReset);
}
private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs e) {
runsCounter = runs.Count;
}
#endregion
}
}