Why you should Memoize your functions?

Anass Daoudi
5 min readFeb 16, 2020

--

Because your CPU will thank you enough for doing it!

First, what is Memoization?

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
source

So the main idea here, is that you don’t have to recalculate things when you get same inputs as last time’s execution, above all when the computation is complex. This computation could be side network effects, math calculations, virtual dom calculation, text parsing, searching, arrays sorting…

Note: I’m talking about memoization of pure function which always return same output when same input(s) no matter the environment, context, time.

Please find all the source code shown in this article in my Github repository:

Challenge:

We have this state object:

state.js

We want to only select todos based on the filter, without recalculating when the same inputs(todos and filter) occur again.

Solution:

As you’ve read in the above definition part, memoization is used to optimize expensive function calls. So, to simulate expensive calculation, I’ve created the function below which when injected inside another one makes its execution slow.

slowFunctionExecution.js

The next function takes a function as a parameter and execute it. It also displays the passed function execution time.

displayFunctionExecutionTime.js

I’ll use this function to compare none memoized and memoized function executions times later

Solution without memoization:

I explain below, step by step, the next code :

noneMemoizedGetTodos.js

First, I’ve created this pure function noneMemoizedGetTodos. It takes todos array and the filter string and return selected todos based on their status using filter array method. As said before, slowFunctionExecution is injected inside to slow the execution.

Next, we have the state containing filter defaults to ‘active’ and some todos.

Finally I execute noneMemoizedGetTodos function twice with same inputs (the state object above in the code), and log the results to the console.

The next image shows the execution of noneMemoizedGetTodos.js file:

execution of noneMemoizedGetTodos.js

Interpretation:

As expected, this function get executed twice taking 101ms in first execution and 80ms in second one, both counting 2 active todos which is correct.

What if we can make second execution takes ≈0ms while getting same results?

In fact we can, since we already calculated the output in the first execution for the same inputs. I’ll demonstrate that in the next part about memoization.

Solution with memoization:

Part 1:

In this part, I’ve implemented memoization to this specific case (getting todos based on the given filter).

For that, I’ve created 3 variables to keep track of last todos, last filter and last result. When both todos and filter don’t change, I return last kept result.

ownMemoizedGetTodos.js

The next image shows the execution of ownMemoizedGetTodos.js file:

execution of ownMemoizedGetTodos.js

Interpretation:

As you see ownMemoizedGetTodos didn’t recalculate todos in the second call since the todos and the filter still the same as the first execution.

We’ve made our program fast by 95ms !

Note: Since ownMemoizedGetTodos uses reference equality comparison (===) to compare todos and filter with previous ones, these inputs should be immutable!

Note: You can implement caching using deep equality comparison, which doesn’t force immutability usage.

You may ask, should I implement this caching logic for all my program functions according to their specific use cases? This is lot of code to add!!

Well no, you may implement a general caching solution, which returns the cached result or recalculates new one based on if the inputs changed or not.

The good news is that you haven’t to do, since some libraries already do that for you, and this is the subject for the next part.

Part 2:

In this part, I am going to memoize getting todos using Reselect redux’s library.

First, install it:

add reselect dependency

Reselect library provides createSelector function.

Signature:

createSelector(…inputSelectors | [inputSelectors], transformFunction)
createSelector returns a selector.

Example:

Creating selector:

selector creation example

noneMemoizedGetTodos is same function as the one from solution without memoization above.

Executing selector:

selector execution example

Explanation:

To execute the memoizedGetTodos selector, you have to provide the state (some input). Then the input selectors (here we have 2, one that selects todos and the other one selects the filter) get executed. If their return values don’t change compared to their previous executions, the transform function will not execute and the cached result will be returned.

Note: by default, input selectors returned values are compared with their previous ones using reference equality comparison (===), so their inputs (input passed to memoizedGetTodos in our case which is state object) must be immutable!

Note: You can customize your selector to use deep equality comparison instead, without enforcing immutability usage.

So let’s use reselect in our main example:

memoizedGetTodos.js

Let’s execute memoizedGetTodos.js file and interpret the results:

execution of memoizedGetTodos.js

The first execution returns 4 todos because the filter is ‘all’.
The second returns cached data without executing the transform function since the inputs are still the same.
The third recalculates since the filter has changed to ‘done’.
The fourth returns cached data without executing the transform function since the inputs are still the same.

As noted above, I’ve changed filter by creating new state object and not mutating old one using the following line of code:

newState.js

Update: I’ll make PART 2 of this article to go more on Memoization and simplify its usage. Things you probably don’t want to miss!

That’s it.

Thanks for your reading time.

If you liked the article, please clap it to let me know!

Waiting for your comments too.

In my future eventual articles, I am going to talk about my personal journey in the full stack development; in front-end using (React.js, Redux, React Native, TypeScript, Webpack…), in back-end using (Node.js, Express.js, TypeScript, Java, php…) while maintaining my operations using (Jenkins, Docker, NGINX…) and hosting on clouds like Heroku and Netlify.

Check more on me in my web site
Check my other blogs here

--

--

Anass Daoudi

full stack web developer / software and computer science engineer www.anass-daoudi.com