[search] Added FeaturesLayerMatcher cache to store loaded streets between queries.

This commit is contained in:
vng 2016-01-25 16:31:07 +03:00 committed by Sergey Yershov
parent 1c175aafd9
commit b7de3bf800
10 changed files with 168 additions and 78 deletions

View file

@ -57,6 +57,7 @@ HEADERS += \
v2/rank_table_cache.hpp \
v2/search_model.hpp \
v2/search_query_v2.hpp \
v2/stats_cache.hpp \
v2/street_vicinity_loader.hpp \
SOURCES += \

View file

@ -13,59 +13,76 @@ namespace v2
// static
double const FeaturesLayerMatcher::kBuildingRadiusMeters = 50;
FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, MwmContext & context,
my::Cancellable const & cancellable)
: m_context(context)
FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, my::Cancellable const & cancellable)
: m_context(nullptr)
, m_reverseGeocoder(index)
, m_houseToStreetTable(HouseToStreetTable::Load(m_context.m_value))
, m_loader(context.m_value, context.m_vector, scales::GetUpperScale(),
ReverseGeocoder::kLookupRadiusM)
, m_nearbyStreetsCache("F2NS")
, m_matchingStreetsCache("B2S")
, m_loader(scales::GetUpperScale(), ReverseGeocoder::kLookupRadiusM)
, m_cancellable(cancellable)
{
ASSERT(m_houseToStreetTable.get(), ("Can't load HouseToStreetTable"));
}
void FeaturesLayerMatcher::InitContext(MwmContext * context)
{
m_context = context;
m_houseToStreetTable = HouseToStreetTable::Load(m_context->m_value);
ASSERT(m_houseToStreetTable, ());
m_loader.InitContext(context);
}
void FeaturesLayerMatcher::FinishQuery()
{
m_nearbyStreetsCache.FinishQuery();
m_matchingStreetsCache.FinishQuery();
m_loader.FinishQuery();
}
uint32_t FeaturesLayerMatcher::GetMatchingStreet(uint32_t houseId)
{
auto const it = m_matchingStreetsCache.find(houseId);
if (it != m_matchingStreetsCache.cend())
return it->second;
auto r = m_matchingStreetsCache.Get(houseId);
if (!r.second)
return r.first;
FeatureType houseFeature;
m_context.m_vector.GetByIndex(houseId, houseFeature);
GetByIndex(houseId, houseFeature);
return GetMatchingStreetImpl(houseId, houseFeature);
r.first = GetMatchingStreetImpl(houseId, houseFeature);
return r.first;
}
uint32_t FeaturesLayerMatcher::GetMatchingStreet(uint32_t houseId, FeatureType & houseFeature)
{
auto const it = m_matchingStreetsCache.find(houseId);
if (it != m_matchingStreetsCache.cend())
return it->second;
auto r = m_matchingStreetsCache.Get(houseId);
if (!r.second)
return r.first;
return GetMatchingStreetImpl(houseId, houseFeature);
r.first = GetMatchingStreetImpl(houseId, houseFeature);
return r.first;
}
vector<ReverseGeocoder::Street> const & FeaturesLayerMatcher::GetNearbyStreets(uint32_t featureId)
{
auto const it = m_nearbyStreetsCache.find(featureId);
if (it != m_nearbyStreetsCache.cend())
return it->second;
auto r = m_nearbyStreetsCache.Get(featureId);
if (!r.second)
return r.first;
FeatureType feature;
m_context.m_vector.GetByIndex(featureId, feature);
GetByIndex(featureId, feature);
return GetNearbyStreetsImpl(featureId, feature);
m_reverseGeocoder.GetNearbyStreets(feature::GetCenter(feature), r.first);
return r.first;
}
vector<ReverseGeocoder::Street> const & FeaturesLayerMatcher::GetNearbyStreets(
uint32_t featureId, FeatureType & feature)
{
auto const it = m_nearbyStreetsCache.find(featureId);
if (it != m_nearbyStreetsCache.cend())
return it->second;
auto r = m_nearbyStreetsCache.Get(featureId);
if (!r.second)
return r.first;
return GetNearbyStreetsImpl(featureId, feature);
m_reverseGeocoder.GetNearbyStreets(feature::GetCenter(feature), r.first);
return r.first;
}
uint32_t FeaturesLayerMatcher::GetMatchingStreetImpl(uint32_t houseId, FeatureType & houseFeature)
@ -77,18 +94,10 @@ uint32_t FeaturesLayerMatcher::GetMatchingStreetImpl(uint32_t houseId, FeatureTy
if (!m_houseToStreetTable->Get(houseId, streetIndex))
streetIndex = streets.size();
if (streetIndex < streets.size() && streets[streetIndex].m_id.m_mwmId == m_context.m_id)
if (streetIndex < streets.size() && streets[streetIndex].m_id.m_mwmId == m_context->m_id)
streetId = streets[streetIndex].m_id.m_index;
m_matchingStreetsCache[houseId] = streetId;
return streetId;
}
vector<ReverseGeocoder::Street> const & FeaturesLayerMatcher::GetNearbyStreetsImpl(
uint32_t featureId, FeatureType & feature)
{
auto & streets = m_nearbyStreetsCache[featureId];
m_reverseGeocoder.GetNearbyStreets(feature::GetCenter(feature), streets);
return streets;
}
} // namespace v2
} // namespace search

View file

@ -58,7 +58,8 @@ public:
static uint32_t const kInvalidId = numeric_limits<uint32_t>::max();
static double const kBuildingRadiusMeters;
FeaturesLayerMatcher(Index & index, MwmContext & context, my::Cancellable const & cancellable);
FeaturesLayerMatcher(Index & index, my::Cancellable const & cancellable);
void InitContext(MwmContext * context);
template <typename TFn>
void Match(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn)
@ -90,6 +91,8 @@ public:
}
}
void FinishQuery();
private:
template <typename TFn>
void MatchPOIsWithBuildings(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn)
@ -152,7 +155,6 @@ private:
if (queryTokens.empty())
return;
auto const & mwmId = m_context.m_handle.GetId();
vector<ReverseGeocoder::Building> nearbyBuildings;
for (size_t i = 0; i < pois.size(); ++i)
{
@ -163,7 +165,7 @@ private:
m_reverseGeocoder.GetNearbyBuildings(poiCenters[i], kBuildingRadiusMeters, nearbyBuildings);
for (auto const & building : nearbyBuildings)
{
if (building.m_id.m_mwmId != mwmId || building.m_distanceMeters > kBuildingRadiusMeters)
if (building.m_id.m_mwmId != m_context->m_id || building.m_distanceMeters > kBuildingRadiusMeters)
continue;
if (HouseNumbersMatch(building.m_name, queryTokens))
fn(pois[i], building.m_id.m_index);
@ -184,7 +186,6 @@ private:
// it's faster to check nearby streets for POIs.
if (pois.size() < streets.size())
{
auto const & mwmId = m_context.m_handle.GetId();
for (uint32_t poiId : pois)
{
// TODO (@y, @m, @vng): implement a faster version
@ -192,7 +193,7 @@ private:
// map.
for (auto const & street : GetNearbyStreets(poiId))
{
if (street.m_id.m_mwmId != mwmId ||
if (street.m_id.m_mwmId != m_context->m_id ||
street.m_distanceMeters > ReverseGeocoder::kLookupRadiusM)
{
continue;
@ -330,26 +331,24 @@ private:
uint32_t GetMatchingStreetImpl(uint32_t houseId, FeatureType & houseFeature);
vector<ReverseGeocoder::Street> const & GetNearbyStreetsImpl(uint32_t featureId,
FeatureType & feature);
inline void GetByIndex(uint32_t id, FeatureType & ft) const
{
m_context.m_vector.GetByIndex(id, ft);
/// @todo Add StatsCache for feature id -> (point, name / house number).
m_context->m_vector.GetByIndex(id, ft);
}
MwmContext & m_context;
MwmContext * m_context;
ReverseGeocoder m_reverseGeocoder;
// Cache of streets in a feature's vicinity. All lists in the cache
// are ordered by distance from the corresponding feature.
unordered_map<uint32_t, vector<ReverseGeocoder::Street>> m_nearbyStreetsCache;
StatsCache<uint32_t, vector<ReverseGeocoder::Street>> m_nearbyStreetsCache;
// Cache of correct streets for buildings. Current search algorithm
// supports only one street for a building, whereas buildings can be
// located on multiple streets.
unordered_map<uint32_t, uint32_t> m_matchingStreetsCache;
StatsCache<uint32_t, uint32_t> m_matchingStreetsCache;
unique_ptr<HouseToStreetTable> m_houseToStreetTable;

View file

@ -421,13 +421,22 @@ void Geocoder::GoImpl(vector<shared_ptr<MwmInfo>> & infos, bool inViewport)
m_context = move(context);
MY_SCOPE_GUARD(cleanup, [&]()
{
m_matcher.reset();
LOG(LDEBUG, ("Search results for", m_context->GetMwmName()));
m_matcher->FinishQuery();
m_matcher = nullptr;
m_context.reset();
m_addressFeatures.clear();
m_streets = nullptr;
});
m_matcher.reset(new FeaturesLayerMatcher(m_index, *m_context, *this /* cancellable */));
auto iMatcher = m_matchersCache.find(m_context->m_id);
if (iMatcher == m_matchersCache.end())
{
iMatcher = m_matchersCache.insert(make_pair(m_context->m_id, make_unique<FeaturesLayerMatcher>(
m_index, *this /* cancellable */))).first;
}
m_matcher = iMatcher->second.get();
m_matcher->InitContext(m_context.get());
unique_ptr<coding::CompressedBitVector> viewportCBV;
if (inViewport)
@ -475,7 +484,7 @@ void Geocoder::ClearCaches()
{
m_geometryFeatures.clear();
m_addressFeatures.clear();
m_matcher.reset();
m_matchersCache.clear();
m_streetsCache.clear();
}

View file

@ -251,7 +251,8 @@ private:
FeaturesFilter m_filter;
// Features matcher for layers intersection.
unique_ptr<FeaturesLayerMatcher> m_matcher;
map<MwmSet::MwmId, unique_ptr<FeaturesLayerMatcher>> m_matchersCache;
FeaturesLayerMatcher * m_matcher;
// Path finder for interpretations.
FeaturesLayerPathFinder m_finder;

View file

@ -11,7 +11,14 @@ MwmContext::MwmContext(MwmSet::MwmHandle handle)
, m_value(*m_handle.GetValue<MwmValue>())
, m_id(m_handle.GetId())
, m_vector(m_value.m_cont, m_value.GetHeader(), m_value.m_table)
, m_index(m_value.m_cont.GetReader(INDEX_FILE_TAG), m_value.m_factory)
{
}
string const & MwmContext::GetMwmName() const
{
return m_id.GetInfo()->GetCountryName();
}
} // namespace v2
} // namespace search

View file

@ -2,6 +2,7 @@
#include "indexer/features_vector.hpp"
#include "indexer/mwm_set.hpp"
#include "indexer/scale_index.hpp"
#include "base/macros.hpp"
@ -19,6 +20,9 @@ struct MwmContext
MwmValue & m_value;
MwmSet::MwmId const & m_id;
FeaturesVector m_vector;
ScaleIndex<ModelReaderPtr> m_index;
string const & GetMwmName() const;
DISALLOW_COPY_AND_MOVE(MwmContext);
};

55
search/v2/stats_cache.hpp Normal file
View file

@ -0,0 +1,55 @@
#pragma once
#include "base/logging.hpp"
#include "std/unordered_map.hpp"
namespace search
{
namespace v2
{
template <class TKey, class TValue>
class StatsCache
{
unordered_map<TKey, TValue> m_map;
size_t m_count, m_new, m_emptyCount;
char const * m_name;
public:
explicit StatsCache(char const * name) : m_count(0), m_new(0), m_emptyCount(0), m_name(name) {}
pair<TValue &, bool> Get(TKey const & key)
{
auto r = m_map.insert(make_pair(key, TValue()));
++m_count;
if (r.second)
++m_new;
return pair<TValue &, bool>(r.first->second, r.second);
}
void Clear()
{
m_map.clear();
m_count = m_new = 0;
}
void FinishQuery()
{
if (m_count != 0)
{
LOG(LDEBUG, ("Cache", m_name, "Queries =", m_count, "From cache =", m_count - m_new, "Added =", m_new));
m_count = m_new = 0;
}
else if (++m_emptyCount > 5)
{
Clear();
m_emptyCount = 0;
}
}
};
} // namespace v2
} // namespace search

View file

@ -15,30 +15,37 @@ namespace search
{
namespace v2
{
StreetVicinityLoader::StreetVicinityLoader(MwmValue & value, FeaturesVector const & featuresVector,
int scale, double offsetMeters)
: m_index(value.m_cont.GetReader(INDEX_FILE_TAG), value.m_factory)
, m_featuresVector(featuresVector)
, m_offsetMeters(offsetMeters)
StreetVicinityLoader::StreetVicinityLoader(int scale, double offsetMeters)
: m_context(nullptr), m_scale(scale), m_offsetMeters(offsetMeters), m_cache("Streets")
{
auto const scaleRange = value.GetHeader().GetScaleRange();
m_scale = my::clamp(scale, scaleRange.first, scaleRange.second);
}
void StreetVicinityLoader::InitContext(MwmContext * context)
{
m_context = context;
auto const scaleRange = m_context->m_value.GetHeader().GetScaleRange();
m_scale = my::clamp(m_scale, scaleRange.first, scaleRange.second);
}
void StreetVicinityLoader::FinishQuery()
{
m_cache.FinishQuery();
}
StreetVicinityLoader::Street const & StreetVicinityLoader::GetStreet(uint32_t featureId)
{
auto it = m_cache.find(featureId);
if (it != m_cache.end())
return it->second;
auto r = m_cache.Get(featureId);
if (!r.second)
return r.first;
LoadStreet(featureId, m_cache[featureId]);
return m_cache[featureId];
LoadStreet(featureId, r.first);
return r.first;
}
void StreetVicinityLoader::LoadStreet(uint32_t featureId, Street & street)
{
FeatureType feature;
m_featuresVector.GetByIndex(featureId, feature);
m_context->m_vector.GetByIndex(featureId, feature);
if (feature.GetFeatureType() != feature::GEOM_LINE)
return;
@ -54,8 +61,8 @@ void StreetVicinityLoader::LoadStreet(uint32_t featureId, Street & street)
auto const & intervals = coveringGetter.Get(m_scale);
for (auto const & interval : intervals)
{
m_index.ForEachInIntervalAndScale(MakeBackInsertFunctor(street.m_features), interval.first,
interval.second, m_scale);
m_context->m_index.ForEachInIntervalAndScale(MakeBackInsertFunctor(street.m_features),
interval.first, interval.second, m_scale);
}
street.m_calculator = make_unique<ProjectionOnStreetCalculator>(points, m_offsetMeters);

View file

@ -1,13 +1,11 @@
#pragma once
#include "search/projection_on_street.hpp"
#include "search/v2/mwm_context.hpp"
#include "search/v2/stats_cache.hpp"
#include "indexer/feature.hpp"
#include "indexer/feature_algo.hpp"
#include "indexer/features_vector.hpp"
#include "indexer/scale_index.hpp"
#include "coding/reader.hpp"
#include "geometry/rect2d.hpp"
@ -15,12 +13,13 @@
#include "std/unordered_map.hpp"
class MwmValue;
namespace search
{
namespace v2
{
struct MwmContext;
// This class is able to load features in a street's vicinity.
//
// NOTE: this class *IS NOT* thread-safe.
@ -41,8 +40,8 @@ public:
DISALLOW_COPY(Street);
};
StreetVicinityLoader(MwmValue & value, FeaturesVector const & featuresVector, int scale,
double offsetMeters);
StreetVicinityLoader(int scale, double offsetMeters);
void InitContext(MwmContext * context);
// Calls |fn| on each index in |sortedIds| where sortedIds[index]
// belongs to the street's vicinity.
@ -62,7 +61,7 @@ public:
continue;
FeatureType ft;
m_featuresVector.GetByIndex(id, ft);
m_context->m_vector.GetByIndex(id, ft);
if (!calculator.GetProjection(feature::GetCenter(ft, FeatureType::WORST_GEOMETRY), proj))
continue;
@ -70,19 +69,18 @@ public:
}
}
Street const & GetStreet(uint32_t featureId);
void FinishQuery();
inline void ClearCache() { m_cache.clear(); }
Street const & GetStreet(uint32_t featureId);
private:
void LoadStreet(uint32_t featureId, Street & street);
ScaleIndex<ModelReaderPtr> m_index;
FeaturesVector const & m_featuresVector;
MwmContext * m_context;
int m_scale;
double const m_offsetMeters;
unordered_map<uint32_t, Street> m_cache;
StatsCache<uint32_t, Street> m_cache;
DISALLOW_COPY_AND_MOVE(StreetVicinityLoader);
};