Memorização — Desempenho de código

Mar 02 2023
O que é Memoização? Memorização é uma técnica usada na programação de computadores para otimizar o desempenho de funções chamadas repetidamente com os mesmos argumentos de entrada. A ideia é armazenar em cache os resultados das chamadas de função anteriores e retornar o resultado armazenado em cache, em vez de recalcular o resultado quando a função é chamada com os mesmos argumentos.

O que é Memoização?

Memorização é uma técnica usada na programação de computadores para otimizar o desempenho de funções chamadas repetidamente com os mesmos argumentos de entrada. A ideia é armazenar em cache os resultados das chamadas de função anteriores e retornar o resultado armazenado em cache, em vez de recalcular o resultado quando a função é chamada com os mesmos argumentos.

Em JavaScript

Você pode implementar a memoização criando um objeto de cache e usando-o para armazenar os resultados de chamadas de função anteriores. Aqui está um exemplo de como implementar a memoização em JavaScript:

function memoize(func) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key]) {
      return cache[key];
    } else {
      const result = func.apply(this, args);
      cache[key] = result;
      return result;
    }
  };
}

Dentro da função retornada, criamos uma string keyusando JSON.stringifypara serializar os argumentos de entrada. Verificamos se a chave existe no objeto de cache e, se existir, retornamos o resultado armazenado em cache. Caso contrário, chamamos a função original com os argumentos de entrada usando o applymétodo, armazenamos o resultado no objeto de cache e retornamos o resultado.

Aqui está um exemplo de como usar a memoizefunção para memorizar uma função Fibonacci simples:

const fibonacci = memoize(function(n) {
  if (n <= 1) {
    return n;
  } else {
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
});

console.log(fibonacci(10)); // 55
console.log(fibonacci(10)); // 55 (cached result)

NodeJS + Redis = Melhor desempenho

Node.js é um tempo de execução de JavaScript do lado do servidor que fornece um modelo de programação assíncrono orientado a eventos. O Redis é um armazenamento de estrutura de dados na memória de código aberto que geralmente é usado como banco de dados, cache e agente de mensagens. No Node.js, você pode usar o Redis para armazenar e recuperar dados na memória, o que pode ajudar a melhorar o desempenho do seu aplicativo.

Aqui está um exemplo para você entender o desempenho ao usar a técnica de cache. E abaixo, passaremos para um código mais prático. Primeiro, vamos instalar os pacotes npm:

npm install express body-parser mongoose redis --save

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const redis = require('redis');

const app = express();

app.use(bodyParser.json());

// connect to redis, use default port and address
const client = redis.createClient('redis://127.0.0.1:6379');
client.on("error", (err) => {
console.error(err);
});

// MongoDB connection
mongoose.connect('mongodb://localhost:27017/', {
dbName: 'notes',
useNewUrlParser: true,
useUnifiedTopology: true
}, err => err ? console.log(err) : console.log('Connected to database'));

// Mongoose Model
const NoteSchema = new mongoose.Schema({
title: String,
note: String
});

const Note = mongoose.model('Note', NoteSchema);

// api post a note
app.post('/api/notes', (req, res, next) => {const { title, note } = req.body;

const _note = new Note({
    title: title,
    note: note
});
// save to MongoDB
_note.save((err, note) => {
    if (err) {
        return res.status(404).json(err);
    }

    // If successful in MongoDB, then add it to Redis
    client.setex(note.id, 60, JSON.stringify(note), (err, reply) => {
        if (err) {
            console.log(err);
        }
        console.log(reply);
    });

    return res.status(201).json({
        message: 'Note has been saved',
        note: note
    });
})
});
const isCached = (req, res, next) => {
const { id } = req.params;

// First check in Redis
client.get(id, (err, data) => {
    if (err) {
        console.log(err);
    }
    if (data) {
        const response = JSON.parse(data);
        return res.status(200).json(response);
    }
    next();
});
}
// When retrieving, we will get it from Redis first instead of accessing MongoDB to get this data.
app.get('/api/notes/:id', isCached, (req, res, next) => {
const { id } = req.params;

Note.findById(id, (err, note) => {
    if (err) {
        return res.status(404).json(err);
    }
    return res.status(200).json({
        note: note
    });
});
});

app.listen(3000, () => console.log('Server running at port 3000'));

Nas linhas de código acima, prestamos atenção à configuração e obtenção do Redis:

client.setex(note.id, 60, JSON.stringify(note), (err, reply) => {
if (err) {
console.log(err);
}
console.log(reply);
});

segundos: Tempo de expiração em segundos como um número.

value: Dados reais a serem armazenados no Redis. Deve ser uma corda. Portanto, estamos serializando objetos em strings.

retorno de chamada: O retorno de chamada leva dois parâmetros err e responder.

E ao recuperar um documento, ele irá para isCached() para recuperá-lo. Se houver algum problema, como o tempo de cache expirou, você continuará recuperando dados no MongoDB.

app.get('/api/notes/:id', isCached, (req, res, next) => {

scss
Copy code
const { id } = req.params;

Note.findById(id, (err, note) => {
    if (err) {
        return res.status(404).json(err);
    }
    return res.status(200).json({
        note: note
    });
});
});