private void SetupChart()
        {
            model = new PlotModel()
            {
            };

            var linearAxis = new OxyPlot.Axes.LinearAxis()
            {
                MinimumPadding = 0,
                MaximumPadding = 0.1,
            };

            var axis = new OxyPlot.Axes.CategoryAxis()
            {
            };

            axis.Labels.AddRange(new[] { "Video A", "Video B" });

            model.Axes.Add(linearAxis);
            model.Axes.Add(axis);

            series = new ErrorColumnSeries()
            {
                StrokeThickness = 1,
            };

            model.Series.Add(series);

            chart.Children.Add(new PlotView()
            {
                Model  = model,
                Width  = chart.Width,
                Height = chart.Height,
            });
        }
Пример #2
0
        Task RenderErrorColumn(Series plot)
        {
            var errorColumn = new ErrorColumnSeries();

            foreach (var item in plot.Data)
            {
                errorColumn.Items.Add(new ColumnItem(item.Value, item.CategoryIndex));
            }
            OxyplotModel.Series.Add(errorColumn);

            return(Task.CompletedTask);
        }
        public static PlotModel GetErrorColumnSeries()
        {
            var model = new PlotModel("ErrorColumnSeries")
            {
                LegendPlacement = LegendPlacement.Outside,
                LegendPosition = LegendPosition.BottomCenter,
                LegendOrientation = LegendOrientation.Horizontal,
                LegendBorderThickness = 0
            };

            var s1 = new ErrorColumnSeries { Title = "Series 1", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1 };
            s1.Items.Add(new ErrorColumnItem { Value = 25, Error = 2 });
            s1.Items.Add(new ErrorColumnItem { Value = 137, Error = 25 });
            s1.Items.Add(new ErrorColumnItem { Value = 18, Error = 4 });
            s1.Items.Add(new ErrorColumnItem { Value = 40, Error = 29 });

            var s2 = new ErrorColumnSeries { Title = "Series 2", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1 };
            s2.Items.Add(new ErrorColumnItem { Value = 35, Error = 20 });
            s2.Items.Add(new ErrorColumnItem { Value = 17, Error = 7 });
            s2.Items.Add(new ErrorColumnItem { Value = 118, Error = 44 });
            s2.Items.Add(new ErrorColumnItem { Value = 49, Error = 29 });

            var categoryAxis = new CategoryAxis { Position = AxisPosition.Bottom };
            categoryAxis.Labels.Add("Category A");
            categoryAxis.Labels.Add("Category B");
            categoryAxis.Labels.Add("Category C");
            categoryAxis.Labels.Add("Category D");

            var valueAxis = new LinearAxis(AxisPosition.Left) { MinimumPadding = 0, MaximumPadding = 0.06, AbsoluteMinimum = 0 };
            model.Series.Add(s1);
            model.Series.Add(s2);
            model.Axes.Add(categoryAxis);
            model.Axes.Add(valueAxis);

            return model;
        }
        public static PlotModel GetErrorColumnSeries()
        {
            var model = new PlotModel
            {
                Title                 = "ErrorColumnSeries",
                LegendPlacement       = LegendPlacement.Outside,
                LegendPosition        = LegendPosition.BottomCenter,
                LegendOrientation     = LegendOrientation.Horizontal,
                LegendBorderThickness = 0
            };

            var s1 = new ErrorColumnSeries {
                Title = "Series 1", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1
            };

            s1.Items.Add(new ErrorColumnItem {
                Value = 25, Error = 2
            });
            s1.Items.Add(new ErrorColumnItem {
                Value = 137, Error = 25
            });
            s1.Items.Add(new ErrorColumnItem {
                Value = 18, Error = 4
            });
            s1.Items.Add(new ErrorColumnItem {
                Value = 40, Error = 29
            });

            var s2 = new ErrorColumnSeries {
                Title = "Series 2", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1
            };

            s2.Items.Add(new ErrorColumnItem {
                Value = 35, Error = 20
            });
            s2.Items.Add(new ErrorColumnItem {
                Value = 17, Error = 7
            });
            s2.Items.Add(new ErrorColumnItem {
                Value = 118, Error = 44
            });
            s2.Items.Add(new ErrorColumnItem {
                Value = 49, Error = 29
            });

            var categoryAxis = new CategoryAxis {
                Position = AxisPosition.Bottom
            };

            categoryAxis.Labels.Add("Category A");
            categoryAxis.Labels.Add("Category B");
            categoryAxis.Labels.Add("Category C");
            categoryAxis.Labels.Add("Category D");

            var valueAxis = new LinearAxis {
                Position = AxisPosition.Left, MinimumPadding = 0, MaximumPadding = 0.06, AbsoluteMinimum = 0
            };

            model.Series.Add(s1);
            model.Series.Add(s2);
            model.Axes.Add(categoryAxis);
            model.Axes.Add(valueAxis);

            return(model);
        }
Пример #5
0
        //bool showXAxis = true, showYAxix = true, isZoomable = true, isMovable = true;
        //IPlotType chart;
        //public bool ShowXAxis
        //{
        //    get => showXAxis;
        //    set
        //    {
        //        if (showXAxis == value) return;
        //        showXAxis = value;
        //    }
        //}
        //public bool ShowYAxix
        //{
        //    get => showYAxix;
        //    set
        //    {
        //        if (showYAxix == value) return;
        //        showYAxix = value;
        //    }
        //}
        //public bool IsZoomable
        //{
        //    get => isZoomable;
        //    set
        //    {
        //        if (isZoomable == value) return;
        //        isZoomable = value;
        //    }
        //}
        //public bool IsMovable
        //{
        //    get => isMovable;
        //    set
        //    {
        //        if (isMovable == value) return;
        //        isMovable = value;
        //    }
        //}

        public async Task Add(PlotModel plotModel)
        {
            this.oxyplotModel       = new OxyPlot.PlotModel();
            this.oxyplotModel.Title = plotModel.Title;
            foreach (var chart in plotModel.Series)
            {
                if (chart is TwoColorArea) //it should be placed before Area if clause
                {
                    var twoColorAreaSeries = new TwoColorAreaSeries
                    {
                        Color  = ChartColor.CastToOxyColor(((TwoColorArea)chart).Color),
                        Color2 = ChartColor.CastToOxyColor(((TwoColorArea)chart).Color2),
                        Limit  = ((TwoColorArea)chart).Limit
                    };
                    foreach (var item in ((TwoColorArea)chart).Data)
                    {
                        twoColorAreaSeries.Points.Add(item);
                    }
                    this.oxyplotModel.Series.Add(twoColorAreaSeries);
                }
                else if (chart is Area)
                {
                    var areaSeries = new AreaSeries();
                    foreach (var point in ((Area)chart).Data)
                    {
                        areaSeries.Points.Add(point);
                    }
                    this.oxyplotModel.Series.Add(areaSeries);
                }
                else if (chart is TwoColorLine)//it should be placed before line if clause
                {
                    var twoColorLineSeries = new TwoColorLineSeries
                    {
                        Color  = ChartColor.CastToOxyColor(((TwoColorLine)chart).Color),
                        Color2 = ChartColor.CastToOxyColor(((TwoColorLine)chart).Color2),
                        Limit  = ((TwoColorLine)chart).Limit
                    };
                    foreach (var item in ((TwoColorLine)chart).Data)
                    {
                        twoColorLineSeries.Points.Add(item);
                    }
                    this.oxyplotModel.Series.Add(twoColorLineSeries);
                }
                else if (chart is Line)
                {
                    var lineSeries = new LineSeries
                    {
                        MarkerType   = MarkerType.Circle,
                        MarkerSize   = 4,
                        MarkerStroke = OxyColors.White
                    };

                    foreach (var point in ((Line)chart).Data)
                    {
                        lineSeries.Points.Add(point);
                    }
                    this.oxyplotModel.Series.Add(lineSeries);
                }
                else if (chart is Pie)
                {
                    var pieSeries = new PieSeries();
                    foreach (var slice in ((Pie)chart).Data)
                    {
                        pieSeries.Slices.Add(slice);
                    }
                    this.oxyplotModel.Series.Add(pieSeries);
                }
                else if (chart is Bar)
                {
                    var barSeries = new BarSeries();
                    foreach (var item in ((Bar)chart).Data)
                    {
                        barSeries.Items.Add(item);
                    }
                    this.oxyplotModel.Series.Add(barSeries);
                }
                else if (chart is ErrorColumn)
                {
                    var errorColumn = new ErrorColumnSeries();
                    foreach (ErrorColumnItem item in ((ErrorColumn)chart).Data)
                    {
                        errorColumn.Items.Add((OxyPlot.Series.ErrorColumnItem)item);
                    }
                    this.oxyplotModel.Series.Add(errorColumn);
                }
                else if (chart is Column)
                {
                    var barSeries = new ColumnSeries();
                    foreach (var item in ((Column)chart).Data)
                    {
                        barSeries.Items.Add(item);
                    }
                    this.oxyplotModel.Series.Add(barSeries);
                }
                else if (chart is Box)
                {
                    var boxSeries = new BoxPlotSeries();
                    foreach (var item in ((Box)chart).Data)
                    {
                        boxSeries.Items.Add(item);
                    }
                    this.oxyplotModel.Series.Add(boxSeries);
                }
                else if (chart is Contour)
                {
                    var contourSeries = new ContourSeries
                    {
                        Data = ((Contour)chart).Data,
                        ColumnCoordinates = ((Contour)chart).ColumnCoordinates,
                        RowCoordinates    = ((Contour)chart).RowCoordinates
                    };
                    this.oxyplotModel.Series.Add(contourSeries);
                }
                else if (chart is RectangleBar)
                {
                    var rectangleBarSeries = new RectangleBarSeries
                    {
                        Title = ((RectangleBar)chart).Title
                    };
                    foreach (var item in ((RectangleBar)chart).Data)
                    {
                        rectangleBarSeries.Items.Add(item);
                    }
                    this.oxyplotModel.Series.Add(rectangleBarSeries);
                }
                else if (chart is CandleStick)
                {
                    var candleStickSeries = new CandleStickSeries();
                    foreach (var item in ((CandleStick)chart).Data)
                    {
                        candleStickSeries.Items.Add(item);
                    }
                    this.oxyplotModel.Series.Add(candleStickSeries);
                }
                else if (chart is HeatMap)
                {
                    var heatMapSeries = new HeatMapSeries()
                    {
                        Data         = ((HeatMap)chart).Data,
                        X0           = ((HeatMap)chart).X0,
                        X1           = ((HeatMap)chart).X1,
                        Y0           = ((HeatMap)chart).Y0,
                        Y1           = ((HeatMap)chart).Y1,
                        Interpolate  = ((HeatMap)chart).Interpolate,
                        RenderMethod = ((HeatMap)chart).RenderMethod
                    };
                    this.oxyplotModel.Series.Add(heatMapSeries);
                }
                else if (chart is HighLow)
                {
                    var highLowSeries = new HighLowSeries();
                    foreach (var item in ((HighLow)chart).Data)
                    {
                        highLowSeries.Items.Add(item);
                    }
                    this.oxyplotModel.Series.Add(highLowSeries);
                }
                else if (chart is IntervalBar)
                {
                    var intervalBarSeries = new IntervalBarSeries();
                    foreach (var item in ((IntervalBar)chart).Data)
                    {
                        intervalBarSeries.Items.Add(item);
                    }
                    this.oxyplotModel.Series.Add(intervalBarSeries);
                }
                else if (chart is Scatter)
                {
                    var scatterSeries = new ScatterSeries();
                    foreach (var item in ((Scatter)chart).Data)
                    {
                        scatterSeries.Points.Add(item);
                    }
                    this.oxyplotModel.Series.Add(scatterSeries);
                }
            }
            foreach (var axis in plotModel.Axes)
            {
                this.oxyplotModel.Axes.Add(axis);
            }
        }
Пример #6
0
    public static void Main(string[] args)
    {
        var benchmarksnames = new string[0];
        var timeout         = Int32.MaxValue;
        var pausetime       = false;
        var monoexe         = String.Empty;
        var graph           = "graph.svg";
        var loadresultfrom  = new string [0];
        var nograph         = false;
        var norun           = false;
        var counter         = String.Empty;
        var geomean         = false;
        var minimum         = Double.NaN;

        var optindex = 0;

        for (; optindex < args.Length; ++optindex)
        {
            if (args [optindex] == "-b" || args [optindex] == "--benchmarks")
            {
                benchmarksnames = args [++optindex].Split(',').Select(s => s.Trim()).Union(benchmarksnames).ToArray();
            }
            else if (args [optindex] == "-t" || args [optindex] == "--timeout")
            {
                timeout = Int32.Parse(args [++optindex]) * 1000;
                timeout = timeout == 0 ? Int32.MaxValue : timeout;
                // } else if (args [optindex] == "-p" || args [optindex] == "--pause-time") {
                //  pausetime = Boolean.Parse (args [++optindex]);
            }
            else if (args [optindex] == "-m" || args [optindex] == "--mono-exe")
            {
                monoexe = args [++optindex];
            }
            else if (args [optindex] == "-g" || args [optindex] == "--graph")
            {
                graph = args [++optindex];
            }
            else if (args [optindex] == "-l" || args [optindex] == "--load-from")
            {
                loadresultfrom = args [++optindex].Split(',').Select(s => s.Trim()).Union(loadresultfrom).ToArray();
            }
            else if (args [optindex] == "--no-graph")
            {
                nograph = true;
            }
            else if (args [optindex] == "--no-run")
            {
                norun = true;
            }
            else if (args [optindex] == "-c" || args [optindex] == "--counter")
            {
                counter = args [++optindex];
            }
            else if (args [optindex] == "--geomean")
            {
                geomean = true;
            }
            else if (args [optindex] == "--minimum")
            {
                minimum = Double.Parse(args [++optindex]);
            }
            else if (args [optindex].StartsWith("--help"))
            {
                UsageAndExit();
            }
            else if (args [optindex] == "--")
            {
                optindex += 1;
                break;
            }
            else if (args [optindex].StartsWith("-"))
            {
                Console.Error.WriteLine("unknown parameter {0}", args [optindex]);
                UsageAndExit();
            }
            else
            {
                break;
            }
        }

        if (norun && nograph)
        {
            UsageAndExit("You cannot disable run and graph at the same time", 1);
        }

        if (!norun && loadresultfrom.Length > 0)
        {
            UsageAndExit("You cannot load a run from a file if you run the benchmarks", 1);
        }

        if (args.Length - optindex < (norun ? 3 : 4))
        {
            UsageAndExit(null, 1);
        }

        var testsdir      = args [optindex++];
        var resultsdir    = args [optindex++];
        var benchmarksdir = args [optindex++];
        var configfiles   = args.Skip(optindex).ToArray();

        List <Result> results = new List <Result> ();

        if (norun)
        {
            foreach (var resultfile in (loadresultfrom.Length > 0 ? loadresultfrom : Directory.EnumerateFiles(resultsdir, "*.json")))
            {
                var result = Result.LoadFrom(resultfile);
                if (result == null)
                {
                    throw new InvalidDataException(String.Format("Cannot load Result from {0}", resultfile));
                }

                if (results.Any(r => r.Benchmark.Equals(result.Benchmark) && r.Config.Equals(result.Config)))
                {
                    continue;
                }

                // If we have been given configuration names, the result's
                // configuration must match one of them.
                if (configfiles.Length > 0 && !configfiles.Any(n => n == result.Config.Name))
                {
                    continue;
                }

                results.Add(result);
            }
        }
        else
        {
            var configs = configfiles.Select(c => Config.LoadFrom(c)).ToList();

            /* Run or load the benchmarks */
            foreach (var benchmark in Benchmark.LoadAllFrom(benchmarksdir, benchmarksnames).OrderBy(b => b.Name))
            {
                foreach (var config in configs)
                {
                    var runfileprefix = String.Join("_", benchmark.Name, config.Name, "");
                    var version       = String.Empty;

                    /* Run the benchmarks */
                    if (config.Count <= 0)
                    {
                        throw new ArgumentOutOfRangeException(String.Format("configs [\"{0}\"].Count <= 0", config.Name));
                    }

                    Console.Out.WriteLine("Running benchmark \"{0}\" with config \"{1}\"", benchmark.Name, config.Name);

                    var info = new ProcessStartInfo()
                    {
                        WorkingDirectory       = Path.Combine(testsdir, benchmark.TestDirectory),
                        UseShellExecute        = false,
                        RedirectStandardOutput = true,
                        RedirectStandardError  = true,
                    };

                    if (config.NoMono)
                    {
                        info.FileName         = Path.Combine(info.WorkingDirectory, benchmark.CommandLine [0]);
                        benchmark.CommandLine = benchmark.CommandLine.Skip(1).ToArray();
                    }
                    else
                    {
                        info.FileName = !String.IsNullOrEmpty(monoexe) ? monoexe : !String.IsNullOrEmpty(config.Mono) ? config.Mono : "mono";
                    }

                    foreach (var env in config.MonoEnvironmentVariables)
                    {
                        info.EnvironmentVariables.Add(env.Key, env.Value);
                    }

                    var envvar    = String.Join(" ", config.MonoEnvironmentVariables.Select(kv => kv.Key + "=" + kv.Value));
                    var arguments = String.Join(" ", config.MonoOptions.Union(benchmark.CommandLine));

                    if (!config.NoMono)
                    {
                        /* Run without timing with --version */
                        info.Arguments = "--version " + arguments;

                        Console.Out.WriteLine("\t$> {0} {1} {2}", envvar, info.FileName, info.Arguments);

                        var process1 = Process.Start(info);
                        version = Task.Run(() => new StreamReader(process1.StandardOutput.BaseStream).ReadToEnd()).Result;
                        var versionerror = Task.Run(() => new StreamReader(process1.StandardError.BaseStream).ReadToEnd());

                        process1.WaitForExit();
                    }
                    else
                    {
                        info.Arguments = arguments;
                    }

                    /* Run with timing */
                    if (!config.NoMono)
                    {
                        info.Arguments = "--stats " + arguments;
                    }

                    var result = new Result {
                        DateTime  = DateTime.Now,
                        Benchmark = benchmark,
                        Config    = config,
                        Version   = version,
                        Timedout  = false,
                        Runs      = new Result.Run [config.Count]
                    };

                    for (var i = 0; i < config.Count + 1; ++i)
                    {
                        Console.Out.WriteLine("\t$> {0} {1} {2}", envvar, info.FileName, info.Arguments);
                        Console.Out.Write("\t\t-> {0} ", i == 0 ? "[dry run]" : String.Format("({0}/{1})", i, config.Count));

                        timeout = benchmark.Timeout > 0 ? benchmark.Timeout : timeout;

                        var sw = Stopwatch.StartNew();

                        var process = Process.Start(info);
                        var stdout  = Task.Factory.StartNew(() => new StreamReader(process.StandardOutput.BaseStream).ReadToEnd(), TaskCreationOptions.LongRunning);
                        var stderr  = Task.Factory.StartNew(() => new StreamReader(process.StandardError.BaseStream).ReadToEnd(), TaskCreationOptions.LongRunning);
                        var success = process.WaitForExit(timeout < 0 ? -1 : (Math.Min(Int32.MaxValue / 1000, timeout) * 1000));

                        sw.Stop();

                        if (!success)
                        {
                            process.Kill();
                        }

                        Console.Out.WriteLine(success ? sw.ElapsedMilliseconds.ToString() + "ms" : "timeout!");

                        // skip first one
                        if (i > 0)
                        {
                            result.Runs [i - 1] = new Result.Run {
                                WallClockTime = success ? TimeSpan.FromTicks(sw.ElapsedTicks) : TimeSpan.Zero,
                                Output        = success ? stdout.Result : null,
                                Error         = success ? stderr.Result : null
                            };

                            result.Timedout = result.Timedout || !success;
                        }

                        process.Close();
                    }

                    // FIXME: implement pausetime
                    if (pausetime)
                    {
                        throw new NotImplementedException();
                    }

                    result.StoreTo(Path.Combine(resultsdir, runfileprefix + DateTime.Now.ToString("s").Replace(':', '-') + ".json"));
                    results.Add(result);
                }
            }
        }

        /* Generate the graph */
        if (!nograph)
        {
            Console.WriteLine("Generate graph in \"{0}\"", graph);
            var plot = new PlotModel {
                LegendPlacement       = LegendPlacement.Outside,
                LegendPosition        = LegendPosition.TopCenter,
                LegendOrientation     = LegendOrientation.Horizontal,
                LegendBorderThickness = 0,
                Padding       = new OxyThickness(10, 0, 0, 75),
                DefaultColors = XamarinColors,
            };

            var categoryaxis = new CategoryAxis {
                Position = AxisPosition.Bottom, Angle = 90
            };
            var valueaxis = new LinearAxis {
                Position           = AxisPosition.Left,
                MinorGridlineStyle = LineStyle.Automatic,
                MajorGridlineStyle = LineStyle.Automatic,
            };

            plot.Axes.Add(categoryaxis);
            plot.Axes.Add(valueaxis);

            var benchmarks = results
                             .GroupBy(r => r.Benchmark)
                             .Select(benchmark => {
                var benchmarkconfigs = benchmark.Select(r => r.Config).ToArray();

                Debug.Assert(benchmarkconfigs.Length == benchmarkconfigs.Distinct().Count(), "There are duplicate configs for benchmark \"{0}\" : {1}",
                             benchmark.Key.Name, String.Join(", ", benchmarkconfigs.OrderBy(c => c.Name).Select(c => c.Name)));

                if (benchmark.Any(r => r.Runs.Any(ru => ru.WallClockTime == TimeSpan.Zero)))
                {
                    Console.WriteLine("Don't have data for benchmark \"{0}\" in all configs - removing", benchmark.Key.Name);
                    return(null);
                }

                double[][] values;

                try {
                    counter = String.IsNullOrWhiteSpace(counter) ? "Time" : counter;
                    values  = benchmark.Select(r => r.Runs.Select(ru => ExtractCounterValue(counter, ru)).ToArray()).ToArray();
                } catch (FormatException) {
                    Console.Error.WriteLine("Could not parse value for counter \"{1}\"", counter);
                    return(null);
                }

                double[] means  = values.Select(vs => vs.Sum() / vs.Length).ToArray();
                double[] errors = values.Zip(means, (vs, m) => m - vs.Min()).ToArray();

                if (minimum != Double.NaN && means.Any(m => m < minimum))
                {
                    Console.WriteLine("Mean value for benchmark \"{0}\" below minimum - removing", benchmark.Key.Name);
                    return(null);
                }

                double ratio = means.ElementAt(0);

                double[] nmeans  = means.Select(m => m / ratio).ToArray();
                double[] nerrors = errors.Select(e => e / ratio).ToArray();

                Debug.Assert(benchmarkconfigs.Length == nmeans.Length);
                Debug.Assert(benchmarkconfigs.Length == nerrors.Length);

                return(new { Benchmark = benchmark.Key, Configs = benchmarkconfigs, NormalizedMeans = nmeans, NormalizedErrors = nerrors });
            })
                             .Where(t => t != null)
                             .ToArray();

            foreach (var n in benchmarks.Select(v => v.Benchmark.Name).Distinct())
            {
                categoryaxis.Labels.Add(n);
            }

            var configs = benchmarks
                          .SelectMany(b => b.Configs.Select((c, i) => new { Benchmark = b.Benchmark, Config = c, NormalizedMean = b.NormalizedMeans [i], NormalizedError = b.NormalizedErrors [i] }))
                          .GroupBy(v => v.Config);

            foreach (var config in configs)
            {
                var serie = new ErrorColumnSeries {
                    Title = config.Key.Name, LabelFormatString = "{0:F2}", StrokeThickness = 1
                };

                var nmeans  = config.Select(c => c.NormalizedMean).ToArray();
                var nerrors = config.Select(c => c.NormalizedError).ToArray();

                for (int i = 0, l = nmeans.Length; i < l; ++i)
                {
                    serie.Items.Add(new ErrorColumnItem {
                        Value = nmeans [i], Error = nerrors [i], Color = OxyColors.Automatic
                    });
                }

                plot.Series.Add(serie);
            }

            if (geomean)
            {
                var geomeanserie = new ColumnSeries {
                    Title = "geomean", LabelFormatString = "{0:F2}", StrokeThickness = 1
                };

                foreach (var v in benchmarks)
                {
                    geomeanserie.Items.Add(new ColumnItem {
                        Value = Math.Pow(v.NormalizedMeans.Aggregate(1d, (a, m) => m * a), 1d / v.NormalizedMeans.Length),
                        Color = OxyColors.Automatic,
                    });
                }

                plot.Series.Add(geomeanserie);
            }

            valueaxis.AbsoluteMinimum = valueaxis.Minimum = benchmarks.Aggregate(Double.MaxValue, (a, v) => Math.Min(a, v.NormalizedMeans.Zip(v.NormalizedErrors, (m, e) => m - e).Min())) * 0.99;
            valueaxis.AbsoluteMaximum = valueaxis.Maximum = benchmarks.Aggregate(Double.MinValue, (a, v) => Math.Max(a, v.NormalizedMeans.Zip(v.NormalizedErrors, (m, e) => m + e).Max())) * 1.01;

            using (var stream = new FileStream(graph, FileMode.Create))
                SvgExporter.Export(plot, stream, 1024, 768, true);
        }
    }