QPlainTestLogger: include extra information in each result

RESULT : tst_MyClass::QString_toInt()
     367 nsecs per iteration (total: 3,672,848, iterations: 10000)
     1,019 CPU cycles per iteration, 2.775 GHz (total: 10,193,637, iterations: 10000)
     3,123 instructions per iteration, 3.055 instr/cycle (total: 3,123,000,194, iterations: 1000000)
     536 branch instructions per iteration, 1.459 G/sec (total: 5,360,022, iterations: 10000)

Change-Id: I3c79b7e08fa346988dfefffd17203fff3ff5b774
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
bb10
Thiago Macieira 2022-10-21 18:30:02 -07:00
parent f341e99aab
commit 224d795ec9
1 changed files with 103 additions and 0 deletions

View File

@ -40,6 +40,9 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
namespace {
static const char multiplePrefixes[] = "\0kMGTPE"; // kilo, mega, giga, tera, peta, exa
static const char submultiplePrefixes[] = "afpnum"; // atto, femto, pico, nano, micro, milli
template <int N> struct FixedBufString
{
static constexpr size_t MaxSize = N;
@ -74,6 +77,34 @@ template <int N> struct FixedBufString
used += qsnprintf(buf.data() + used, MaxSize - used + 1, format,
std::forward<Args>(args)...);
}
template <int Power = 1000> void appendScaled(qreal value, const char *unit)
{
char prefix[2] = {};
qreal v = qAbs(value);
qint64 ratio;
if (v < 1 && Power == 1000) {
const char *prefixes = submultiplePrefixes;
ratio = qreal(std::atto::num) / std::atto::den;
while (value * ratio > 1000 && *prefixes) {
++prefixes;
ratio *= 1000;
}
prefix[0] = *prefixes;
} else {
const char *prefixes = multiplePrefixes;
ratio = 1;
while (value > 1000 * ratio) { // yes, even for binary
++prefixes;
ratio *= Power;
}
prefix[0] = *prefixes;
}
// adjust the value by the ratio
value /= ratio;
appendf(", %.3g %s%s", value, prefix, unit);
}
};
} // unnamed namespace
@ -285,7 +316,23 @@ void QPlainTestLogger::printBenchmarkResultsHeader(const QBenchmarkResult &resul
void QPlainTestLogger::printBenchmarkResults(const QList<QBenchmarkResult> &results)
{
using namespace std::chrono;
FixedBufString<1022> buf;
auto findResultFor = [&results](QTest::QBenchmarkMetric metric) -> std::optional<qreal> {
for (const QBenchmarkResult &result : results) {
if (result.measurement.metric == metric)
return result.measurement.value;
}
return std::nullopt;
};
// we need the execution time quite often, so find it first
qreal executionTime = 0;
if (auto ns = findResultFor(QTest::WalltimeNanoseconds))
executionTime = *ns / (1000 * 1000 * 1000);
else if (auto ms = findResultFor(QTest::WalltimeMilliseconds))
executionTime = *ms / 1000;
for (const QBenchmarkResult &result : results) {
buf.clear();
@ -295,6 +342,62 @@ void QPlainTestLogger::printBenchmarkResults(const QList<QBenchmarkResult> &resu
buf.appendf(" %s %s%s", QTest::formatResult(valuePerIteration, significantDigits).constData(),
unitText, result.setByMacro ? " per iteration" : "");
switch (result.measurement.metric) {
case QTest::BitsPerSecond:
// for bits/s, we'll use powers of 10 (1 Mbit/s = 1000 kbit/s = 1000000 bit/s)
buf.appendScaled<1000>(result.measurement.value, "bit/s");
break;
case QTest::BytesPerSecond:
// for B/s, we'll use powers of 2 (1 MB/s = 1024 kB/s = 1048576 B/s)
buf.appendScaled<1024>(result.measurement.value, "B/s");
break;
case QTest::CPUCycles:
case QTest::RefCPUCycles:
if (!qIsNull(executionTime))
buf.appendScaled(result.measurement.value / executionTime, "Hz");
break;
case QTest::Instructions:
if (auto cycles = findResultFor(QTest::CPUCycles)) {
buf.appendf(", %.3f instr/cycle", result.measurement.value / *cycles);
break;
}
Q_FALLTHROUGH();
case QTest::InstructionReads:
case QTest::Events:
case QTest::BytesAllocated:
case QTest::CPUMigrations:
case QTest::BusCycles:
case QTest::StalledCycles:
case QTest::BranchInstructions:
case QTest::BranchMisses:
case QTest::CacheReferences:
case QTest::CacheReads:
case QTest::CacheWrites:
case QTest::CachePrefetches:
case QTest::CacheMisses:
case QTest::CacheReadMisses:
case QTest::CacheWriteMisses:
case QTest::CachePrefetchMisses:
case QTest::ContextSwitches:
case QTest::PageFaults:
case QTest::MinorPageFaults:
case QTest::MajorPageFaults:
case QTest::AlignmentFaults:
case QTest::EmulationFaults:
if (!qIsNull(executionTime))
buf.appendScaled(result.measurement.value / executionTime, "/sec");
break;
case QTest::FramesPerSecond:
case QTest::CPUTicks:
case QTest::WalltimeMilliseconds:
case QTest::WalltimeNanoseconds:
break; // no additional information
}
Q_ASSERT(result.iterations > 0);
buf.appendf(" (total: %s, iterations: %d)\n",
QTest::formatResult(result.measurement.value, significantDigits).constData(),