blob: e9c3f187240af609ddc0a3f3254772772b018ec5 [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.
#ifndef IMPALA_UTIL_PRETTY_PRINTER_H
#define IMPALA_UTIL_PRETTY_PRINTER_H
#include <boost/algorithm/string.hpp>
#include <cmath>
#include <iomanip>
#include <limits>
#include <sstream>
#include "gen-cpp/RuntimeProfile_types.h"
#include "util/cpu-info.h"
#include "util/template-util.h"
/// Truncate a double to offset decimal places.
#define DOUBLE_TRUNCATE(val, offset) floor(val * pow(10, offset)) / pow(10, offset)
namespace impala {
/// Methods for printing numeric values with optional units, or other types with an
/// applicable operator<<.
class PrettyPrinter {
public:
static std::string Print(bool value, TUnit::type ignored, bool verbose = false) {
std::stringstream ss;
ss << std::boolalpha << value;
return ss.str();
}
/// Prints the 'value' in a human friendly format depending on the data type.
/// i.e. for bytes: 3145728 -> 3MB
/// If verbose is true, this also prints the raw value (before unit conversion) for
/// types where this is applicable.
template<typename T>
static ENABLE_IF_ARITHMETIC(T, std::string)
Print(T value, TUnit::type unit, bool verbose = false) {
std::stringstream ss;
ss.flags(std::ios::fixed);
switch (unit) {
case TUnit::NONE: {
ss << value;
return ss.str();
}
case TUnit::UNIT: {
std::string unit;
double output = GetUnit(value, &unit);
if (unit.empty()) {
ss << value;
} else {
ss << std::setprecision(PRECISION) << output << unit;
}
if (verbose) ss << " (" << value << ")";
break;
}
case TUnit::UNIT_PER_SECOND: {
std::string unit;
double output = GetUnit(value, &unit);
if (output == 0) {
ss << "0";
} else {
ss << std::setprecision(PRECISION) << output << " " << unit << "/sec";
}
break;
}
case TUnit::CPU_TICKS: {
if (value < CpuInfo::cycles_per_ms()) {
ss << std::setprecision(PRECISION) << (value / 1000.) << "K clock cycles";
} else {
value /= CpuInfo::cycles_per_ms();
PrintTimeMs(value, &ss);
}
break;
}
case TUnit::TIME_NS: {
PrintTimeNs(value, &ss);
break;
}
case TUnit::TIME_US: {
PrintTimeNs(value * THOUSAND, &ss);
break;
}
case TUnit::TIME_MS: {
PrintTimeMs(value, &ss);
break;
}
case TUnit::TIME_S: {
PrintTimeMs(value * THOUSAND, &ss);
break;
}
case TUnit::BYTES: {
std::string unit;
double output = GetByteUnit(value, &unit);
if (output == 0) {
ss << "0";
} else {
ss << std::setprecision(PRECISION) << output << " " << unit;
if (verbose) ss << " (" << value << ")";
}
break;
}
case TUnit::BYTES_PER_SECOND: {
std::string unit;
double output = GetByteUnit(value, &unit);
ss << std::setprecision(PRECISION) << output << " " << unit << "/sec";
break;
}
case TUnit::DOUBLE_VALUE: {
double output = *reinterpret_cast<double*>(&value);
ss << std::setprecision(PRECISION) << output << " ";
break;
}
// Printed as integer values
case TUnit::BASIS_POINTS: {
DCHECK_LE(value, 10000);
ss << (value / 100);
break;
}
default:
DCHECK(false) << "Unsupported TUnit: " << value;
break;
}
return ss.str();
}
/// For non-arithmetics, just write the value as a string and return it.
//
/// TODO: There's no good is_string equivalent, so there's a needless copy for strings
/// here.
template<typename T>
static ENABLE_IF_NOT_ARITHMETIC(T, std::string)
Print(const T& value, TUnit::type unit) {
std::stringstream ss;
ss << std::boolalpha << value;
return ss.str();
}
/// Utility method to print an iterable type to a stringstream like [v1, v2, v3]
template <typename I>
static void PrintStringList(const I& iterable, TUnit::type unit,
std::stringstream* out) {
std::vector<std::string> strings;
for (typename I::const_iterator it = iterable.begin(); it != iterable.end(); ++it) {
std::stringstream ss;
ss << PrettyPrinter::Print(*it, unit);
strings.push_back(ss.str());
}
(*out) <<"[" << boost::algorithm::join(strings, ", ") << "]";
}
/// Convenience method
static std::string PrintBytes(int64_t value) {
return PrettyPrinter::Print(value, TUnit::BYTES);
}
private:
static const int PRECISION = 2;
static const int TIME_NS_PRECISION = 3;
static const int64_t KILOBYTE = 1024;
static const int64_t MEGABYTE = KILOBYTE * 1024;
static const int64_t GIGABYTE = MEGABYTE * 1024;
static const int64_t SECOND = 1000;
static const int64_t MINUTE = SECOND * 60;
static const int64_t HOUR = MINUTE * 60;
static const int64_t THOUSAND = 1000;
static const int64_t MILLION = THOUSAND * 1000;
static const int64_t BILLION = MILLION * 1000;
template <typename T>
static double GetByteUnit(T value, std::string* unit) {
if (value == 0) {
*unit = "";
return value;
} else if (value >= GIGABYTE || (value < 0 && value <= -GIGABYTE)) {
*unit = "GB";
return value / (double) GIGABYTE;
} else if (value >= MEGABYTE || (value < 0 && value <= -MEGABYTE)) {
*unit = "MB";
return value / (double) MEGABYTE;
} else if (value >= KILOBYTE || (value < 0 && value <= -KILOBYTE)) {
*unit = "KB";
return value / (double) KILOBYTE;
} else {
*unit = "B";
return value;
}
}
template <typename T>
static double GetUnit(T value, std::string* unit) {
if (value >= BILLION) {
*unit = "B";
return value / (1. * BILLION);
} else if (value >= MILLION) {
*unit = "M";
return value / (1. * MILLION);
} else if (value >= THOUSAND) {
*unit = "K";
return value / (1. * THOUSAND);
} else {
*unit = "";
return value;
}
}
/// Utility to perform integer modulo if T is integral, otherwise to use fmod().
template <typename T>
static ENABLE_IF_INTEGRAL(T, int64_t) Mod(const T& value, const int modulus) {
return value % modulus;
}
template <typename T>
static ENABLE_IF_FLOAT(T, double) Mod(const T& value, int modulus) {
return fmod(value, 1. * modulus);
}
/// Pretty print the value (time in ns) to ss.
template <typename T>
static void PrintTimeNs(T value, std::stringstream* ss) {
*ss << std::setprecision(TIME_NS_PRECISION);
if (value >= BILLION) {
/// If the time is over a second, print it up to ms.
value /= MILLION;
PrintTimeMs(value, ss);
} else if (value >= MILLION) {
/// if the time is over a ms, print it up to microsecond in the unit of ms.
*ss << DOUBLE_TRUNCATE(static_cast<double>(value) / MILLION, TIME_NS_PRECISION)
<< "ms";
} else if (value > THOUSAND) {
/// if the time is over a microsecond, print it using unit microsecond.
*ss << DOUBLE_TRUNCATE(static_cast<double>(value) / THOUSAND, TIME_NS_PRECISION)
<< "us";
} else {
*ss << DOUBLE_TRUNCATE(value, TIME_NS_PRECISION) << "ns";
}
}
/// Print the value (time in ms) to ss.
template <typename T>
static void PrintTimeMs(T value, std::stringstream* ss) {
DCHECK_GE(value, static_cast<T>(0));
if (value == 0) {
*ss << "0";
} else {
bool hour = false;
bool minute = false;
bool second = false;
if (value >= HOUR) {
*ss << static_cast<int64_t>(value / HOUR) << "h";
value = Mod(value, HOUR);
hour = true;
}
if (value >= MINUTE) {
*ss << static_cast<int64_t>(value / MINUTE) << "m";
value = Mod(value, MINUTE);
minute = true;
}
if (!hour && value >= SECOND) {
*ss << static_cast<int64_t>(value / SECOND) << "s";
value = Mod(value, SECOND);
second = true;
}
if (!hour && !minute) {
if (second) *ss << std::setw(3) << std::setfill('0');
*ss << static_cast<int64_t>(value) << "ms";
}
}
}
};
}
#endif