0

I have a mathematical function called in render loop (240Hz). It's a function of some (atomic) state that very rarely changes. It calls 3 times to std::log, twice to std::sqrt, once to std::sin.

When is it worth memoizing such a function (what if the computation was more intensive)?

Will the cpu ever optimize pure functions repeatedly called with the same arguments?

float expensiveFunction(float x) { static float lastInput = -1; static float lastResult = 0; if (x == lastInput) return lastResult; ... } 
7
  • 1
    "When is it worth memoizing such a function" - I don't think there is some general rule. It's all a metter of performance measurement on a per-case bases. Commented Jun 10 at 4:06
  • I noticed you used the word atomic. If you are doing this in multiple threads, then note that static variables are not themselves thread safe, just their initialization is. If not actual multithreading then no worries. Commented Jun 10 at 4:23
  • @NathanOliver the function is only being called from one thread Commented Jun 10 at 4:31
  • 1
    rough estimates, log is about 100 cycles, sqrt is closer to 50, and sin is around 300 at most, you just saved yourself 600 cycles, on a 3 GHz machine that's 200 nanoseconds ... Commented Jun 10 at 4:48
  • 1
    @AhmedAEK I recall a video about the old Doom game. The developers even used a lookup table for the digits of pi to save the repeatedly recalculation of pi. It all depends on the use case for what can be considered as time consuming. Commented Jun 10 at 14:56

1 Answer 1

5

When is it worth memoizing such a function (what if the computation was more intensive)?

It depends. Measure and profile, premature optimisation is the root of all evil.

Will the cpu ever optimize pure functions repeatedly called with the same arguments?

If the compiler can see that you have a pure function (no side effects, result only depends on arguments and no global/static state), then it may optimize repeated calls that are "near" each other (e.g. same function). C23 has attributes that help with that but C++ does not.

It's a function of some (atomic) state that very rarely changes.

If this function is only ever called at with one value at a time, then it might be easier to cache at callsite:

class holder { public: holder(float value) : value{ value } {} float get_value() const { return value; } void set_value(float value) { if (value != this->value) { this->value = value; cache.reset(); } } float get_calculation() const { if (!cache) { cache = expensive_calculation(value); } return *cache; } private: float value{}; // or just use non-mutable `float` // and always set in ctor and `set_value` mutable std::optional<float> cache{}; }; 
Sign up to request clarification or add additional context in comments.

3 Comments

+1 for C23 attributes, however I think my way is much better. set_value is not thread safe. When the function takes multiple arguments you have to reset the cache at each set_value, and if you have multiple functions this is a combinatorial explosion.
The solution is tailor made to details you yourself provided - single value, single thread
Another addition to the "It depends" statement. If the function depends only on a simple numeric and constant input, then constexpr can eliminate the need for a cache. The compiler would replace the function call by the result value. It all depends on many things. There are very many aspects which may count for or against a memorization.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.