// ---------------------------------------------------------------------- // File: FreedBytesHistogram.cc // Author: Steven Murray - CERN // ---------------------------------------------------------------------- /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2011 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program 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 .* ************************************************************************/ #include "mgm/tgc/Constants.hh" #include "mgm/tgc/FreedBytesHistogram.hh" #include "mgm/CtaUtils.hh" #include #include EOSTGCNAMESPACE_BEGIN //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ FreedBytesHistogram::FreedBytesHistogram( const std::uint32_t nbBins, const std::uint32_t binWidthSecs, IClock& clock): m_histogram(nbBins, 0), m_startIndex(0), m_binWidthSecs(binWidthSecs), m_clock(clock) { m_lastUpdateTimestamp = m_clock.getTime(); if (0 == nbBins || TGC_FREED_BYTES_HISTOGRAM_MAX_NB_BINS < nbBins) { std::ostringstream msg; msg << __FUNCTION__ << " failed: nbBins is invalid. Value must be > 0 and <= " << TGC_FREED_BYTES_HISTOGRAM_MAX_NB_BINS; throw InvalidNbBins(msg.str()); } if (0 == binWidthSecs || TGC_FREED_BYTES_HISTOGRAM_MAX_BIN_WIDTH_SECS < binWidthSecs) { std::ostringstream msg; msg << __FUNCTION__ << " failed: binWidthSecs is invalid. Value must be > 0 and <= " << TGC_FREED_BYTES_HISTOGRAM_MAX_BIN_WIDTH_SECS; throw InvalidBinWidth(msg.str()); } } //------------------------------------------------------------------------------ // Notify cache that bytes were freed //------------------------------------------------------------------------------ void FreedBytesHistogram::bytesFreed(const uint64_t nbBytes) { std::lock_guard lock(m_mutex); alignHistogramWithNow(); // Update youngest bin m_histogram.at(m_startIndex) += nbBytes; } //------------------------------------------------------------------------------ // Return number of bytes freed in the specified last number of seconds //------------------------------------------------------------------------------ std::uint64_t FreedBytesHistogram::getNbBytesFreedInLastNbSecs(const std::uint32_t lastNbSecs) { std::lock_guard lock(m_mutex); const std::uint32_t nbBins = m_histogram.size(); const std::uint32_t historicalDepth = nbBins * m_binWidthSecs; if (lastNbSecs > m_histogram.size() * m_binWidthSecs) { std::ostringstream msg; msg << __FUNCTION__ << " failed: Cannot go back more than " << historicalDepth << " seconds" ": requested=" << lastNbSecs << ": Try reducing " << TGC_NAME_QRY_PERIOD_SECS; throw TooFarBackInTime(msg.str()); } const size_t nbBinsToTotal = CtaUtils::divideAndRoundUp(lastNbSecs, m_binWidthSecs); alignHistogramWithNow(); uint64_t total = 0; for (size_t binIndexOffset = 0; binIndexOffset < nbBinsToTotal; binIndexOffset++) { const size_t binIndex = (m_startIndex + binIndexOffset) % nbBins; total += m_histogram.at(binIndex); } return total; } //------------------------------------------------------------------------------ // Return the total number of freed bytes //------------------------------------------------------------------------------ std::uint64_t FreedBytesHistogram::getTotalBytesFreed() { std::lock_guard lock(m_mutex); alignHistogramWithNow(); std::uint64_t total = 0; for (size_t i = 0; i < m_histogram.size(); i++) { total += m_histogram.at(i); } return total; } //---------------------------------------------------------------------------- //! Return the number of bytes freed in the specified bin //---------------------------------------------------------------------------- std::uint64_t FreedBytesHistogram::getFreedBytesInBin(const std::uint32_t binIndex) const { std::lock_guard lock(m_mutex); const std::uint32_t maxBinIndex = m_histogram.size() - 1; if (binIndex > maxBinIndex) { std::ostringstream msg; msg << __FUNCTION__ << " failed: binIndex is too large: binIndex=" << binIndex << " maxBinIndex=" << maxBinIndex; throw InvalidBinIndex(msg.str()); } const std::uint32_t circularBinIndex = (m_startIndex + binIndex) % m_histogram.size(); return m_histogram.at(circularBinIndex); } //------------------------------------------------------------------------------ // Slide histogram to the right until the first bin is aligned with now //------------------------------------------------------------------------------ void FreedBytesHistogram::alignHistogramWithNow() { const time_t now = m_clock.getTime(); const time_t ageSecs = now - m_lastUpdateTimestamp; const size_t rawNbBinsToMove = CtaUtils::divideAndRoundToNearest(ageSecs, m_binWidthSecs); const size_t nbBinsToMove = std::min(m_histogram.size(), rawNbBinsToMove); // Move start index backwards in order to slide histigram to the the right m_startIndex = (m_startIndex + m_histogram.size() - nbBinsToMove) % m_histogram.size(); // Zero off out-of-date bins for (size_t i = 0; i < nbBinsToMove; i++) { const size_t binIndex = (m_startIndex + i) % m_histogram.size(); m_histogram.at(binIndex) = 0; } // Update histogram timestamp m_lastUpdateTimestamp = now; } //------------------------------------------------------------------------------ // Set the bin width //------------------------------------------------------------------------------ void FreedBytesHistogram::setBinWidthSecs(const std::uint32_t newBinWidthSecs) { if (0 == newBinWidthSecs || TGC_FREED_BYTES_HISTOGRAM_MAX_BIN_WIDTH_SECS < newBinWidthSecs) { std::ostringstream msg; msg << __FUNCTION__ << " failed: newBinWidthSecs is invalid. Value must be > 0 and <= " << TGC_FREED_BYTES_HISTOGRAM_MAX_BIN_WIDTH_SECS; throw InvalidBinWidth(msg.str()); } std::lock_guard lock(m_mutex); const std::uint32_t nbBins = m_histogram.size(); std::vector tempHistogram(m_histogram.size(), 0); const std::uint32_t newHistoricalDepthSecs = nbBins * newBinWidthSecs; for (std::uint32_t secsAgo = 1; secsAgo <= newHistoricalDepthSecs; secsAgo++) { const std::uint32_t binIndex = (secsAgo - 1) / newBinWidthSecs; std::uint64_t bytesFreedPerSec = 0; try { bytesFreedPerSec = getFreedBytesPerSec(secsAgo); } catch (TooFarBackInTime&) { break; } tempHistogram.at(binIndex) += bytesFreedPerSec; } for (std::uint32_t binIndex = 0; binIndex < nbBins; binIndex++) { m_histogram.at(binIndex) = tempHistogram.at(binIndex); } m_startIndex = 0; m_binWidthSecs = newBinWidthSecs; } //------------------------------------------------------------------------------ // Return bin width in seconds //------------------------------------------------------------------------------ std::uint32_t FreedBytesHistogram::getBinWidthSecs() const { std::lock_guard lock(m_mutex); return m_binWidthSecs; } //------------------------------------------------------------------------------ // Return number of bins //------------------------------------------------------------------------------ std::uint32_t FreedBytesHistogram::getNbBins() const { std::lock_guard lock(m_mutex); return m_histogram.size(); } //------------------------------------------------------------------------------ // Return bytes freed per second during the specified second //------------------------------------------------------------------------------ std::uint64_t FreedBytesHistogram::getFreedBytesPerSec(const std::uint32_t secsAgo) const { if (secsAgo > m_histogram.size() * m_binWidthSecs) { std::ostringstream msg; msg << __FUNCTION__ << " failed: Cannot go back more than " << m_histogram.size() * m_binWidthSecs << " seconds" ": requested=" << secsAgo; throw TooFarBackInTime(msg.str()); } if (0 == secsAgo) { return 0; } const size_t binIndexOffset = (secsAgo - 1) / m_binWidthSecs; const size_t binIndex = (m_startIndex + binIndexOffset) % m_histogram.size(); const uint64_t freedBytes = m_histogram.at(binIndex); const uint64_t bytesPerSec = CtaUtils::divideAndRoundToNearest(freedBytes, m_binWidthSecs); return bytesPerSec; } EOSTGCNAMESPACE_END