From 503fab769f0f613eea2418e5da5132407e5204b3 Mon Sep 17 00:00:00 2001 From: Constantin Shalnev Date: Thu, 25 Jun 2015 10:34:02 +0300 Subject: [PATCH] fixed notes --- base/base_tests/cache_test.cpp | 82 ++++++++++++++++++++++++++++++- base/cache.hpp | 55 +++++++++++++++++++-- routing/features_road_graph.cpp | 86 ++++++++++++++++----------------- routing/features_road_graph.hpp | 17 ++----- routing/routing_algorithm.cpp | 14 ++---- 5 files changed, 182 insertions(+), 72 deletions(-) diff --git a/base/base_tests/cache_test.cpp b/base/base_tests/cache_test.cpp index 8447d03d22..a035deaf2f 100644 --- a/base/base_tests/cache_test.cpp +++ b/base/base_tests/cache_test.cpp @@ -3,6 +3,23 @@ #include "base/macros.hpp" #include "base/stl_add.hpp" +#include "std/bind.hpp" + +namespace +{ +class SimpleFunctor +{ +public: + void operator() (char c) + { + m_v.push_back(c); + } + vector m_v; +}; + +double constexpr kEpsilon = 1e-6; +} // namespace + UNIT_TEST(CacheSmoke) { char const s [] = "1123212434445"; @@ -33,7 +50,7 @@ UNIT_TEST(CacheSmoke) UNIT_TEST(CacheSmoke_0) { - my::Cache cache(3); + my::Cache cache(3); // it contains 2^3=8 elements bool found = true; cache.Find(0, found); TEST(!found, ()); @@ -41,3 +58,66 @@ UNIT_TEST(CacheSmoke_0) cache.ForEachValue(MakeBackInsertFunctor(v)); TEST_EQUAL(v, vector(8, 0), ()); } + +UNIT_TEST(CacheSmoke_1) +{ + my::Cache cache(3); // it contains 2^3=8 elements + SimpleFunctor f; + cache.ForEachValue(f); // f passed by reference + TEST_EQUAL(f.m_v, vector(8, 0), ()); +} + +UNIT_TEST(CacheSmoke_2) +{ + my::Cache cache(3); // it contains 2^3=8 elements + SimpleFunctor f; + cache.ForEachValue(ref(f)); // f passed by reference + TEST_EQUAL(f.m_v, vector(8, 0), ()); +} + +UNIT_TEST(CacheSmoke_3) +{ + my::CacheWithStat cache(3); // it contains 2^3=8 elements + + // 0 access, cache miss is 0 + TEST(my::AlmostEqualAbs(0.0, cache.GetCacheMiss(), kEpsilon), ()); + + bool found = true; + cache.Find(1, found); + TEST(!found, ()); + // 1 access, 1 miss, cache miss = 1/1 = 1 + TEST(my::AlmostEqualAbs(1.0, cache.GetCacheMiss(), kEpsilon), ()); + + found = false; + cache.Find(1, found); + TEST(found, ()); + // 2 access, 1 miss, cache miss = 1/2 = 0.5 + TEST(my::AlmostEqualAbs(0.5, cache.GetCacheMiss(), kEpsilon), ()); + + found = false; + cache.Find(2, found); + TEST(!found, ()); + // 3 access, 2 miss, cache miss = 2/3 = 0.6(6) + TEST(my::AlmostEqualAbs(2.0/3.0, cache.GetCacheMiss(), kEpsilon), ()); + + cache.Reset(); + + // 0 access, cache miss is 0 + TEST(my::AlmostEqualAbs(0.0, cache.GetCacheMiss(), kEpsilon), ()); +} + +UNIT_TEST(CacheSmoke_4) +{ + my::CacheWithStat cache(3); // it contains 2^3=8 elements + SimpleFunctor f; + cache.ForEachValue(f); // f passed by reference + TEST_EQUAL(f.m_v, vector(8, 0), ()); +} + +UNIT_TEST(CacheSmoke_5) +{ + my::CacheWithStat cache(3); // it contains 2^3=8 elements + SimpleFunctor f; + cache.ForEachValue(ref(f)); // f passed by reference + TEST_EQUAL(f.m_v, vector(8, 0), ()); +} diff --git a/base/cache.hpp b/base/cache.hpp index 30b51f2911..625a373c46 100644 --- a/base/cache.hpp +++ b/base/cache.hpp @@ -6,7 +6,7 @@ namespace my { - // Simple Cache that stores list of most recently used values. + // Simple cache that stores list of values. template class Cache { private: @@ -14,7 +14,7 @@ namespace my Cache & operator = (Cache const &); // Not implemented. public: - // Create cache with maximum size @maxCachedObjects. + // @logCacheSize is pow of two for number of elements in cache. explicit Cache(uint32_t logCacheSize) : m_Cache(new Data[1 << logCacheSize]), m_HashMask((1 << logCacheSize) - 1) { @@ -59,7 +59,7 @@ namespace my } template - void ForEachValue(F f) + void ForEachValue(F && f) { for (uint32_t i = 0; i <= m_HashMask; ++i) f(m_Cache[i].m_Value); @@ -106,4 +106,53 @@ namespace my Data * const m_Cache; uint32_t const m_HashMask; }; + + // Simple cache that stores list of values and provides cache missing statistics. + // CacheWithStat class has the same interface as Cache class + template + class CacheWithStat + { + public: + // @logCacheSize is pow of two for number of elements in cache. + explicit CacheWithStat(uint32_t logCacheSize) + : m_cache(logCacheSize), m_miss(0), m_access(0) + { + } + + double GetCacheMiss() const + { + if (m_access == 0) + return 0.0; + return (double)m_miss / (double)m_access; + } + + uint32_t GetCacheSize() const { return m_cache.GetCacheSize(); } + + TValue & Find(TKey const & key, bool & found) + { + ++m_access; + TValue & res = m_cache.Find(key, found); + if (!found) + ++m_miss; + return res; + } + + template + void ForEachValue(F && f) + { + m_cache.ForEachValue(std::forward(f)); + } + + void Reset() + { + m_cache.Reset(); + m_access = 0; + m_miss = 0; + } + + private: + Cache m_cache; + uint32_t m_miss; + uint32_t m_access; + }; } diff --git a/routing/features_road_graph.cpp b/routing/features_road_graph.cpp index 678ae89be4..412170717d 100644 --- a/routing/features_road_graph.cpp +++ b/routing/features_road_graph.cpp @@ -16,17 +16,15 @@ namespace routing namespace { -uint32_t const FEATURE_CACHE_SIZE = 10; -double const READ_CROSS_EPSILON = 1.0E-4; +uint32_t constexpr kPowOfTwoForFeatureCacheSize = 10; // cache contains 2 ^ kPowOfTwoForFeatureCacheSize elements +double constexpr kReadCrossEpsilon = 1.0E-4; } // namespace FeaturesRoadGraph::FeaturesRoadGraph(IVehicleModel const & vehicleModel, Index const & index, MwmSet::MwmId const & mwmID) : m_index(index), m_mwmID(mwmID), m_vehicleModel(vehicleModel), - m_cache(FEATURE_CACHE_SIZE), - m_cacheMiss(0), - m_cacheAccess(0) + m_cache(kPowOfTwoForFeatureCacheSize) { } @@ -56,8 +54,7 @@ public: return; // load feature from cache - IRoadGraph::RoadInfo const & ri = m_graph.GetCachedRoadInfo(fID.m_offset, true /*preload*/, ft, speedKMPH); - ASSERT_EQUAL(speedKMPH, ri.m_speedKMPH, ()); + IRoadGraph::RoadInfo const & ri = m_graph.GetCachedRoadInfo(fID.m_offset, ft, speedKMPH); m_edgesLoader(fID.m_offset, ri); } @@ -67,26 +64,18 @@ private: IRoadGraph::CrossEdgesLoader & m_edgesLoader; }; -void FeaturesRoadGraph::LoadFeature(uint32_t featureId, FeatureType & ft) const -{ - Index::FeaturesLoaderGuard loader(m_index, m_mwmID); - loader.GetFeature(featureId, ft); - ft.ParseGeometry(FeatureType::BEST_GEOMETRY); - - ASSERT_EQUAL(ft.GetFeatureType(), feature::GEOM_LINE, (featureId)); - ASSERT_GREATER(ft.GetPointsCount(), 1, (featureId)); -} - IRoadGraph::RoadInfo FeaturesRoadGraph::GetRoadInfo(uint32_t featureId) const { - FeatureType ft; - return GetCachedRoadInfo(featureId, false /*preload*/, ft, 0.0 /*speedKMPH*/); + RoadInfo const & ri = GetCachedRoadInfo(featureId); + ASSERT_GREATER(ri.m_speedKMPH, 0.0, ()); + return ri; } double FeaturesRoadGraph::GetSpeedKMPH(uint32_t featureId) const { - FeatureType ft; - return GetCachedRoadInfo(featureId, false /*preload*/, ft, 0.0 /*speedKMPH*/).m_speedKMPH; + double const speedKMPH = GetCachedRoadInfo(featureId).m_speedKMPH; + ASSERT_GREATER(speedKMPH, 0.0, ()); + return speedKMPH; } double FeaturesRoadGraph::GetMaxSpeedKMPH() const @@ -99,8 +88,8 @@ void FeaturesRoadGraph::ForEachFeatureClosestToCross(m2::PointD const & cross, { CrossFeaturesLoader featuresLoader(*this, edgesLoader); m_index.ForEachInRect(featuresLoader, - m2::RectD(cross.x - READ_CROSS_EPSILON, cross.y - READ_CROSS_EPSILON, - cross.x + READ_CROSS_EPSILON, cross.y + READ_CROSS_EPSILON), + m2::RectD(cross.x - kReadCrossEpsilon, cross.y - kReadCrossEpsilon, + cross.x + kReadCrossEpsilon, cross.y + kReadCrossEpsilon), scales::GetUpperScale()); } @@ -114,37 +103,46 @@ double FeaturesRoadGraph::GetSpeedKMPHFromFt(FeatureType const & ft) const return m_vehicleModel.GetSpeed(ft); } +IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(uint32_t featureId) const +{ + bool found = false; + RoadInfo & ri = m_cache.Find(featureId, found); + + if (found) + return ri; + + FeatureType ft; + Index::FeaturesLoaderGuard loader(m_index, m_mwmID); + loader.GetFeature(featureId, ft); + ASSERT_EQUAL(ft.GetFeatureType(), feature::GEOM_LINE, (featureId)); + + ft.ParseGeometry(FeatureType::BEST_GEOMETRY); + + ri.m_bidirectional = !IsOneWay(ft); + ri.m_speedKMPH = GetSpeedKMPHFromFt(ft); + ft.SwapPoints(ri.m_points); + + return ri; +} + IRoadGraph::RoadInfo const & FeaturesRoadGraph::GetCachedRoadInfo(uint32_t featureId, - bool preload, FeatureType & ft, double speedKMPH) const { bool found = false; RoadInfo & ri = m_cache.Find(featureId, found); - if (!found) - { - if (preload) - { - // ft must be set - ASSERT(featureId == ft.GetID().m_offset, ()); - ft.ParseGeometry(FeatureType::BEST_GEOMETRY); - } - else - { - LoadFeature(featureId, ft); - speedKMPH = GetSpeedKMPHFromFt(ft); - } + if (found) + return ri; - ri.m_bidirectional = !IsOneWay(ft); - ri.m_speedKMPH = speedKMPH; - ft.SwapPoints(ri.m_points); + // ft must be set + ASSERT_EQUAL(featureId, ft.GetID().m_offset, ()); - m_cacheMiss++; - } - m_cacheAccess++; + ft.ParseGeometry(FeatureType::BEST_GEOMETRY); - ASSERT_EQUAL(ri.m_speedKMPH, GetSpeedKMPHFromFt(ft), ()); + ri.m_bidirectional = !IsOneWay(ft); + ri.m_speedKMPH = speedKMPH; + ft.SwapPoints(ri.m_points); return ri; } diff --git a/routing/features_road_graph.hpp b/routing/features_road_graph.hpp index 61908c9b9f..bc71e4d66c 100644 --- a/routing/features_road_graph.hpp +++ b/routing/features_road_graph.hpp @@ -27,12 +27,7 @@ public: inline MwmSet::MwmId const & GetMwmID() const { return m_mwmID; } - double GetCacheMiss() const - { - if (m_cacheAccess == 0) - return 0.0; - return (double)m_cacheMiss / (double)m_cacheAccess; - } + double GetCacheMiss() const { return m_cache.GetCacheMiss(); } protected: // IRoadGraph overrides: @@ -47,13 +42,9 @@ private: bool IsOneWay(FeatureType const & ft) const; double GetSpeedKMPHFromFt(FeatureType const & ft) const; - void LoadFeature(uint32_t featureId, FeatureType & ft) const; - // Optimization: - // If preload is set to true then feature ft is set and speedKMPH contains valid value, - // otherwise feature is not set and must be load and speed is not valid. + RoadInfo const & GetCachedRoadInfo(uint32_t featureId) const; RoadInfo const & GetCachedRoadInfo(uint32_t featureId, - bool preload, FeatureType & ft, double speedKMPH) const; @@ -61,9 +52,7 @@ private: MwmSet::MwmId const m_mwmID; IVehicleModel const & m_vehicleModel; - mutable my::Cache m_cache; - mutable uint32_t m_cacheMiss; - mutable uint32_t m_cacheAccess; + mutable my::CacheWithStat m_cache; }; } // namespace routing diff --git a/routing/routing_algorithm.cpp b/routing/routing_algorithm.cpp index fe5ecdd513..5bfa8f5e81 100644 --- a/routing/routing_algorithm.cpp +++ b/routing/routing_algorithm.cpp @@ -56,13 +56,10 @@ public: for (auto const & e : edges) { - double const speedKMPH = m_roadGraph.GetSpeedKMPH(e); - if (speedKMPH <= 0.0) - continue; - ASSERT_EQUAL(v, e.GetStartJunction(), ()); - adj.emplace_back(e.GetEndJunction(), TimeBetweenSec(e.GetStartJunction(), e.GetEndJunction(), speedKMPH * KMPH2MPS)); + double const speedMPS = m_roadGraph.GetSpeedKMPH(e) * KMPH2MPS; + adj.emplace_back(e.GetEndJunction(), TimeBetweenSec(e.GetStartJunction(), e.GetEndJunction(), speedMPS)); } } @@ -76,13 +73,10 @@ public: for (auto const & e : edges) { - double const speedKMPH = m_roadGraph.GetSpeedKMPH(e); - if (speedKMPH <= 0.0) - continue; - ASSERT_EQUAL(v, e.GetEndJunction(), ()); - adj.emplace_back(e.GetStartJunction(), TimeBetweenSec(e.GetStartJunction(), e.GetEndJunction(), speedKMPH * KMPH2MPS)); + double const speedMPS = m_roadGraph.GetSpeedKMPH(e) * KMPH2MPS; + adj.emplace_back(e.GetStartJunction(), TimeBetweenSec(e.GetStartJunction(), e.GetEndJunction(), speedMPS)); } }