Memoization in HMPL. DevBlog #1

In version 2.1.3, among other things, new functionality was introduced to improve the performance of sites using hmpl.js.

Query memoization is one of the best optimization techniques in programming. “What is it and how does it work?” – I will try to answer these questions in this article.

By the way, you can find all the innovations related to the template language in the thematic tg channel.

The concept of memoization

Before moving on to considering specific functionality, let’s first consider this concept in programming in general.

Memoization – saving the results of executing functions to prevent repeated calculations. This is one of the optimization methods used to increase the speed of execution of computer programs.

Before calling a function, it is checked whether the function has been called before:

  • if it has not been called, then the function is called and the result of its execution is saved;

  • if called, the saved result is used.

An example of memoization in JavaScript would be the following code:

// До внедрения мемоизации

const reverseText = (string) =>{
  const len = string.length;
  let reversedText = new Array();
  let j = 0;
  for (let i = len - 1; i >= 0; i--){
    reversedText[j] = string[i];
    j++;
  }
  return reversedText.join('');
}

const a = reverseText("a123"); // Срабатывает цикл
const b = reverseText("a123"); // Срабатывает цикл

// После внедрения мемоизации

const memoizeFn = (fn) => {
  const cache = {};
  return (string) => {
    if (string in cache) return cache[string];
    return (cache[string] = fn(string));
  };
};

const memoizedReverseText = memoizeFn(reverseText);

const a = memoizedReverseText("a123"); // Срабатывает цикл
const b = memoizedReverseText("a123"); // Не срабатывает цикл

We create a wrapper (higher-order function) over an already existing function, which adds a certain state, denoted cache. The cache stores the arguments and, accordingly, the values ​​received from the function. By the key, if it is equal to the input argument, you can already get the finished result in the object without reversing the string.

This is the basis on which all optimization is built. It’s trite, we don’t repeat the code again, but only take the already calculated value.

Also, the object was called a cache for a reason. A cache is a fast-access intermediate buffer containing information that is most likely to be requested.

Here you can already remember the memory, which is located between the processor and RAM, but this is clearly a slightly different story.

In general, memoization is widely used in software, which makes it an excellent way to quickly speed up a particular code execution. But this method, of course, also has disadvantages.

The main disadvantage, of course, is the unnecessary allocation of memory for storing results. If a function is executed once, then there is simply no point in memoizing its return values.

Memoization in HMPL

Since HMPL is a template language for displaying a user interface from a server to a client, http requests will need to be memoized. Accordingly, the expected result will be saving the HTML markup. Here's an example of how HMPL works:

const newDiv = compile(
  `<div>
      <button>Get the square root of 256</button>
      <div>{{ src: "/api/getTheSquareRootOf256", after: "click:button" }}</div>
  </div>`
)().response;

Based on the event listener on the button, it is obvious that a request to the server can be sent as many times as desired, but the square root of 256 is 16, so it will be the same request, theoretically.

So, the problem with previous versions was that a new element was constantly placed for each request, even if the response from the request was the same.

Especially to optimize this process, an additional field was introduced, which has the name memo.

const newDiv = compile(
  `<div>
      <button>Get the square root of 256</button>
      <div>{{ src: "/api/getTheSquareRootOf256", memo:true, after: "click:button" }}</div>
  </div>`
)().response;

The value it takes is true or false. Only works for request objects that are theoretically sent repeatedly.

To visually display the process, a small diagram was created:

1. Memoization in HMPL (source)

The concept of a cache, which has already been used earlier, also appears here. Also, if we take HTTP requests, then additional concepts are considered fresh And stale response, revalidation, etc. This all comes from the HTTP cache theory. This point is discussed in more detail in the mdn documentation here. An analogy can be drawn that memoization in HMPL is logically comparable to no-cache cache mode value.

An example of hmpl working without memoization and with it in the DOM:

Without memoization:

With memoization:

During the test, the button to receive the user interface from the server was actively pressed.

In one case, we constantly replace div in the new one, although the answer from the server comes the same, in the other one, we save the same element, but only if the answers are the same.

Memoization for file types with HMPL extension

Also, memoization will be available not only for one request object, but also for all objects received from the function compile with option enabled memo:

const templateFn = hmpl.compile(
  `{ 
     {
       "src":"/api/test" 
     } 
   }`,
  {
    memo: true,
  }
);

const elementObj1 = templateFn();
const elementObj2 = templateFn();

It will not interfere in any way with other request objects that fire only once.

Since the work is based on the compile function hmpl-loaderthen an option will soon be added in which it will be possible to enable memoization for all files with the extension .hmpl:

module.exports = {
  module: {
    rules: [
      {
        test: /\.hmpl$/i,
        use: [{ loader: "hmpl-loader", options: { memo: true } }],
      },
    ],
  },
};

Already open for this issue.

Thank you all very much for reading this article!

PS More changes that have been made to the new version 2.1.3 can be found here.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *