From 42fcf557b820f33a9e15aac7a2b1fd319d93c40b Mon Sep 17 00:00:00 2001 From: Gabriel Ozouf Date: Tue, 9 Jun 2020 16:19:23 +0200 Subject: [PATCH] [apps/graph] Limited number of cached functions The caches used for function values memoization are now stored in ContinuousFunctionStore : there are now only a fixed number, instead of one per function. This effectively enables caching only for the first few functions on screen, while reducing the memory usage. Change-Id: I2ade091717f73a14a756fe527c773db8e8627be7 --- apps/graph/continuous_function_store.h | 3 +++ apps/graph/graph/graph_view.cpp | 7 ++++--- apps/shared/continuous_function.cpp | 10 +++++----- apps/shared/continuous_function.h | 7 +++++-- apps/shared/continuous_function_cache.cpp | 20 ++++++++++++++------ apps/shared/continuous_function_cache.h | 6 +++++- apps/shared/curve_view.cpp | 8 ++++---- apps/shared/curve_view.h | 6 +++--- 8 files changed, 43 insertions(+), 24 deletions(-) diff --git a/apps/graph/continuous_function_store.h b/apps/graph/continuous_function_store.h index 2670a08e8..eb506822b 100644 --- a/apps/graph/continuous_function_store.h +++ b/apps/graph/continuous_function_store.h @@ -16,6 +16,7 @@ public: return recordSatisfyingTestAtIndex(i, &isFunctionActiveOfType, &plotType); } Shared::ExpiringPointer modelForRecord(Ion::Storage::Record record) const { return Shared::ExpiringPointer(static_cast(privateModelForRecord(record))); } + Shared::ExpiringPointer cacheAtIndex(int i) const { return (i < Shared::ContinuousFunctionCache::k_numberOfAvailableCaches) ? Shared::ExpiringPointer(k_functionCaches + i) : nullptr; } Ion::Storage::Record::ErrorStatus addEmptyModel() override; private: const char * modelExtension() const override { return Ion::Storage::funcExtension; } @@ -26,6 +27,8 @@ private: return isFunctionActive(model, context) && plotType == static_cast(model)->plotType(); } mutable Shared::ContinuousFunction m_functions[k_maxNumberOfMemoizedModels]; + mutable Shared::ContinuousFunctionCache k_functionCaches[Shared::ContinuousFunctionCache::k_numberOfAvailableCaches]; + }; } diff --git a/apps/graph/graph/graph_view.cpp b/apps/graph/graph/graph_view.cpp index cc4358d9d..226838e91 100644 --- a/apps/graph/graph/graph_view.cpp +++ b/apps/graph/graph/graph_view.cpp @@ -27,7 +27,8 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { const int activeFunctionsCount = functionStore->numberOfActiveFunctions(); for (int i = 0; i < activeFunctionsCount ; i++) { Ion::Storage::Record record = functionStore->activeRecordAtIndex(i); - ExpiringPointer f = functionStore->modelForRecord(record);; + ExpiringPointer f = functionStore->modelForRecord(record); + ExpiringPointer cch = functionStore->cacheAtIndex(i); Shared::ContinuousFunction::PlotType type = f->plotType(); Poincare::Expression e = f->expressionReduced(context()); if (e.isUndefined() || ( @@ -63,7 +64,7 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { Poincare::Context * c = (Poincare::Context *)context; return f->evaluateXYAtParameter(t, c); }, - &ContinuousFunctionCache::PrepareCache); + &ContinuousFunctionCache::PrepareCache, cch.operator->()); /* Draw tangent */ if (m_tangent && record == m_selectedRecord) { float tangentParameterA = f->approximateDerivative(m_curveViewCursor->x(), context()); @@ -86,7 +87,7 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { return f->evaluateXYAtParameter(t, c); }, f.operator->(), context(), false, f->color(), true, false, 0.0f, 0.0f, /* drawCurve's default arguments */ - &ContinuousFunctionCache::PrepareCache); + &ContinuousFunctionCache::PrepareCache, cch.operator->()); } } diff --git a/apps/shared/continuous_function.cpp b/apps/shared/continuous_function.cpp index b00f69046..59a9b43d7 100644 --- a/apps/shared/continuous_function.cpp +++ b/apps/shared/continuous_function.cpp @@ -125,7 +125,7 @@ void ContinuousFunction::setPlotType(PlotType newPlotType, Poincare::Preferences recordData()->setPlotType(newPlotType); - cache()->clear(); + clearCache(); // Recompute the layouts m_model.tidy(); @@ -253,12 +253,12 @@ float ContinuousFunction::tMax() const { void ContinuousFunction::setTMin(float tMin) { recordData()->setTMin(tMin); - cache()->clear(); + clearCache(); } void ContinuousFunction::setTMax(float tMax) { recordData()->setTMax(tMax); - cache()->clear(); + clearCache(); } void * ContinuousFunction::Model::expressionAddress(const Ion::Storage::Record * record) const { @@ -353,7 +353,7 @@ Poincare::Expression ContinuousFunction::sumBetweenBounds(double start, double e Poincare::Coordinate2D ContinuousFunction::checkForCacheHitAndEvaluate(float t, Poincare::Context * context) const { Poincare::Coordinate2D res(NAN, NAN); - if (cache()->filled()) { + if (cacheIsFilled()) { res = cache()->valueForParameter(this, t); } if (std::isnan(res.x1()) || std::isnan(res.x2())) { @@ -364,7 +364,7 @@ Poincare::Coordinate2D ContinuousFunction::checkForCacheHitAndEvaluate(fl } Ion::Storage::Record::ErrorStatus ContinuousFunction::setContent(const char * c, Poincare::Context * context) { - cache()->clear(); + clearCache(); return ExpressionModelHandle::setContent(c, context); } diff --git a/apps/shared/continuous_function.h b/apps/shared/continuous_function.h index 93e8c5998..284e967b9 100644 --- a/apps/shared/continuous_function.h +++ b/apps/shared/continuous_function.h @@ -80,7 +80,10 @@ public: Poincare::Expression sumBetweenBounds(double start, double end, Poincare::Context * context) const override; // Cache - ContinuousFunctionCache * cache() const { return const_cast(&m_cache); } + ContinuousFunctionCache * cache() const { return m_cache; } + void setCache(ContinuousFunctionCache * v) { m_cache = v; } + void clearCache() { m_cache = nullptr; } + bool cacheIsFilled() const { return cache() && cache()->filled(); } Ion::Storage::Record::ErrorStatus setContent(const char * c, Poincare::Context * context) override; private: constexpr static float k_polarParamRangeSearchNumberOfPoints = 100.0f; // This is ad hoc, no special justification @@ -125,7 +128,7 @@ private: RecordDataBuffer * recordData() const; template Poincare::Coordinate2D templatedApproximateAtParameter(T t, Poincare::Context * context) const; Model m_model; - ContinuousFunctionCache m_cache; + ContinuousFunctionCache * m_cache; }; } diff --git a/apps/shared/continuous_function_cache.cpp b/apps/shared/continuous_function_cache.cpp index 567452f3c..ce8c08737 100644 --- a/apps/shared/continuous_function_cache.cpp +++ b/apps/shared/continuous_function_cache.cpp @@ -5,20 +5,28 @@ namespace Shared { constexpr int ContinuousFunctionCache::k_sizeOfCache; constexpr float ContinuousFunctionCache::k_cacheHitTolerance; +constexpr int ContinuousFunctionCache::k_numberOfAvailableCaches; // public -void ContinuousFunctionCache::PrepareCache(void * f, void * c, float tMin, float tStep) { +void ContinuousFunctionCache::PrepareCache(void * f, void * ctx, void * cch, float tMin, float tStep) { + if (!cch) { + return; + } ContinuousFunction * function = (ContinuousFunction *)f; - Poincare::Context * context = (Poincare::Context *)c; - ContinuousFunctionCache * functionCache = function->cache(); - if (functionCache->filled() && tStep / StepFactor(function) == functionCache->step()) { + Poincare::Context * context = (Poincare::Context *)ctx; + if (!function->cache()) { + ContinuousFunctionCache * cache = (ContinuousFunctionCache *)cch; + cache->clear(); + function->setCache(cache); + } + if (function->cache()->filled() && tStep / StepFactor(function) == function->cache()->step()) { if (function->plotType() == ContinuousFunction::PlotType::Cartesian) { function->cache()->pan(function, context, tMin); } return; } - functionCache->setRange(function, tMin, tStep); - functionCache->memoize(function, context); + function->cache()->setRange(function, tMin, tStep); + function->cache()->memoize(function, context); } void ContinuousFunctionCache::clear() { diff --git a/apps/shared/continuous_function_cache.h b/apps/shared/continuous_function_cache.h index 0752f75b2..046357e71 100644 --- a/apps/shared/continuous_function_cache.h +++ b/apps/shared/continuous_function_cache.h @@ -11,7 +11,11 @@ class ContinuousFunction; class ContinuousFunctionCache { public: - static void PrepareCache(void * f, void * c, float tMin, float tStep); + /* The size of the cache is chosen to optimize the display of cartesian + * function */ + static constexpr int k_numberOfAvailableCaches = 2; + + static void PrepareCache(void * f, void * ctx, void * cch, float tMin, float tStep); float step() const { return m_tStep; } bool filled() const { return m_filled; } diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index e32fd9881..239ea34d9 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -602,8 +602,8 @@ const uint8_t thickStampMask[(thickStampSize+1)*(thickStampSize+1)] = { constexpr static int k_maxNumberOfIterations = 10; -void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd, float tStep, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, bool drawStraightLinesEarly, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound, EvaluateXYForDoubleParameter xyDoubleEvaluation, PrepareContinuousFunction functionPreparator) const { - functionPreparator(model, context, tStart, tStep); +void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd, float tStep, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, bool drawStraightLinesEarly, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound, EvaluateXYForDoubleParameter xyDoubleEvaluation, PrepareContinuousFunction functionPreparator, void * cache) const { + functionPreparator(model, context, cache, tStart, tStep); float previousT = NAN; float t = NAN; float previousX = NAN; @@ -635,7 +635,7 @@ void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd } while (true); } -void CurveView::drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound, EvaluateXYForDoubleParameter xyDoubleEvaluation, PrepareContinuousFunction functionPreparator) const { +void CurveView::drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound, EvaluateXYForDoubleParameter xyDoubleEvaluation, PrepareContinuousFunction functionPreparator, void * cache) const { float rectLeft = pixelToFloat(Axis::Horizontal, rect.left() - k_externRectMargin); float rectRight = pixelToFloat(Axis::Horizontal, rect.right() + k_externRectMargin); float tStart = std::isnan(rectLeft) ? xMin : std::max(xMin, rectLeft); @@ -645,7 +645,7 @@ void CurveView::drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, flo return; } float tStep = pixelWidth(); - drawCurve(ctx, rect, tStart, tEnd, tStep, xyFloatEvaluation, model, context, true, color, thick, colorUnderCurve, colorLowerBound, colorUpperBound, xyDoubleEvaluation, functionPreparator); + drawCurve(ctx, rect, tStart, tEnd, tStep, xyFloatEvaluation, model, context, true, color, thick, colorUnderCurve, colorLowerBound, colorUpperBound, xyDoubleEvaluation, functionPreparator, cache); } void CurveView::drawHistogram(KDContext * ctx, KDRect rect, EvaluateYForX yEvaluation, void * model, void * context, float firstBarAbscissa, float barWidth, diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index 6f953a43a..280ed488c 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -19,7 +19,7 @@ public: typedef Poincare::Coordinate2D (*EvaluateXYForFloatParameter)(float t, void * model, void * context); typedef Poincare::Coordinate2D (*EvaluateXYForDoubleParameter)(double t, void * model, void * context); typedef float (*EvaluateYForX)(float x, void * model, void * context); - typedef void (* PrepareContinuousFunction)(void * model, void * context, float xMin, float xStep); + typedef void (* PrepareContinuousFunction)(void * model, void * context, void * cache, float xMin, float xStep); enum class Axis { Horizontal = 0, Vertical = 1 @@ -108,8 +108,8 @@ protected: void drawGrid(KDContext * ctx, KDRect rect) const; void drawAxes(KDContext * ctx, KDRect rect) const; void drawAxis(KDContext * ctx, KDRect rect, Axis axis) const; - void drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd, float tStep, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, bool drawStraightLinesEarly, KDColor color, bool thick = true, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, EvaluateXYForDoubleParameter xyDoubleEvaluation = nullptr, PrepareContinuousFunction functionPreparator = [](void * model, void * context, float xMin, float xStep) {}) const; - void drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, KDColor color, bool thick = true, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, EvaluateXYForDoubleParameter xyDoubleEvaluation = nullptr, PrepareContinuousFunction functionPreparator = [](void * model, void * context, float xMin, float xStep) {}) const; + void drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd, float tStep, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, bool drawStraightLinesEarly, KDColor color, bool thick = true, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, EvaluateXYForDoubleParameter xyDoubleEvaluation = nullptr,PrepareContinuousFunction functionPreparator = [](void * model, void * context, void * cache, float xMin, float xStep) {}, void * cache = nullptr) const; + void drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForFloatParameter xyFloatEvaluation, void * model, void * context, KDColor color, bool thick = true, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, EvaluateXYForDoubleParameter xyDoubleEvaluation = nullptr, PrepareContinuousFunction functionPreparator = [](void * model, void * context, void * cache, float xMin, float xStep) {}, void * cache = nullptr) const; void drawHistogram(KDContext * ctx, KDRect rect, EvaluateYForX yEvaluation, void * model, void * context, float firstBarAbscissa, float barWidth, bool fillBar, KDColor defaultColor, KDColor highlightColor, float highlightLowerBound = INFINITY, float highlightUpperBound = -INFINITY) const; void computeLabels(Axis axis);