JavaScript: il pattern/funzione memoizer e la performance

JavaScript: il pattern/funzione memoizer e la performance

Uno dei punti deboli nella performance JavaScript è costituito dalle funzioni ripetute. Più sono gli oggetti o gli elementi su cui queste funzioni operano e maggiore sarà il tempo di esecuzione. Per questo motivo gli sviluppatori JavaScript (a partire da Douglas Crockford) hanno introdotto il concetto di memoizer o di funzione di tipo memoize. In pratica si tratta di una funzione che ne esegue un'altra mettendola in cache con il suo valore restituito, in modo che le successive chiamate usino la copia cache e non la funzione originale. Vediamone i dettagli.

Ecco un'implementazione tipica di questo pattern:


var memoize = function(func) { 
    return function() { 
        // Aggiungo un oggetto memory alla funzione se non esiste già
        
        func.memory = func.memory || {}; 
		        
        /* Creo una chiave per memorizzare ed accedere alla funzione all'interno dell'oggetto memory.
           La chiave dovrebbe essere una combinazione di tutti gli argomenti passati alla funzione
           per fare in modo che sia unica in base alle combinazioni di input */
         
        arguments.join = Array.prototype.join;
        var key = arguments.join("|"); 
		
        // La chiave esiste nell'oggetto memory?
         
        if (key in func.memory) { 
             // Se esiste, restituisco il valore associato per evitare che venga ricomputato
             
            return func.memory[key];
		} else {
		     // Se non esiste, eseguo la funzione associata e salvo il risultato
		    //  nell'oggetto memory
		     
            func.memory[key] = func.apply(this, arguments); 
			
            // Restituisco il nuovo valore salvato, risultato dell'esecuzione della funzione 
            return func.memory[key]; 
        } 
    } 
};

Il tipico esempio pratico dell'uso di questa funzione è quello del calcolo del fattoriale ripetuto per un numero molto elevato di volte:


var computeFactorial = function(input) { 
    var result; 
    for (var count = 0; count < 99999; count++) { 
        result = 1; 
        for (var num = 2; num <= input; num++) { 
            result *= num; 
        } 
    } 
    return result; 
} 

Ecco come viene usato il pattern:


computeFactorial = memoize(computeFactorial); 

// Osserviamo la velocità di esecuzione

computeFactorial(100); // ~945 millisecondi 
computeFactorial(50); // ~506 millisecondi 
computeFactorial(100); // 0-1 millisecondi (usa il valore memorizzato)
computeFactorial(50); // 0-1 millisecondi  (usa il valore memorizzato)

Quando viene usato il valore memorizzato, i benefici sulla performance sono evidenti.

Torna su