Skip to content

Commit 51eda36

Browse files
authored
[ML] Track memory usage in CHierarchicalResultsNormalizer (#2831)
This PR makes CHierarchicalResultsNormalizer to track its memory usage. Fixes #2244
1 parent 853d54e commit 51eda36

19 files changed

+189
-25
lines changed

bin/normalize/Main.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include <model/CAnomalyDetectorModelConfig.h>
2929

30+
#include <api/CAnomalyJobConfig.h>
3031
#include <api/CCsvInputParser.h>
3132
#include <api/CCsvOutputWriter.h>
3233
#include <api/CIoManager.h>
@@ -144,8 +145,12 @@ int main(int argc, char** argv) {
144145
ioMgr.outputStream());
145146
}()};
146147

148+
// Initialize memory limits with default values.
149+
// This is fine as the normalizer doesn't use the memory limit.
150+
ml::model::CLimits limits{false};
151+
147152
// This object will do the work
148-
ml::api::CResultNormalizer normalizer{modelConfig, *outputWriter};
153+
ml::api::CResultNormalizer normalizer{modelConfig, *outputWriter, limits};
149154

150155
// Restore state
151156
if (!quantilesStateFile.empty()) {

docs/CHANGELOG.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030

3131
== {es} version 9.1.0
3232

33+
=== Enhancements
34+
35+
* Track memory used in the hierarchical results normalizer. (See {ml-pull}2831[#2831].)
36+
3337
=== Bug Fixes
3438

3539
== {es} version 9.0.0

include/api/CAnomalyJob.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ class API_EXPORT CAnomalyJob : public CDataProcessor {
425425
//! be pruned, i.e. those which are so old as to be effectively dead.
426426
void pruneAllModels(std::size_t buckets = 0) const;
427427

428+
const model::CHierarchicalResultsNormalizer& normalizer() const;
429+
428430
private:
429431
//! The job ID
430432
std::string m_JobId;

include/api/CResultNormalizer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ class API_EXPORT CResultNormalizer {
8181

8282
public:
8383
CResultNormalizer(const model::CAnomalyDetectorModelConfig& modelConfig,
84-
CSimpleOutputWriter& outputWriter);
84+
CSimpleOutputWriter& outputWriter,
85+
model::CLimits& limits);
8586

8687
//! Initialise the system change normalizer
8788
bool initNormalizer(const std::string& stateFileName);

include/model/CHierarchicalResultsAggregator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#ifndef INCLUDED_ml_model_CHierarchicalResultsAggregator_h
1313
#define INCLUDED_ml_model_CHierarchicalResultsAggregator_h
1414

15+
#include <core/CMemoryDef.h>
16+
1517
#include <model/CDetectorEqualizer.h>
1618
#include <model/CHierarchicalResultsLevelSet.h>
1719
#include <model/ImportExport.h>

include/model/CHierarchicalResultsLevelSet.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#ifndef INCLUDED_ml_model_CHierarchicalResultsLevelSet_h
1313
#define INCLUDED_ml_model_CHierarchicalResultsLevelSet_h
1414

15+
#include "model/ImportExport.h"
1516
#include <core/CCompressedDictionary.h>
1617

1718
#include <maths/common/CChecksum.h>
@@ -21,6 +22,10 @@
2122

2223
#include <cstdint>
2324

25+
namespace CHierarchicalResultsLevelSetTest {
26+
struct testMemoryUsage;
27+
}
28+
2429
namespace ml {
2530
namespace model {
2631

@@ -240,6 +245,26 @@ class CHierarchicalResultsLevelSet : public CHierarchicalResultsVisitor {
240245
return maths::common::CChecksum::calculate(seed, m_LeafSet);
241246
}
242247

248+
void debugMemoryUsage(const core::CMemoryUsage::TMemoryUsagePtr& mem) const {
249+
mem->setName("Hierarchical Results Level Set Memory Usage");
250+
core::memory_debug::dynamicSize("m_BucketElement", m_BucketElement, mem);
251+
core::memory_debug::dynamicSize("m_InfluencerBucketSet", m_InfluencerBucketSet, mem);
252+
core::memory_debug::dynamicSize("m_InfluencerSet", m_InfluencerSet, mem);
253+
core::memory_debug::dynamicSize("m_PartitionSet", m_PartitionSet, mem);
254+
core::memory_debug::dynamicSize("m_PersonSet", m_PersonSet, mem);
255+
core::memory_debug::dynamicSize("m_LeafSet", m_LeafSet, mem);
256+
}
257+
258+
std::size_t memoryUsage() const {
259+
std::size_t mem = core::memory::dynamicSize(m_BucketElement);
260+
mem += core::memory::dynamicSize(m_InfluencerBucketSet);
261+
mem += core::memory::dynamicSize(m_InfluencerSet);
262+
mem += core::memory::dynamicSize(m_PartitionSet);
263+
mem += core::memory::dynamicSize(m_PersonSet);
264+
mem += core::memory::dynamicSize(m_LeafSet);
265+
return mem;
266+
}
267+
243268
private:
244269
//! Get an element of \p set by name.
245270
static const T* element(const TWordTypePrVec& set, const std::string& name) {
@@ -299,6 +324,8 @@ class CHierarchicalResultsLevelSet : public CHierarchicalResultsVisitor {
299324
//! The container for leaves comprising distinct named
300325
//! (partition, person) field name pairs.
301326
TWordTypePrVec m_LeafSet;
327+
328+
friend struct CHierarchicalResultsLevelSetTest::testMemoryUsage;
302329
};
303330

304331
template<typename T>

include/model/CHierarchicalResultsNormalizer.h

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@
1212
#ifndef INCLUDED_ml_model_CHierarchicalResultsNormalizer_h
1313
#define INCLUDED_ml_model_CHierarchicalResultsNormalizer_h
1414

15+
#include <core/CMemoryDef.h>
1516
#include <core/CNonCopyable.h>
1617

1718
#include <model/CAnomalyScore.h>
1819
#include <model/CHierarchicalResultsLevelSet.h>
20+
#include <model/CLimits.h>
21+
#include <model/CMonitoredResource.h>
1922
#include <model/ImportExport.h>
2023

21-
#include <memory>
2224
#include <string>
23-
#include <utility>
2425
#include <vector>
2526

2627
namespace ml {
@@ -44,6 +45,10 @@ struct MODEL_EXPORT SNormalizer {
4445
//! Compute a checksum for this object.
4546
uint64_t checksum() const;
4647

48+
void debugMemoryUsage(const core::CMemoryUsage::TMemoryUsagePtr& mem) const;
49+
50+
std::size_t memoryUsage() const;
51+
4752
std::string s_Description;
4853
TNormalizerPtr s_Normalizer;
4954
};
@@ -84,6 +89,7 @@ struct MODEL_EXPORT SNormalizer {
8489
//! normalizers is negligible.
8590
class MODEL_EXPORT CHierarchicalResultsNormalizer
8691
: public CHierarchicalResultsLevelSet<hierarchical_results_normalizer_detail::SNormalizer>,
92+
public CMonitoredResource,
8793
private core::CNonCopyable {
8894
public:
8995
using TBase = CHierarchicalResultsLevelSet<hierarchical_results_normalizer_detail::SNormalizer>;
@@ -106,9 +112,10 @@ class MODEL_EXPORT CHierarchicalResultsNormalizer
106112
enum ERestoreOutcome { E_Ok = 0, E_Corrupt = 1, E_Incomplete = 2 };
107113

108114
public:
109-
CHierarchicalResultsNormalizer(const CAnomalyDetectorModelConfig& modelConfig);
115+
CHierarchicalResultsNormalizer(CLimits& limits,
116+
const CAnomalyDetectorModelConfig& modelConfig);
110117

111-
~CHierarchicalResultsNormalizer() override = default;
118+
~CHierarchicalResultsNormalizer() override;
112119

113120
//! Add a job for the subsequent invocations of the normalizer.
114121
void setJob(EJob job);
@@ -167,6 +174,19 @@ class MODEL_EXPORT CHierarchicalResultsNormalizer
167174
const std::string& functionName,
168175
const std::string& valueFieldName) const;
169176

177+
//! Get the memory used by this hierarchical results normalizer.
178+
void debugMemoryUsage(const core::CMemoryUsage::TMemoryUsagePtr& mem) const override;
179+
180+
//! Return the total memory usage.
181+
std::size_t memoryUsage() const override;
182+
183+
//! Get the static size of this object.
184+
std::size_t staticSize() const override;
185+
186+
//! Update the overall model size stats with information from the
187+
//! hierarchical results normalizer.
188+
void updateModelSizeStats(CResourceMonitor::SModelSizeStats& modelSizeStats) const override;
189+
170190
private:
171191
//! \brief Creates new normalizer instances.
172192
class CNormalizerFactory {
@@ -210,15 +230,18 @@ class MODEL_EXPORT CHierarchicalResultsNormalizer
210230
static std::string leafCue(const TWord& word);
211231

212232
private:
233+
//! Configurable limits
234+
CLimits& m_Limits;
235+
213236
//! The jobs that the normalizer will perform when invoked
214237
//! can be: update, normalize or update + normalize.
215-
EJob m_Job;
238+
EJob m_Job{E_NoOp};
216239

217240
//! The model configuration file.
218241
const CAnomalyDetectorModelConfig& m_ModelConfig;
219242

220243
//! Whether the last update of the quantiles has caused a big change.
221-
bool m_HasLastUpdateCausedBigChange;
244+
bool m_HasLastUpdateCausedBigChange{false};
222245
};
223246
}
224247
}

include/model/CResourceMonitor.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ class MODEL_EXPORT CResourceMonitor {
177177
//! by calling this once per bucket processed until the initially requested memory limit is reached.
178178
void decreaseMargin(core_t::TTime elapsedTime);
179179

180+
//! Returns the sum of used memory plus any extra memory
181+
std::size_t totalMemory() const;
182+
180183
private:
181184
using TMonitoredResourcePtrSizeUMap =
182185
boost::unordered_map<CMonitoredResource*, std::size_t>;
@@ -218,9 +221,6 @@ class MODEL_EXPORT CResourceMonitor {
218221
//! Get the low memory limit with margin applied.
219222
std::size_t lowLimit() const;
220223

221-
//! Returns the sum of used memory plus any extra memory
222-
std::size_t totalMemory() const;
223-
224224
//! Adjusts the amount of memory reported to take into
225225
//! account the current value of the byte limit margin and the effects
226226
//! of background persistence.

lib/api/CAnomalyJob.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ CAnomalyJob::CAnomalyJob(std::string jobId,
129129
m_MaxDetectors{std::numeric_limits<size_t>::max()},
130130
m_PersistenceManager{persistenceManager}, m_MaxQuantileInterval{maxQuantileInterval},
131131
m_LastNormalizerPersistTime{core::CTimeUtils::now()},
132-
m_Aggregator{modelConfig}, m_Normalizer{modelConfig} {
132+
m_Aggregator{modelConfig}, m_Normalizer{limits, modelConfig} {
133133
m_JsonOutputWriter.limitNumberRecords(maxAnomalyRecords);
134134

135135
m_Limits.resourceMonitor().memoryUsageReporter(
@@ -1652,6 +1652,9 @@ void CAnomalyJob::pruneAllModels(std::size_t buckets) const {
16521652
(buckets == 0) ? detector->pruneModels() : detector->pruneModels(buckets);
16531653
}
16541654
}
1655+
const model::CHierarchicalResultsNormalizer& CAnomalyJob::normalizer() const {
1656+
return m_Normalizer;
1657+
}
16551658

16561659
CAnomalyJob::TAnomalyDetectorPtr
16571660
CAnomalyJob::makeDetector(const model::CAnomalyDetectorModelConfig& modelConfig,

lib/api/CResultNormalizer.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ const std::string CResultNormalizer::INFLUENCER_LEVEL("infl");
3737
const std::string CResultNormalizer::ZERO("0");
3838

3939
CResultNormalizer::CResultNormalizer(const model::CAnomalyDetectorModelConfig& modelConfig,
40-
CSimpleOutputWriter& outputWriter)
40+
CSimpleOutputWriter& outputWriter,
41+
model::CLimits& limits)
4142
: m_ModelConfig(modelConfig), m_OutputWriter(outputWriter),
4243
m_WriteFieldNames(true),
4344
m_OutputFieldNormalizedScore(m_OutputFields[NORMALIZED_SCORE_NAME]),
44-
m_Normalizer(m_ModelConfig) {
45+
m_Normalizer(limits, m_ModelConfig) {
4546
}
4647

4748
bool CResultNormalizer::initNormalizer(const std::string& stateFileName) {

0 commit comments

Comments
 (0)