| /* |
| * 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; |
| } |
| } |
| } |
| } |