[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
This commit is contained in:
Gabriel Ozouf
2020-06-09 16:19:23 +02:00
committed by Émilie Feral
parent 95fef86ec0
commit 42fcf557b8
8 changed files with 43 additions and 24 deletions

View File

@@ -16,6 +16,7 @@ public:
return recordSatisfyingTestAtIndex(i, &isFunctionActiveOfType, &plotType);
}
Shared::ExpiringPointer<Shared::ContinuousFunction> modelForRecord(Ion::Storage::Record record) const { return Shared::ExpiringPointer<Shared::ContinuousFunction>(static_cast<Shared::ContinuousFunction *>(privateModelForRecord(record))); }
Shared::ExpiringPointer<Shared::ContinuousFunctionCache> cacheAtIndex(int i) const { return (i < Shared::ContinuousFunctionCache::k_numberOfAvailableCaches) ? Shared::ExpiringPointer<Shared::ContinuousFunctionCache>(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<Shared::ContinuousFunction *>(model)->plotType();
}
mutable Shared::ContinuousFunction m_functions[k_maxNumberOfMemoizedModels];
mutable Shared::ContinuousFunctionCache k_functionCaches[Shared::ContinuousFunctionCache::k_numberOfAvailableCaches];
};
}

View File

@@ -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<ContinuousFunction> f = functionStore->modelForRecord(record);;
ExpiringPointer<ContinuousFunction> f = functionStore->modelForRecord(record);
ExpiringPointer<ContinuousFunctionCache> 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->());
}
}

View File

@@ -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<float> ContinuousFunction::checkForCacheHitAndEvaluate(float t, Poincare::Context * context) const {
Poincare::Coordinate2D<float> 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<float> ContinuousFunction::checkForCacheHitAndEvaluate(fl
}
Ion::Storage::Record::ErrorStatus ContinuousFunction::setContent(const char * c, Poincare::Context * context) {
cache()->clear();
clearCache();
return ExpressionModelHandle::setContent(c, context);
}

View File

@@ -80,7 +80,10 @@ public:
Poincare::Expression sumBetweenBounds(double start, double end, Poincare::Context * context) const override;
// Cache
ContinuousFunctionCache * cache() const { return const_cast<ContinuousFunctionCache *>(&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<typename T> Poincare::Coordinate2D<T> templatedApproximateAtParameter(T t, Poincare::Context * context) const;
Model m_model;
ContinuousFunctionCache m_cache;
ContinuousFunctionCache * m_cache;
};
}

View File

@@ -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() {

View File

@@ -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; }

View File

@@ -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,

View File

@@ -19,7 +19,7 @@ public:
typedef Poincare::Coordinate2D<float> (*EvaluateXYForFloatParameter)(float t, void * model, void * context);
typedef Poincare::Coordinate2D<double> (*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);