mitsuba/src/libcore/statistics.cpp

256 lines
7.6 KiB
C++

/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2011 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/mitsuba.h>
#include <mitsuba/core/statistics.h>
#include <mitsuba/core/lock.h>
MTS_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
// Statistics collection
// -----------------------------------------------------------------------
bool ProgressReporter::m_enabled = true;
ProgressReporter::ProgressReporter(const std::string &title, long long total, const void *ptr)
: m_title(title), m_total(total), m_value(0), m_percentage(-1), m_fillPos(0), m_ptr(ptr) {
m_fillSize = (int) (PROGRESS_MSG_SIZE - title.length() - 3);
SAssert(m_fillSize > 0);
for (int i=0; i<m_fillSize; i++)
m_string[i] = ' ';
m_string[m_fillSize] = '\0';
m_timer = new Timer();
m_lastMs = 0;
}
void ProgressReporter::setEnabled(bool value) {
m_enabled = value;
}
void ProgressReporter::reset() {
for (int i=0; i<m_fillSize; i++)
m_string[i] = ' ';
m_timer->reset();
m_lastMs = 0;
m_value = 0;
m_percentage = -1;
m_fillPos = 0;
}
void ProgressReporter::update(long long value) {
if (!m_enabled)
return;
value = std::min(std::max(value, (long long) 0), (long long) m_total);
Float perc = (value * 100.0f) / m_total;
unsigned int curMs = m_timer->getMilliseconds();
m_value = value;
if (value == m_total || (
curMs - m_lastMs > 1000)) {
m_percentage = (int) perc;
int fillEnd = (int) ((value * m_fillSize) / m_total);
Float time = curMs / 1000.0f;
Float remaining = (time*m_total) / value - time;
for (int i=m_fillPos; i<fillEnd; i++)
m_string[i] = '+';
m_fillPos = fillEnd;
std::ostringstream oss;
std::string eta = timeString(remaining);
oss << '\r' << m_title << ": [" << m_string << "] (";
oss << timeString(time) << ", ETA: "
<< eta << ") \b\b";
Thread::getThread()->getLogger()->logProgress(
perc, m_title, oss.str(), eta, m_ptr);
m_lastMs = curMs;
}
}
StatsCounter::StatsCounter(const std::string &cat, const std::string &name, EStatsType type, uint64_t initial, uint64_t base)
: m_category(cat), m_name(name), m_type(type) {
m_value = (CacheLineCounter *) allocAligned(sizeof(CacheLineCounter) * NUM_COUNTERS);
m_base = (CacheLineCounter *) allocAligned(sizeof(CacheLineCounter) * NUM_COUNTERS);
memset(m_value, 0, sizeof(CacheLineCounter) * NUM_COUNTERS);
memset(m_base, 0, sizeof(CacheLineCounter) * NUM_COUNTERS);
#if defined(WIN32) && !defined(WIN64)
if (sizeof(LONG) != sizeof(uint32_t)) {
cerr << "Internal error: sizeof(LONG) != sizeof(uint32_t)!" << endl;
exit(-1);
}
m_value[0].value = (LONG) initial;
m_base[0].value = (LONG) base;
#else
m_value[0].value = initial;
m_base[0].value = base;
#endif
Statistics::getInstance()->registerCounter(this);
}
StatsCounter::~StatsCounter() {
freeAligned(m_value);
freeAligned(m_base);
}
bool StatsCounter::operator<(const StatsCounter &v) const {
if (getCategory() == v.getCategory())
return getName() < v.getName();
return getCategory() < v.getCategory();
}
ref<Statistics> Statistics::m_instance = new Statistics();
void Statistics::staticInitialization() {
SAssert(sizeof(CacheLineCounter) == 128);
}
void Statistics::staticShutdown() {
m_instance = NULL;
}
Statistics::Statistics() {
m_mutex = new Mutex();
}
void Statistics::registerCounter(const StatsCounter *ctr) {
m_counters.push_back(ctr);
}
void Statistics::logPlugin(const std::string &name, const std::string &descr) {
m_plugins.push_back(std::pair<std::string, std::string>(name, descr));
}
void Statistics::printStats() {
SLog(EInfo, "Statistics: \n%s", getStats().c_str());
}
std::string Statistics::getStats() {
std::ostringstream oss;
m_mutex->lock();
oss << "------------------------------------------------------------" << std::endl;
oss << " * Loaded plugins :" << std::endl;
if (m_plugins.size() == 0) {
oss << " none." << std::endl;
} else {
std::sort(m_plugins.begin(), m_plugins.end());
for (unsigned int i=0; i<m_plugins.size(); i++)
oss << " - " << m_plugins[i].first << " [" << m_plugins[i].second
<< "]" << endl;
}
std::string suffixesNumber[] = { "", " K", " M", " G", " T" };
std::string suffixesByte[] = { " B", " KiB", " MiB", " GiB", " TiB" };
std::string category = "";
std::sort(m_counters.begin(), m_counters.end(), compareCategory());
int statsEntries = 0;
for (size_t i=0; i<m_counters.size(); ++i) {
const StatsCounter *counter = m_counters[i];
char temp[128];
float value = (float) counter->getValue();
float baseValue = (float) counter->getBase();
int suffixIndex = 0, suffixIndex2 = 0;
if ((counter->getType() != EPercentage && value == 0) ||
(counter->getType() == EPercentage && baseValue == 0))
continue;
if (category != counter->getCategory()) {
category = counter->getCategory();
oss << std::endl << " * " << category << " :" << std::endl;
}
switch (counter->getType()) {
case ENumberValue:
while (value > 1000.0f) {
value /= 1000.0f;
suffixIndex++;
}
if (value - std::floor(value) < 0.001f)
snprintf(temp, sizeof(temp), " - %s : %.0f%s", counter->getName().c_str(), value, suffixesNumber[suffixIndex].c_str());
else
snprintf(temp, sizeof(temp), " - %s : %.3f%s", counter->getName().c_str(), value, suffixesNumber[suffixIndex].c_str());
break;
case EByteCount:
while (value > 1024.0f) {
value /= 1024.0f;
suffixIndex++;
}
if (value - std::floor(value) < 0.001f)
snprintf(temp, sizeof(temp), " - %s : %.0f%s", counter->getName().c_str(), value, suffixesByte[suffixIndex].c_str());
else
snprintf(temp, sizeof(temp), " - %s : %.3f%s", counter->getName().c_str(), value, suffixesByte[suffixIndex].c_str());
break;
case EPercentage: {
Float value2 = value, value3 = baseValue;
while (value2 > 1000.0f) {
value2 /= 1000.0f;
suffixIndex++;
}
while (value3 > 1000.0f) {
value3 /= 1000.0f;
suffixIndex2++;
}
snprintf(temp, sizeof(temp), " - %s : %.1f %% (%.1f%s of %.1f%s)",
counter->getName().c_str(), baseValue == 0 ? (Float) 0 : value/baseValue * 100,
value2, suffixesNumber[suffixIndex].c_str(),
value3, suffixesNumber[suffixIndex2].c_str());
break;
}
case EAverage: {
Float avg = value / (Float) baseValue;
Float value2 = value, value3 = baseValue;
while (value2 > 1000.0f) {
value2 /= 1000.0f;
suffixIndex++;
}
while (value3 > 1000.0f) {
value3 /= 1000.0f;
suffixIndex2++;
}
snprintf(temp, sizeof(temp), " - %s : %.1f (%.1f%s / %.1f%s)",
counter->getName().c_str(), avg,
value2, suffixesNumber[suffixIndex].c_str(),
value3, suffixesNumber[suffixIndex2].c_str());
break;
}
default:
Log(EError, "Unknown counter type!");
}
oss << temp << std::endl;
++statsEntries;
}
if (statsEntries == 0) {
oss << " * Statistics:" << std::endl
<< " none." << std::endl;
}
oss << "------------------------------------------------------------";
m_mutex->unlock();
return oss.str();
}
MTS_IMPLEMENT_CLASS(Statistics, false, Object)
MTS_NAMESPACE_END