blob: 0f5744dd2aa1d776e351034fbdf71bc0e790ca7b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Apache.Ignite.Benchmarks.Result
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
internal class BenchmarkFileResultWriter : IBenchmarkResultWriter
{
/** Probe file name: percentile. */
private const string ProbePercentile = "PercentileProbe.csv";
/** Probe file name: throughput. */
private const string ProbeThroughput = "ThroughputLatencyProbe.csv";
/** File header: percentile probe. */
private const string HdrPercentile = "**\"Latency, microseconds\",\"Operations, %\"";
/** File header: throughput probe. */
private const string HdrThroughput =
"**\"Time, sec\",\"Operations/sec (more is better)\",\"Latency, nsec (less is better)\"";
/** Cached culture. */
private static readonly CultureInfo Culture = new CultureInfo("en-US");
/** Writer. */
private readonly Writer _writer = new Writer();
/** Benchmarks. */
private volatile IDictionary<string, BenchmarkTask> _benchmarks;
/** <inheritdoc/> */
public void Initialize(BenchmarkBase benchmark, ICollection<string> opNames)
{
_benchmarks = new Dictionary<string, BenchmarkTask>(opNames.Count);
// 1. Create folder for results.
var now = DateTime.Now;
var suffix = "-t=" + benchmark.Threads + "-d=" + benchmark.Duration +
"-w=" + benchmark.Warmup;
var path = benchmark.ResultFolder + "\\" + now.ToString("yyyyMMdd-HHmmss", Culture) + "-" +
benchmark.GetType().Name + suffix;
if (Directory.Exists(path))
Directory.Delete(path, true);
Directory.CreateDirectory(path);
var dateStr = "--Created " + DateTime.Now.ToString("yyyyMMdd-HHmmss", Culture);
var cfgStr = "--Benchmark config: " + benchmark;
// 2. For each operation create separate folder and initialize probe files there.
foreach (var opName in opNames)
{
var opDesc = benchmark.GetType().Name + "-" + opName + suffix;
var opPath = path + "\\" + opDesc;
Directory.CreateDirectory(opPath);
var task = new BenchmarkTask(opPath + "\\" + ProbePercentile,
opPath + "\\" + ProbeThroughput);
_benchmarks[opName] = task;
File.AppendAllText(task.FilePercentile, dateStr + "\n");
File.AppendAllText(task.FilePercentile, cfgStr + "\n");
File.AppendAllText(task.FilePercentile, "--Description: " + opDesc + "\n");
File.AppendAllText(task.FilePercentile, "@@" + benchmark.GetType().Name + "\n");
File.AppendAllText(task.FilePercentile, HdrPercentile + "\n");
File.AppendAllText(task.FileThroughput, dateStr + "\n");
File.AppendAllText(task.FileThroughput, cfgStr + "\n");
File.AppendAllText(task.FileThroughput, "--Description: " + opDesc + "\n");
File.AppendAllText(task.FileThroughput, "@@" + benchmark.GetType().Name + "\n");
File.AppendAllText(task.FileThroughput, HdrThroughput + "\n");
}
// 3. Start writer thread.
new Thread(_writer.Run).Start();
}
/** <inheritdoc/> */
public void WriteThroughput(string opName, long duration, long opCount)
{
var benchmark = _benchmarks[opName];
if (!benchmark.FirstThroughput())
{
var sec = benchmark.Counter();
var ops = (float) opCount;
var latency = (float) duration*1000000000/(opCount*Stopwatch.Frequency);
var text = sec + "," + ops.ToString("F2", Culture) + "," + latency.ToString("F2", Culture);
Write0(benchmark.FileThroughput, text);
}
}
/** <inheritdoc/> */
public void WritePercentiles(string opName, long interval, long[] slots)
{
var benchmark = _benchmarks[opName];
var total = slots.Sum();
long time = 0;
foreach (var slot in slots)
{
var val = (float) slot/total;
Write0(benchmark.FilePercentile, time + "," + val.ToString("F2", Culture));
time += interval;
}
}
/** <inheritdoc/> */
public void Commit()
{
_writer.Add(new StopTask().Run);
_writer.AwaitStop();
}
/// <summary>
/// Internal write routine.
/// </summary>
/// <param name="path">Path.</param>
/// <param name="text">Text.</param>
private void Write0(string path, string text)
{
_writer.Add(_ => new WriteTask(path, text).Run());
}
/// <summary>
/// Writer.
/// </summary>
private class Writer
{
/** Queue. */
private readonly BlockingCollection<Task> _queue = new BlockingCollection<Task>();
/** Stop flag. */
private volatile bool _stop;
/// <summary>
/// Runner method.
/// </summary>
public void Run()
{
while (!_stop)
{
var task = _queue.Take();
task.Invoke(this);
}
}
/// <summary>
/// Add task to queue.
/// </summary>
/// <param name="task">Task.</param>
public void Add(Task task)
{
_queue.Add(task);
}
/// <summary>
/// Mark writer stopped.
/// </summary>
public void Stop()
{
lock (this)
{
_stop = true;
Monitor.PulseAll(this);
}
}
/// <summary>
/// Await for writer stop.
/// </summary>
/// <returns></returns>
public void AwaitStop()
{
lock (this)
{
while (!_stop)
Monitor.Wait(this);
}
}
}
/// <summary>
/// Writer task.
/// </summary>
/// <param name="writer">Writer.</param>
private delegate void Task(Writer writer);
/// <summary>
/// Writer write task.
/// </summary>
private class WriteTask
{
/** File name. */
private readonly string _fileName;
/** Text. */
private readonly string _text;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="fileName">File name.</param>
/// <param name="text">Text.</param>
public WriteTask(string fileName, string text)
{
_fileName = fileName;
_text = text;
}
/// <summary>
/// Runner.
/// </summary>
public void Run()
{
File.AppendAllText(_fileName, _text + "\n");
}
}
/// <summary>
/// Writer stop task.
/// </summary>
private class StopTask
{
/// <summary>
/// Runner.
/// </summary>
public void Run(Writer writer)
{
writer.Stop();
}
}
/// <summary>
/// Benchmark task.
/// </summary>
private class BenchmarkTask
{
/** Counter. */
private int _ctr;
/** First throughput flag. */
private int _first;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="filePercentile">File percentile.</param>
/// <param name="fileThroughput">File throughput.</param>
public BenchmarkTask(string filePercentile, string fileThroughput)
{
FilePercentile = filePercentile;
FileThroughput = fileThroughput;
}
/// <summary>
/// File percentile.
/// </summary>
public string FilePercentile { get; private set; }
/// <summary>
/// File throughput.
/// </summary>
public string FileThroughput { get; private set; }
/// <summary>
/// Get counter value.
/// </summary>
/// <returns>Counter value.</returns>
public int Counter()
{
return Interlocked.Increment(ref _ctr);
}
/// <summary>
/// Check whether this is the first throughput task.
/// </summary>
/// <returns>True if first.</returns>
public bool FirstThroughput()
{
return Interlocked.CompareExchange(ref _first, 1, 0) == 0;
}
}
}
}