From bdae38621b69f06effbab583729954dded2f54ef Mon Sep 17 00:00:00 2001 From: Antal van den Bosch Date: Fri, 19 Jun 2026 15:31:12 +0200 Subject: [PATCH] ClassDistribution: store distributions as a sorted vector The per-class distribution was a std::map: a red-black-tree node plus a separately heap-allocated Vfield for every target class, for (potentially) every node in the instance base. On a large base this is the dominant memory consumer. Store the distribution as a flat std::vector, kept sorted by value->Index() (the map key was redundant -- it always equals value->Index()). Vfield becomes a plain copyable value type stored inline. Lookups are a binary search, with an O(1) fast path for the common sorted-append case; the sorted invariant keeps (de)serialisation byte-for-byte identical. Measured on a word-prediction base (~4.9M instances, ~13.5M nodes; l4r0): - peak RSS loading a TRIBL2 base: 2.07 GB -> 1.40 GB (-32%) - peak RSS, IGTree: 1.26 GB -> 0.87 GB (-31%) - TRIBL2 test-phase instructions: +3.4% (in-place sorted inserts cost a tail shift where the tree was O(log n); intrinsic to the layout, bites only on large distributions) Verified byte-identical test output (plain and +v db), a freshly trained instance base byte-identical to the previous on-disk format, and unchanged IGTree accuracy. Co-Authored-By: Claude Opus 4.8 --- include/timbl/Targets.h | 29 ++++- src/Features.cxx | 28 ++--- src/MBLClass.cxx | 6 +- src/Targets.cxx | 249 +++++++++++++++++++++++----------------- 4 files changed, 181 insertions(+), 131 deletions(-) diff --git a/include/timbl/Targets.h b/include/timbl/Targets.h index 77513da..e7f06db 100644 --- a/include/timbl/Targets.h +++ b/include/timbl/Targets.h @@ -110,10 +110,15 @@ namespace Timbl { public: Vfield( const TargetValue *val, int freq, double w ): value(val), frequency(freq), weight(w) {}; - Vfield( const Vfield& in ): - value(in.value), frequency(in.frequency), weight(in.weight) {}; - Vfield& operator=( const Vfield& ) = delete; // forbid copies - ~Vfield(){}; + // Vfield is a plain value type: it is stored BY VALUE in + // ClassDistribution's vector and owns nothing (value is a non-owning + // pointer into the Targets table). Full value semantics are therefore + // required (vector needs move-assignment to keep itself sorted) and safe. + Vfield( const Vfield& ) = default; + Vfield( Vfield&& ) = default; + Vfield& operator=( const Vfield& ) = default; + Vfield& operator=( Vfield&& ) = default; + ~Vfield() = default; std::ostream& put( std::ostream& ) const; const TargetValue *Value() const { return value; }; void Value( const TargetValue *t ){ value = t; }; @@ -123,7 +128,7 @@ namespace Timbl { void DecFreq() { frequency -= 1; }; double Weight() const { return weight; }; void SetWeight( double w ){ weight = w; }; - size_t Index(); + size_t Index() const; protected: const TargetValue *value; size_t frequency; @@ -138,7 +143,14 @@ namespace Timbl { friend std::ostream& operator<<( std::ostream&, const ClassDistribution * ); friend class WClassDistribution; public: - using VDlist = std::map; + // The distribution is stored as a flat vector of Vfield values, kept + // sorted by value->Index(). Compared to the former + // std::map this removes, per target class, one heap + // allocation and the red-black-tree node overhead -- a large memory win + // since there is (potentially) one distribution per instance-base node. + // Lookups are a binary search; the sorted invariant also keeps + // (de)serialisation order stable. + using VDlist = std::vector; using dist_iterator = VDlist::const_iterator; ClassDistribution( ): total_items(0) {}; ClassDistribution( const ClassDistribution& ); @@ -176,6 +188,11 @@ namespace Timbl { const TargetValue* BestTargetW( bool &, bool = false ) const; virtual ClassDistribution *clone( ) const { return new ClassDistribution(); }; + // find the entry for target index 'id'; returns nullptr if absent. + Vfield *find_index( size_t id ); + const Vfield *find_index( size_t id ) const; + // return the sorted insert position (lower_bound) for target index 'id'. + VDlist::iterator lower_index( size_t id ); size_t total_items; VDlist distribution; }; diff --git a/src/Features.cxx b/src/Features.cxx index 8cd62c1..8a73358 100644 --- a/src/Features.cxx +++ b/src/Features.cxx @@ -130,8 +130,8 @@ namespace Timbl { // Loop over all present classes. // for ( const auto& tit : FV->TargetDist ){ - FV->ValueClassProb->Assign( tit.second->Index(), - tit.second->Freq()/(double)freq ); + FV->ValueClassProb->Assign( tit.Index(), + tit.Freq()/(double)freq ); } } } @@ -313,7 +313,7 @@ namespace Timbl { // FVEntropy = 0.0; for ( const auto& it : pnt->TargetDist ){ - Prob = it.second->Freq()/(double)Freq; + Prob = it.Freq()/(double)Freq; FVEntropy += Prob * Log2(Prob); } entropy += -FVEntropy * Freq / (double)TotalVals; @@ -392,7 +392,7 @@ namespace Timbl { if ( Freq > 0 ){ double FVEntropy = 0.0; for ( const auto& tit : fv->TargetDist ){ - double Prob = tit.second->Freq() / (double)Freq; + double Prob = tit.Freq() / (double)Freq; FVEntropy += Prob * Log2(Prob); } entropy += -FVEntropy * Freq / (double)TotalVals; @@ -442,8 +442,8 @@ namespace Timbl { n_i_dot[i] = 0; // ALL values should be zeroed const FeatureValue *fv = FVA[i]; for ( const auto& tit : fv->TargetDist ){ - n_dot_j[tit.second->Index()-1] += tit.second->Freq(); - n_i_dot[i] += tit.second->Freq(); + n_dot_j[tit.Index()-1] += tit.Freq(); + n_i_dot[i] += tit.Freq(); } n_dot_dot += n_i_dot[i]; } @@ -455,17 +455,17 @@ namespace Timbl { if ( n >= Size ){ break; } - while ( n < it.second->Index()-1 ){ + while ( n < it.Index()-1 ){ double tmp = ((double)n_dot_j[n++] * (double)n_i_dot[m]) / (double)n_dot_dot; chi_square += tmp; } - if ( n == it.second->Index()-1 ){ + if ( n == it.Index()-1 ){ double tmp = ((double)n_dot_j[n++] * (double)n_i_dot[m]) / (double)n_dot_dot; if ( fabs(tmp) > Epsilon){ - chi_square += ( (tmp - it.second->Freq()) * - (tmp - it.second->Freq()) ) / tmp; + chi_square += ( (tmp - it.Freq()) * + (tmp - it.Freq()) ) / tmp; } } else { @@ -496,8 +496,8 @@ namespace Timbl { for ( const auto* fv : values_array ){ n_i_dot[i] = 0; // ALL values should be zeroed for ( const auto& t_it : fv->TargetDist ){ - long int fr = t_it.second->Freq(); - n_dot_j[t_it.second->Index()-1] += fr; + long int fr = t_it.Freq(); + n_dot_j[t_it.Index()-1] += fr; n_i_dot[i] += fr; } n_dot_dot += n_i_dot[i]; @@ -511,8 +511,8 @@ namespace Timbl { if ( n >= Size ){ break; } - size_t id = t_it.second->Index()-1; - long int fr = t_it.second->Freq(); + size_t id = t_it.Index()-1; + long int fr = t_it.Freq(); while ( n < id ){ double tmp = ((double)n_dot_j[n++] * (double)n_i_dot[m]) / (double)n_dot_dot; diff --git a/src/MBLClass.cxx b/src/MBLClass.cxx index d0cdc01..488907f 100644 --- a/src/MBLClass.cxx +++ b/src/MBLClass.cxx @@ -1848,7 +1848,7 @@ namespace Timbl { } tester->init( Inst, EffectiveFeatures(), ib_offset ); auto lastpos = best_distrib->begin(); - Vfield *Bpnt = lastpos->second; + const Vfield *Bpnt = ( lastpos != best_distrib->end() ) ? &*lastpos : nullptr; size_t EffFeat = EffectiveFeatures() - ib_offset; size_t CurPos = 0; while ( Bpnt ) { @@ -1873,7 +1873,7 @@ namespace Timbl { CurPos = EndPos-1; ++lastpos; if ( lastpos != best_distrib->end() ){ - Bpnt = lastpos->second; + Bpnt = &*lastpos; } else { best_distrib = IB->NextGraphTest( CurrentFV, @@ -1882,7 +1882,7 @@ namespace Timbl { if ( best_distrib ){ lastpos = best_distrib->begin(); if ( lastpos != best_distrib->end() ){ - Bpnt = lastpos->second; + Bpnt = &*lastpos; } } } diff --git a/src/Targets.cxx b/src/Targets.cxx index a6c6fd8..2b614d6 100644 --- a/src/Targets.cxx +++ b/src/Targets.cxx @@ -50,7 +50,7 @@ namespace Timbl { using namespace Common; using TiCC::operator<<; - size_t Vfield::Index() { return value->Index(); } + size_t Vfield::Index() const { return value->Index(); } ostream& operator<<(ostream& os, const Vfield *vd ) { return vd->put( os ); @@ -76,20 +76,49 @@ namespace Timbl { return (int)floor(randnum+0.5); } - void ClassDistribution::clear(){ - for ( const auto& d : distribution ){ - delete d.second; + // distribution is kept sorted by value->Index(); these helpers locate or + // position entries with a binary search. + ClassDistribution::VDlist::iterator + ClassDistribution::lower_index( size_t id ){ + // Fast path for the common append case: when building a distribution + // from sorted input (read_distribution) or merging a disjoint tail, the + // new index is larger than everything present, so the insert position is + // simply end() -- no binary search needed. + if ( distribution.empty() || distribution.back().value->Index() < id ){ + return distribution.end(); + } + return lower_bound( distribution.begin(), distribution.end(), id, + []( const Vfield& v, size_t i ){ + return v.value->Index() < i; } ); + } + + Vfield *ClassDistribution::find_index( size_t id ){ + auto it = lower_index( id ); + if ( it != distribution.end() && it->value->Index() == id ){ + return &*it; + } + return nullptr; + } + + const Vfield *ClassDistribution::find_index( size_t id ) const { + auto it = lower_bound( distribution.begin(), distribution.end(), id, + []( const Vfield& v, size_t i ){ + return v.value->Index() < i; } ); + if ( it != distribution.end() && it->value->Index() == id ){ + return &*it; } + return nullptr; + } + + void ClassDistribution::clear(){ distribution.clear(); total_items = 0; } double ClassDistribution::Confidence( const TargetValue *tv ) const { - auto it = find_if( distribution.begin(), distribution.end(), - [tv]( const std::pair& v ){ - return v.second->Value() == tv ; } ); - if ( it != distribution.end() ){ - return it->second->Weight(); + const Vfield *f = find_index( tv->Index() ); + if ( f ){ + return f->Weight(); } return 0.0; } @@ -99,13 +128,12 @@ namespace Timbl { oss.setf(ios::showpoint); bool first = true; oss << "{ "; - for ( const auto& it : distribution ){ - const Vfield *f = it.second; - if ( f->frequency >= minf ){ + for ( const auto& f : distribution ){ + if ( f.frequency >= minf ){ if ( !first ){ oss << ", "; } - oss << f->value << " " << double(f->frequency); + oss << f.value << " " << double(f.frequency); first = false; } } @@ -118,18 +146,17 @@ namespace Timbl { oss.setf(ios::showpoint); bool first = true; oss << "{ "; - for( const auto& it : distribution ){ - const Vfield *f = it.second; - if ( abs(f->weight) < minw ){ + for( const auto& f : distribution ){ + if ( abs(f.weight) < minw ){ continue; } - if ( abs(f->weight) < Epsilon ){ + if ( abs(f.weight) < Epsilon ){ continue; } if ( !first ){ oss << ", "; } - oss << f->value << " " << f->weight; + oss << f.value << " " << f.weight; first = false; } oss << " }"; @@ -147,9 +174,8 @@ namespace Timbl { double minw = 0.0; if ( beam > 0 ){ set freqs; - for ( const auto& it : distribution ){ - const Vfield *f = it.second; - freqs.insert( f->frequency ); + for ( const auto& f : distribution ){ + freqs.insert( f.frequency ); } int cnt=0; for ( const auto& rit : freqs ){ @@ -167,9 +193,8 @@ namespace Timbl { double minw = 0.0; if ( beam > 0 ){ set wgths; - for ( const auto& it : distribution ){ - const Vfield *f = it.second; - wgths.insert( f->weight ); + for ( const auto& f : distribution ){ + wgths.insert( f.weight ); } int cnt=0; for ( const auto& rit : wgths ){ @@ -200,7 +225,7 @@ namespace Timbl { if ( TotalVals > 0 ){ // Loop over the classes in the distribution for ( const auto& it : distribution ){ - size_t Freq = it.second->Freq(); + size_t Freq = it.Freq(); if ( Freq > 0 ){ double Prob = Freq / (double)TotalVals; entropy += Prob * Log2(Prob); @@ -213,10 +238,10 @@ namespace Timbl { void WClassDistribution::Normalize() { double sum = accumulate( distribution.begin(), distribution.end(), 0.0, - []( double r, const std::pair& v ){ - return r + v.second->Weight(); } ); + []( double r, const Vfield& v ){ + return r + v.Weight(); } ); for ( auto& it : distribution ){ - it.second->SetWeight( it.second->Weight() / sum ); + it.SetWeight( it.Weight() / sum ); } } @@ -226,12 +251,12 @@ namespace Timbl { // search for val, if not there: add entry with frequency factor; // otherwise increment the ExamplarWeight size_t id = val->Index(); - auto const& it = distribution.find( id ); - if ( it != distribution.end() ){ - it->second->SetWeight( it->second->Weight() + factor ); + auto it = lower_index( id ); + if ( it != distribution.end() && it->value->Index() == id ){ + it->SetWeight( it->Weight() + factor ); } else { - distribution[id] = new Vfield( val, 1, factor ); + distribution.insert( it, Vfield( val, 1, factor ) ); } } total_items += targ.num_of_values(); @@ -239,18 +264,17 @@ namespace Timbl { } void WClassDistribution::Normalize_2( ) { - for ( const auto& d : distribution ){ - d.second->SetWeight( log1p( d.second->Weight() ) ); + for ( auto& d : distribution ){ + d.SetWeight( log1p( d.Weight() ) ); } Normalize(); } ClassDistribution *ClassDistribution::to_VD_Copy( ) const { ClassDistribution *res = new ClassDistribution(); - for ( const auto& [key,vdf] : distribution ){ - res->distribution[key] = new Vfield( vdf->Value(), - vdf->Freq(), - vdf->Freq() ); + res->distribution.reserve( distribution.size() ); + for ( const auto& vdf : distribution ){ + res->distribution.emplace_back( vdf.Value(), vdf.Freq(), vdf.Freq() ); } res->total_items = total_items; return res; @@ -258,10 +282,9 @@ namespace Timbl { WClassDistribution *ClassDistribution::to_WVD_Copy() const { WClassDistribution *res = new WClassDistribution(); - for ( const auto& [key,vdf] : distribution ){ - res->distribution[key] = new Vfield( vdf->Value(), - vdf->Freq(), - vdf->Freq() ); + res->distribution.reserve( distribution.size() ); + for ( const auto& vdf : distribution ){ + res->distribution.emplace_back( vdf.Value(), vdf.Freq(), vdf.Freq() ); } res->total_items = total_items; return res; @@ -269,10 +292,9 @@ namespace Timbl { WClassDistribution *WClassDistribution::to_WVD_Copy( ) const { WClassDistribution *result = new WClassDistribution(); - for ( const auto& [key,vdf] : distribution ){ - result->distribution[key] = new Vfield( vdf->Value(), - vdf->Freq(), - vdf->Weight() ); + result->distribution.reserve( distribution.size() ); + for ( const auto& vdf : distribution ){ + result->distribution.emplace_back( vdf.Value(), vdf.Freq(), vdf.Weight() ); } result->total_items = total_items; return result; @@ -290,13 +312,12 @@ namespace Timbl { ostringstream oss; oss << "{ "; bool first = true; - for ( const auto& it : distribution ){ - const Vfield *f = it.second; - if ( f->frequency > 0 ){ + for ( const auto& f : distribution ){ + if ( f.frequency > 0 ){ if ( !first ){ oss << ", "; } - oss << f->value->Index() << " " << f->frequency; + oss << f.value->Index() << " " << f.frequency; first = false; } } @@ -308,14 +329,13 @@ namespace Timbl { ostringstream oss; bool first = true; oss << "{ "; - for ( const auto& it : distribution ){ - const Vfield *f = it.second; - if ( f->frequency > 0 ){ + for ( const auto& f : distribution ){ + if ( f.frequency > 0 ){ if ( !first ){ oss << ", "; } - oss << f->Value()->Index() << " " - << f->frequency << " " << f->weight; + oss << f.Value()->Index() << " " + << f.frequency << " " << f.weight; first = false; } } @@ -331,13 +351,12 @@ namespace Timbl { ostringstream oss; oss << "{ "; bool first = true; - for ( const auto& it : distribution ){ - const Vfield *f = it.second; - if ( f->frequency > 0 ){ + for ( const auto& f : distribution ){ + if ( f.frequency > 0 ){ if ( !first ){ oss << ", "; } - oss << f->value << " " << f->frequency; + oss << f.value << " " << f.frequency; first = false; } } @@ -349,14 +368,13 @@ namespace Timbl { ostringstream oss; oss << "{ "; bool first = true; - for ( const auto& it : distribution ){ - const Vfield *f = it.second; - if ( f->frequency > 0 ){ + for ( const auto& f : distribution ){ + if ( f.frequency > 0 ){ if ( !first ){ oss << ", "; } oss.setf(ios::showpoint); - oss << f->value << " " << f->frequency << " " << f->weight; + oss << f.value << " " << f.frequency << " " << f.weight; first = false; } } @@ -366,18 +384,30 @@ namespace Timbl { void ClassDistribution::SetFreq( const TargetValue *val, const int freq, double ){ - // add entry with frequency freq; - Vfield *temp = new Vfield( val, freq, freq ); - distribution[val->Index()] = temp; + // add entry with frequency freq (replacing any existing entry for val); + size_t id = val->Index(); + auto it = lower_index( id ); + if ( it != distribution.end() && it->value->Index() == id ){ + *it = Vfield( val, freq, freq ); + } + else { + distribution.insert( it, Vfield( val, freq, freq ) ); + } total_items += freq; } void WClassDistribution::SetFreq( const TargetValue *val, const int freq, double sw ){ - // add entry with frequency freq; + // add entry with frequency freq (replacing any existing entry for val); // also sets the sample_weight - Vfield *temp = new Vfield( val, freq, sw ); - distribution[val->Index()] = temp; + size_t id = val->Index(); + auto it = lower_index( id ); + if ( it != distribution.end() && it->value->Index() == id ){ + *it = Vfield( val, freq, sw ); + } + else { + distribution.insert( it, Vfield( val, freq, sw ) ); + } total_items += freq; } @@ -387,12 +417,12 @@ namespace Timbl { // search for val, if not there: add entry with frequency 'occ'; // otherwise increment the freqency size_t id = val->Index(); - auto const& it = distribution.find( id ); - if ( it != distribution.end() ){ - it->second->IncFreq( occ ); + auto it = lower_index( id ); + if ( it != distribution.end() && it->value->Index() == id ){ + it->IncFreq( occ ); } else { - distribution[id] = new Vfield( val, occ, 1.0 ); + distribution.insert( it, Vfield( val, occ, 1.0 ) ); } total_items += occ; return true; @@ -405,39 +435,40 @@ namespace Timbl { // otherwise increment the freqency // also set sample weight size_t id = val->Index(); - auto const& it = distribution.find( id ); - if ( it != distribution.end() ){ - it->second->IncFreq( occ ); + auto it = lower_index( id ); + if ( it != distribution.end() && it->value->Index() == id ){ + it->IncFreq( occ ); } else { - distribution[id] = new Vfield( val, occ, sw ); + it = distribution.insert( it, Vfield( val, occ, sw ) ); } total_items += occ; - return fabs( distribution[id]->Weight() - sw ) > Epsilon; + return fabs( it->Weight() - sw ) > Epsilon; } void ClassDistribution::DecFreq( const TargetValue *val ){ // search for val, if not there, just forget // otherwise decrement the freqency - auto const& it = distribution.find( val->Index() ); - if ( it != distribution.end() ){ - it->second->DecFreq(); + Vfield *f = find_index( val->Index() ); + if ( f ){ + f->DecFreq(); total_items -= 1; } } void ClassDistribution::Merge( const ClassDistribution& VD ){ - for ( const auto& [key,vd] : VD.distribution ){ - if ( distribution.find(key) != distribution.end() ){ + for ( const auto& vd : VD.distribution ){ + size_t key = vd.value->Index(); + auto it = lower_index( key ); + if ( it != distribution.end() && it->value->Index() == key ){ // the key is already present, increment the frequency - distribution[key]->AddFreq( vd->Freq() ); + it->AddFreq( vd.Freq() ); } else { // add a key // VD might be weighted. But we don't need/want that info here // Weight == Freq is more convenient - distribution[key] = new Vfield( vd->Value(), vd->Freq(), - vd->Freq() ); + distribution.insert( it, Vfield( vd.Value(), vd.Freq(), vd.Freq() ) ); } } total_items += VD.total_items; @@ -445,13 +476,15 @@ namespace Timbl { void WClassDistribution::MergeW( const ClassDistribution& VD, double Weight ){ - for ( const auto& [key,vd] : VD.distribution ){ - if ( distribution.find(key) != distribution.end() ){ - distribution[key]->SetWeight( distribution[key]->Weight() + vd->Weight() *Weight ); + for ( const auto& vd : VD.distribution ){ + size_t key = vd.value->Index(); + auto it = lower_index( key ); + if ( it != distribution.end() && it->value->Index() == key ){ + it->SetWeight( it->Weight() + vd.Weight() * Weight ); } else { - distribution[key] = new Vfield( vd->Value(), 1, - vd->Weight() * Weight); + distribution.insert( it, Vfield( vd.Value(), 1, + vd.Weight() * Weight ) ); } } total_items += VD.total_items; @@ -467,13 +500,13 @@ namespace Timbl { tie = false; auto It = distribution.begin(); if ( It != distribution.end() ){ - const Vfield *pnt = It->second; + const Vfield *pnt = &*It; size_t Max = pnt->Freq(); if ( do_rand ){ int nof_best=1, pick=1; ++It; while ( It != distribution.end() ){ - pnt = It->second; + pnt = &*It; if ( pnt->Freq() > Max ){ Max = pnt->Freq(); nof_best = 1; @@ -490,7 +523,7 @@ namespace Timbl { It = distribution.begin(); nof_best = 0; while ( It != distribution.end() ){ - pnt = It->second; + pnt = &*It; if ( pnt->Freq() == Max ){ if ( ++nof_best == pick ){ return pnt->Value(); @@ -504,7 +537,7 @@ namespace Timbl { best = pnt->Value(); ++It; while ( It != distribution.end() ){ - pnt = It->second; + pnt = &*It; if ( pnt->Freq() > Max ){ tie = false; best = pnt->Value(); @@ -536,17 +569,17 @@ namespace Timbl { auto It = distribution.begin(); tie = false; if ( It != distribution.end() ){ - double Max = It->second->Weight(); + double Max = It->Weight(); if ( do_rand ){ int nof_best=1, pick=1; ++It; while ( It != distribution.end() ){ - if ( It->second->Weight() > Max ){ - Max = It->second->Weight(); + if ( It->Weight() > Max ){ + Max = It->Weight(); nof_best = 1; } else { - if ( abs(It->second->Weight()- Max) < Epsilon ){ + if ( abs(It->Weight()- Max) < Epsilon ){ nof_best++; } } @@ -557,9 +590,9 @@ namespace Timbl { It = distribution.begin(); nof_best = 0; while ( It != distribution.end() ){ - if ( abs(It->second->Weight() - Max) < Epsilon ){ + if ( abs(It->Weight() - Max) < Epsilon ){ if ( ++nof_best == pick ){ - return It->second->Value(); + return It->Value(); } } ++It; @@ -567,19 +600,19 @@ namespace Timbl { return NULL; } else { - best = It->second->Value(); + best = It->Value(); ++It; while ( It != distribution.end() ){ - if ( It->second->Weight() > Max ){ + if ( It->Weight() > Max ){ tie = false; - best = It->second->Value(); - Max = It->second->Weight(); + best = It->Value(); + Max = It->Weight(); } else { - if ( abs(It->second->Weight() - Max) < Epsilon ) { + if ( abs(It->Weight() - Max) < Epsilon ) { tie = true; - if ( It->second->Value()->ValFreq() > best->ValFreq() ){ - best = It->second->Value(); + if ( It->Value()->ValFreq() > best->ValFreq() ){ + best = It->Value(); } } }