blob: 07795a51f43d9804599cc9414844754d305770be [file] [log] [blame]
// Copyright (C) 2021 The Android Open Source Project
//
// Licensed 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.
#include <ditto/result.h>
#include <ditto/statistics.h>
#include <ditto/timespec_utils.h>
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
const int kTimeSampleDisplayWidth = 11; // this width is used displaying a time sample value
const int kTableWidth = 164; // table width; can be adjusted in case of longer instruction paths
const char* kTableDivider = " | "; // table character divider
const int kMaxHistogramHeight = 20; // used for normalizing the histogram (represents the
// maximum height of the histogram)
const int kMaxHistogramWidth = 50; // used for normalizing the histogram (represents the
// maximum width of the histogram)
const char kCsvDelimiter = ','; // delimiter used for .csv files
static int bin_size; // bin size corresponding to the normalization
// of the Oy axis of the histograms
namespace dittosuite {
Result::Result(const std::string& name, const std::vector<timespec>& time_samples)
: name_(name), time_samples_(time_samples) {}
void Result::AddSubResult(std::unique_ptr<Result> result) {
sub_results_.push_back(std::move(result));
}
void Result::Analyse() {
min_ = StatisticsGetMin(time_samples_);
max_ = StatisticsGetMax(time_samples_);
mean_ = StatisticsGetMean(time_samples_);
median_ = StatisticsGetMedian(time_samples_);
sd_ = NsToTimespec(StatisticsGetSd(time_samples_));
}
std::string Result::ComputeNextInstructionPath(const std::string& instruction_path) {
return instruction_path + (instruction_path != "" ? "/" : "") + name_;
}
void Result::Print(const std::string& instruction_path) {
std::string next_instruction_path = ComputeNextInstructionPath(instruction_path);
std::cout << next_instruction_path << std::endl;
std::cout << "Min: " << min_.tv_sec << "s, " << min_.tv_nsec << "ns" << std::endl;
std::cout << "Max: " << max_.tv_sec << "s, " << max_.tv_nsec << "ns" << std::endl;
std::cout << "Mean: " << mean_.tv_sec << "s, " << mean_.tv_nsec << "ns" << std::endl;
std::cout << "Median: " << median_.tv_sec << "s, " << median_.tv_nsec << "ns" << std::endl;
std::cout << "SD: " << sd_.tv_sec << "s, " << sd_.tv_nsec << "ns" << std::endl << std::endl;
for (const auto& sub_result : sub_results_) {
sub_result->Print(next_instruction_path);
}
}
void PrintTableBorder() {
std::cout << std::endl;
for (int i = 0; i < kTableWidth; i++) std::cout << "-";
std::cout << std::endl;
}
void PrintStatisticsTableHeader() {
std::cout << "\x1b[1m"; // beginning of bold
PrintTableBorder();
std::cout << "| "; // beginning of table row
std::cout << std::setw(70) << std::left << "Instruction name";
std::cout << kTableDivider;
std::cout << std::setw(15) << std::left << " Min";
std::cout << kTableDivider;
std::cout << std::setw(15) << std::left << " Max";
std::cout << kTableDivider;
std::cout << std::setw(15) << std::left << " Mean";
std::cout << kTableDivider;
std::cout << std::setw(15) << std::left << " Median";
std::cout << kTableDivider;
std::cout << std::setw(15) << std::left << " SD";
std::cout << kTableDivider;
PrintTableBorder();
std::cout << "\x1b[0m"; // ending of bold
}
void PrintTimespecInTable(const timespec& t) {
std::cout << std::setw(13) << TimespecToNs(t) << "ns";
}
// Recursive function to print one row at a time
// of statistics table content (the instruction path, min, max and mean).
void Result::PrintStatisticsTableContent(const std::string& instruction_path) {
std::cout << "| "; // started new row
std::string next_instruction_path = ComputeNextInstructionPath(instruction_path);
int subinstruction_level =
std::count(next_instruction_path.begin(), next_instruction_path.end(), '/');
// If the instruction path name contains too many subinstrions,
// print only the last 2 preceded by "../".
if (subinstruction_level > 2) {
std::size_t first_truncate_pos = next_instruction_path.find('/');
next_instruction_path = ".." + next_instruction_path.substr(first_truncate_pos);
}
std::cout << std::setw(70) << std::left << next_instruction_path << std::right;
std::cout << kTableDivider;
PrintTimespecInTable(min_);
std::cout << kTableDivider;
PrintTimespecInTable(max_);
std::cout << kTableDivider;
PrintTimespecInTable(mean_);
std::cout << kTableDivider;
PrintTimespecInTable(median_);
std::cout << kTableDivider;
PrintTimespecInTable(sd_);
std::cout << kTableDivider; // ended current row
PrintTableBorder();
for (const auto& sub_result : sub_results_) {
sub_result->PrintStatisticsTableContent(next_instruction_path);
}
}
void Result::PrintStatisticsTable() {
PrintStatisticsTableHeader();
PrintStatisticsTableContent("");
}
// makes (normalized) histogram from vector
void Result::MakeHistogramFromVector(const std::vector<int>& freq_vector, const int& min_value) {
std::cout.width(kTimeSampleDisplayWidth - 3);
std::cout << "Time(" << time_unit_.name << ") |";
std::cout << " Normalized number of time samples";
std::cout << std::endl;
for (int i = 0; i <= kMaxHistogramWidth + 15; i++) std::cout << "-";
std::cout << std::endl;
int sum = 0;
int max_frequency = *std::max_element(freq_vector.begin(), freq_vector.end());
for (unsigned int i = 0; i < freq_vector.size(); i++) {
std::cout.width(kTimeSampleDisplayWidth);
std::cout << min_value + bin_size * i << kTableDivider;
for (int j = 0; j < freq_vector[i] * kMaxHistogramWidth / max_frequency; j++) std::cout << "x";
std::cout << " {" << freq_vector[i] << "}";
sum += freq_vector[i];
std::cout << std::endl;
}
std::cout << "Total samples: { " << sum << " }" << std::endl;
std::cout << std::endl;
}
// makes and returns the normalized frequency vector
std::vector<int> Result::ComputeNormalizedFrequencyVector() {
int64_t min_value = TimespecToNs(min_) / time_unit_.dividing_factor;
std::vector<int> freq_vector(kMaxHistogramHeight, 0);
for (auto time_sample : time_samples_) {
freq_vector[(TimespecToNs(time_sample) / time_unit_.dividing_factor - min_value) / bin_size]++;
}
return freq_vector;
}
Result::TimeUnit Result::GetTimeUnit(const timespec& min_value) {
TimeUnit result;
if (TimespecToNs(min_value) <= 1e7) {
// time unit in nanoseconds
result.dividing_factor = 1;
result.name = "ns";
} else if (TimespecToNs(min_value) <= 1e10) {
// time unit in microseconds
result.dividing_factor = 1e3;
result.name = "us";
} else if (TimespecToNs(min_value) <= 1e13) {
// time unit in milliseconds
result.dividing_factor = 1e6;
result.name = "ms";
} else {
// time unit in seconds
result.dividing_factor = 1e9;
result.name = "s";
}
return result;
}
void Result::PrintHistograms(const std::string& instruction_path) {
std::string next_instruction_path = ComputeNextInstructionPath(instruction_path);
std::cout << std::endl;
std::cout << "\x1b[1m"; // beginning of bold
std::cout << "Instruction path: " << next_instruction_path;
std::cout << "\x1b[0m" << std::endl; // ending of bold
std::cout << std::endl;
time_unit_ = GetTimeUnit(min_);
int64_t min_value = TimespecToNs(min_) / time_unit_.dividing_factor;
int64_t max_value = TimespecToNs(max_) / time_unit_.dividing_factor;
bin_size = (max_value - min_value) / kMaxHistogramHeight + 1;
std::vector<int> freq_vector = ComputeNormalizedFrequencyVector();
MakeHistogramFromVector(freq_vector, min_value);
std::cout << std::endl << std::endl;
for (const auto& sub_result : sub_results_) {
sub_result->PrintHistograms(next_instruction_path);
}
}
// Recursive function to print one row at a time using the .csv stream given as a parameter
// of statistics table content (the instruction path, min, max, mean and SD).
void Result::PrintStatisticInCsv(std::ostream& csv_stream, const std::string& instruction_path) {
std::string next_instruction_path = ComputeNextInstructionPath(instruction_path);
csv_stream << next_instruction_path << kCsvDelimiter;
csv_stream << TimespecToNs(min_) << kCsvDelimiter;
csv_stream << TimespecToNs(max_) << kCsvDelimiter;
csv_stream << TimespecToNs(mean_) << kCsvDelimiter;
csv_stream << TimespecToNs(median_) << kCsvDelimiter;
csv_stream << TimespecToNs(sd_);
csv_stream << std::endl; // ending of row
for (const auto& sub_result : sub_results_) {
sub_result->PrintStatisticInCsv(csv_stream, next_instruction_path);
}
}
void Result::MakeStatisticsCsv() {
std::ostream csv_stream(std::cout.rdbuf());
csv_stream << "Instruction path" << kCsvDelimiter;
csv_stream << "Min" << kCsvDelimiter;
csv_stream << "Max" << kCsvDelimiter;
csv_stream << "Mean" << kCsvDelimiter;
csv_stream << "Median" << kCsvDelimiter;
csv_stream << "SD" << std::endl;
PrintStatisticInCsv(csv_stream, "");
}
} // namespace dittosuite