Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

319 lines
11 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "test/testsupport/perf_test.h"
#include <stdio.h>
#include <fstream>
#include <set>
#include <sstream>
#include <vector>
#include "rtc_base/checks.h"
#include "rtc_base/critical_section.h"
#include "test/testsupport/perf_test_graphjson_writer.h"
#include "test/testsupport/perf_test_histogram_writer.h"
ABSL_FLAG(bool,
write_histogram_proto_json,
Reland "Flip histograms to true by default, fix unit in isac_fix_test." This reverts commit c59a3049016f697e05437b91ae494c40cede3506. Reason for revert: Other perf tests greening up, can now land this Original change's description: > Revert "Flip histograms to true by default, fix unit in isac_fix_test." > > This reverts commit 7b201012bc7f7cd95249b8314d0d7ebabe966d8b. > > Reason for revert: Seems to work, but need to get low bw tests working first > > Original change's description: > > Flip histograms to true by default, fix unit in isac_fix_test. > > > > Requires downstream changes for all WebRTC perf tests, and > > a corresponding recipe change so isac_fix_test starts using the new > > flow. > > > > Bug: chromium:1029452 > > Change-Id: I8918fca9bef003d365037c1c6bf7c55747dfed99 > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/170633 > > Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> > > Commit-Queue: Patrik Höglund <phoglund@webrtc.org> > > Cr-Commit-Position: refs/heads/master@{#30906} > > TBR=phoglund@webrtc.org,mbonadei@webrtc.org > > Change-Id: I96c2309cd71be14c5a27b515736a32f1b256453c > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: chromium:1029452 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/171865 > Reviewed-by: Patrik Höglund <phoglund@webrtc.org> > Commit-Queue: Patrik Höglund <phoglund@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#30913} TBR=phoglund@webrtc.org,mbonadei@webrtc.org Change-Id: If39500beeca74b8e0ed9e97724a55529125a2253 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: chromium:1029452 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/171876 Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Commit-Queue: Patrik Höglund <phoglund@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30920}
2020-03-27 13:05:08 +00:00
true,
Add PerfResultsReporter. This is the WebRTC equivalent of testing/perf/perf_result_reporter.h in Chromium. That class was introduced because the PrintResult functions are quite hard to use right. It was easy to mix up metrics, modifiers and stories, for instance. I choose to introduce this new class because I need to create a new API for PrintResult anyway. For instance, the important bool isn't really supported by histograms. Also I would like to restrict units to an enum because you cannot make up your own units anymore. We could also have had a strictly checked string type, but that's bad API design. An enum is better because the compiler will check that the unit is valid rather than at runtime. Furthermore, down the line we can probably make each reporter write protos directly to /tmp and merge them later, instead of having a singleton which writes results at the end and keeps all test results in memory. This abstraction makes it easy to make a clean and simple implementation of just that. Steps: 1) land this 2) start rewriting perf tests to use this class 3) nuke PrintResult functions 4) don't convert units to string, convert directly from Unit to proto::Unit 5) write protos directly from this class (either through a singleton or directly) and nuke the perf results writer abstraction. Bug: chromium:1029452 Change-Id: Ia919c371a69309130c797fdf01ae5bd64345ab2e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168770 Reviewed-by: Artem Titov <titovartem@webrtc.org> Commit-Queue: Patrik Höglund <phoglund@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30599}
2020-02-24 13:16:03 +01:00
"Use the histogram C++ API, which will write Histogram protos "
"instead of Chart JSON. See histogram.proto in third_party/catapult. "
"This flag only has effect if --isolated_script_test_perf_output is "
"specified");
namespace webrtc {
namespace test {
namespace {
template <typename Container>
void OutputListToStream(std::ostream* ostream, const Container& values) {
const char* sep = "";
for (const auto& v : values) {
(*ostream) << sep << v;
sep = ",";
}
}
struct PlottableCounter {
std::string graph_name;
std::string trace_name;
webrtc::SamplesStatsCounter counter;
std::string units;
};
class PlottableCounterPrinter {
public:
PlottableCounterPrinter() : output_(stdout) {}
void SetOutput(FILE* output) {
rtc::CritScope lock(&crit_);
output_ = output;
}
void AddCounter(const std::string& graph_name,
const std::string& trace_name,
const webrtc::SamplesStatsCounter& counter,
const std::string& units) {
rtc::CritScope lock(&crit_);
plottable_counters_.push_back({graph_name, trace_name, counter, units});
}
void Print(const std::vector<std::string>& desired_graphs_raw) const {
std::set<std::string> desired_graphs(desired_graphs_raw.begin(),
desired_graphs_raw.end());
rtc::CritScope lock(&crit_);
for (auto& counter : plottable_counters_) {
if (!desired_graphs.empty()) {
auto it = desired_graphs.find(counter.graph_name);
if (it == desired_graphs.end()) {
continue;
}
}
std::ostringstream value_stream;
value_stream.precision(8);
value_stream << R"({"graph_name":")" << counter.graph_name << R"(",)";
value_stream << R"("trace_name":")" << counter.trace_name << R"(",)";
value_stream << R"("units":")" << counter.units << R"(",)";
if (!counter.counter.IsEmpty()) {
value_stream << R"("mean":)" << counter.counter.GetAverage() << ',';
value_stream << R"("std":)" << counter.counter.GetStandardDeviation()
<< ',';
}
value_stream << R"("samples":[)";
const char* sep = "";
for (const auto& sample : counter.counter.GetTimedSamples()) {
value_stream << sep << R"({"time":)" << sample.time.us() << ','
<< R"("value":)" << sample.value << '}';
sep = ",";
}
value_stream << "]}";
fprintf(output_, "PLOTTABLE_DATA: %s\n", value_stream.str().c_str());
}
}
private:
rtc::CriticalSection crit_;
std::vector<PlottableCounter> plottable_counters_ RTC_GUARDED_BY(&crit_);
FILE* output_ RTC_GUARDED_BY(&crit_);
};
PlottableCounterPrinter& GetPlottableCounterPrinter() {
static PlottableCounterPrinter* printer_ = new PlottableCounterPrinter();
return *printer_;
}
class ResultsLinePrinter {
public:
ResultsLinePrinter() : output_(stdout) {}
void SetOutput(FILE* output) {
rtc::CritScope lock(&crit_);
output_ = output;
}
void PrintResult(const std::string& graph_name,
const std::string& trace_name,
const double value,
const std::string& units,
bool important,
ImproveDirection improve_direction) {
std::ostringstream value_stream;
value_stream.precision(8);
value_stream << value;
PrintResultImpl(graph_name, trace_name, value_stream.str(), std::string(),
std::string(), UnitWithDirection(units, improve_direction),
important);
}
void PrintResultMeanAndError(const std::string& graph_name,
const std::string& trace_name,
const double mean,
const double error,
const std::string& units,
bool important,
ImproveDirection improve_direction) {
std::ostringstream value_stream;
value_stream.precision(8);
value_stream << mean << ',' << error;
PrintResultImpl(graph_name, trace_name, value_stream.str(), "{", "}",
UnitWithDirection(units, improve_direction), important);
}
void PrintResultList(const std::string& graph_name,
const std::string& trace_name,
const rtc::ArrayView<const double> values,
const std::string& units,
const bool important,
webrtc::test::ImproveDirection improve_direction) {
std::ostringstream value_stream;
value_stream.precision(8);
OutputListToStream(&value_stream, values);
PrintResultImpl(graph_name, trace_name, value_stream.str(), "[", "]", units,
important);
}
private:
void PrintResultImpl(const std::string& graph_name,
const std::string& trace_name,
const std::string& values,
const std::string& prefix,
const std::string& suffix,
const std::string& units,
bool important) {
rtc::CritScope lock(&crit_);
// <*>RESULT <graph_name>: <trace_name>= <value> <units>
// <*>RESULT <graph_name>: <trace_name>= {<mean>, <std deviation>} <units>
// <*>RESULT <graph_name>: <trace_name>= [<value>,value,value,...,] <units>
fprintf(output_, "%sRESULT %s: %s= %s%s%s %s\n", important ? "*" : "",
graph_name.c_str(), trace_name.c_str(), prefix.c_str(),
values.c_str(), suffix.c_str(), units.c_str());
}
rtc::CriticalSection crit_;
FILE* output_ RTC_GUARDED_BY(&crit_);
};
ResultsLinePrinter& GetResultsLinePrinter() {
static ResultsLinePrinter* const printer_ = new ResultsLinePrinter();
return *printer_;
}
PerfTestResultWriter& GetPerfWriter() {
if (absl::GetFlag(FLAGS_write_histogram_proto_json)) {
static PerfTestResultWriter* writer = CreateHistogramWriter();
return *writer;
} else {
static PerfTestResultWriter* writer = CreateGraphJsonWriter();
return *writer;
}
}
} // namespace
void ClearPerfResults() {
GetPerfWriter().ClearResults();
}
void SetPerfResultsOutput(FILE* output) {
GetPlottableCounterPrinter().SetOutput(output);
GetResultsLinePrinter().SetOutput(output);
}
std::string GetPerfResults() {
return GetPerfWriter().Serialize();
}
void PrintPlottableResults(const std::vector<std::string>& desired_graphs) {
GetPlottableCounterPrinter().Print(desired_graphs);
}
bool WritePerfResults(const std::string& output_path) {
std::string results = GetPerfResults();
FILE* output;
if (absl::GetFlag(FLAGS_write_histogram_proto_json)) {
output = fopen(output_path.c_str(), "wb");
} else {
output = fopen(output_path.c_str(), "w");
}
if (output == NULL) {
printf("Failed to write to %s.\n", output_path.c_str());
return false;
}
size_t written =
fwrite(results.c_str(), sizeof(char), results.size(), output);
fclose(output);
if (written != results.size()) {
long expected = results.size();
printf("Wrote %zu, tried to write %lu\n", written, expected);
return false;
}
return true;
}
void PrintResult(const std::string& measurement,
const std::string& modifier,
const std::string& trace,
const double value,
const std::string& units,
bool important,
ImproveDirection improve_direction) {
std::string graph_name = measurement + modifier;
RTC_CHECK(std::isfinite(value))
<< "Expected finite value for graph " << graph_name << ", trace name "
<< trace << ", units " << units << ", got " << value;
GetPerfWriter().LogResult(graph_name, trace, value, units, important,
improve_direction);
GetResultsLinePrinter().PrintResult(graph_name, trace, value, units,
important, improve_direction);
}
void PrintResult(const std::string& measurement,
const std::string& modifier,
const std::string& trace,
const SamplesStatsCounter& counter,
const std::string& units,
const bool important,
ImproveDirection improve_direction) {
std::string graph_name = measurement + modifier;
GetPlottableCounterPrinter().AddCounter(graph_name, trace, counter, units);
double mean = counter.IsEmpty() ? 0 : counter.GetAverage();
double error = counter.IsEmpty() ? 0 : counter.GetStandardDeviation();
PrintResultMeanAndError(measurement, modifier, trace, mean, error, units,
important, improve_direction);
}
void PrintResultMeanAndError(const std::string& measurement,
const std::string& modifier,
const std::string& trace,
const double mean,
const double error,
const std::string& units,
bool important,
ImproveDirection improve_direction) {
RTC_CHECK(std::isfinite(mean));
RTC_CHECK(std::isfinite(error));
std::string graph_name = measurement + modifier;
GetPerfWriter().LogResultMeanAndError(graph_name, trace, mean, error, units,
important, improve_direction);
GetResultsLinePrinter().PrintResultMeanAndError(
graph_name, trace, mean, error, units, important, improve_direction);
}
void PrintResultList(const std::string& measurement,
const std::string& modifier,
const std::string& trace,
const rtc::ArrayView<const double> values,
const std::string& units,
bool important,
ImproveDirection improve_direction) {
for (double v : values) {
RTC_CHECK(std::isfinite(v));
}
std::string graph_name = measurement + modifier;
GetPerfWriter().LogResultList(graph_name, trace, values, units, important,
improve_direction);
GetResultsLinePrinter().PrintResultList(graph_name, trace, values, units,
important, improve_direction);
}
} // namespace test
} // namespace webrtc