简体   繁体   中英

Javascript Memoization in Browser Not Seeing Speed Up

I am attempting to memoize a function in javascript, to be run in browser, client side. Writing this function in R (the language I am most comfortable using). In R, I see significant benefits from using memoization (4 minutes run time to 0.02 seconds for P_n(7/10, 20, 15, 6, 1) ). When I rewrite the function in javascript, I see almost no benefit. What is the problem with my javascript code (or am I going about this the wrong way entirely)?

Below are the memoized functions in R and javascript respectively. The R function (first of the two) runs very fast compared to the naive recursion, while javascript essentially sees no difference. Some amount of memoization is happening, however, because if I run the exact same function call twice, ie P_memo(7/10, 20, 15, 6, 1) and then P_memo(7/10, 20, 15, 6, 1) again, the second call takes 0 time. The first call should be dramatically quicker due to re-use of intermediate calls in the recursion.

P_n <- (function() {

  # Memoization handled through the use of cache

  cache <- NULL

  cache_reset <- function() {
    cache <<- new.env(TRUE, emptyenv())
  }

  cache_set <- function(key, value) {
    assign(key, value, envir = cache)
  }

  cache_get <- function(key) {
    get(key, envir = cache, inherits = FALSE)
  }

  cache_has_key <- function(key) {
    exists(key, envir = cache, inherits = FALSE)
  }

  # Initialize the cache
  cache_reset()


  # This is the function that gets returned by the anonymous function and
  # becomes P_n
  function(rho, n, i, k, s) {
    nc <- paste(rho, n, i, k, s)

    # Handle "vectors" by element
    if(length(n) > 1){
      return(lapply(n, function(n) sapply(n, P_n, rho = rho, i = 1:(n+k), k = k, s = s)))  
    }
    if (length(i) > 1) {
      return(sapply(i, P_n, rho = rho, n = n, k = k, s = s))
    }

    # Cached cases
    if (cache_has_key(nc)) 
      return(cache_get(nc))


    # Everything else

    #proposition 1
    if(i == (n+k)){
      #print('Proposition 1')
      if(k >= s){
        return((rho / (rho + 1))^n)
      }else if( (k+n) <= s){
        product_iter = 1
        for(j in 1:n){
          product_iter = product_iter * ( rho + (k + j - 1)/s )
        }
        out = rho^n / product_iter
        cache_set(nc, out)
        return(out)
      }else if( k < s & s < (k + n)){
        product_iter2 = 1
        for(j in 1:(s-k)){
          product_iter2 = product_iter2 * ( rho + (k + j - 1)/s )
        }
        product_denom = ((rho + 1)^(n-s+k)) * product_iter2
        out = rho^n / product_denom
        cache_set(nc, out)
        return(out)
      }
    }
    #proposition 2
    else if(k == 0 & n == i){
      #print('Proposition 2')
      if(n <= s){
        product_iter11 = 1
        for(j in 1:n){
          product_iter11 = product_iter11 * (rho + (j - 1)/s)
        }
        return(rho^n / product_iter11)
      }else if(n > s){
        product_iter12 = 1
        for(j in 1:s){
          product_iter12 = product_iter12 * ( rho + (j - 1)/s )
        }
        product_denom12 = ((rho + 1)^(n-s)) * product_iter12
        out = rho^n / product_denom12
        cache_set(nc, out)
        return(out)
      }
    }
    #if i = 1
    else if(i == 1){
      upsum = 0
      for(j in 2:(n + k)){
        upsum = upsum + P_n(rho, n, j, k, s)
      }
      out = 1 - upsum
      cache_set(nc, out)
      return(out)
    }
    #proposition 3
    else if(n == 1 & 2 <= i & i <= k){
      #print('Proposition 3')
      if(k <= s){
        begin = rho / (rho + (i - 1)/s)

        product_iter13 = 1
        for(j in 1:(k-i+1)){
          product_iter13 = product_iter13 * (1 - rho / (rho + (k - j + 1)/s) )
        }
        out = begin * product_iter13
        cache_set(nc, out)
        return(out)
      }else if(k > s & i > s){
        out = rho / (rho+1)^(k-i+2)
        cache_set(nc, out)
        return(out)
      }else if(i <= s & s <= k){

        begin2 = rho / (( rho + 1 )^(k - s + 1) * ( rho + (i - 1)/s))

        product_iter14 = 1
        for(j in 1:(s-i)){
          product_iter14 = product_iter14 * (1 - rho / (rho + (s - j)/s) )
        }
        out = begin2 * product_iter14
        cache_set(nc, out)
        return(out)
      }

    }
    #proposition 4
    else if( n >= 2 & 2 <= i & i <= (k + n - 1)){
      #print('Proposition 4')
      if(i>s){
        begin11 = rho/(rho+1)

        product_iter21 = 0
        for(j in (i-1):(k+n-1)){
          product_iter21 = product_iter21 + (1 / (rho+1))^(j-i+1) * P_n(rho, n-1, j, k, s)
        }
        out = begin11 * product_iter21
        cache_set(nc, out)
        return(out)
      }else if(i <= s){

        begin12 = rho / (rho + (i-1)/s)
        summer1 = 0
        for(j in (i-1):(s-1)){

          product_iter22 = 1
          for(h in 1:(j-i+1)){
            product_iter22 = product_iter22 * (1 - rho / (rho + (j - h + 1) / s))
          }
          summer1 = summer1 + product_iter22 * P_n(rho, n-1, j, k, s)
        }

        product_iter23 = 1
        for(h in 1:(s-i)){
          product_iter23 = product_iter23 * (1 - rho / (rho + (s-h) / s))
        }

        summer2 = 0
        for(j in s:(k+n-1)){
          summer2 = summer2 + ((1 / (rho + 1))^(j-s+1) * P_n(rho, n-1, j, k, s)) 
        }

        bottom = product_iter23 * summer2
        inner = summer1 + bottom
        out = begin12 * inner
        cache_set(nc, out)
        return(out)
      }
    }
    #check if missed all propositions
    else{
      stop("No proposition detected")
    }

  }

})()
var P_memo = (function() {
    var memo = {};
    var slice = Array.prototype.slice;

    function f(rho, n, i, k, s){
        var args = slice.call(arguments);
        var value;
        if (args in memo) {
            return(memo[args]);
        }else{
            // NOTES ON THE UNITS OF INPUTS
            //rho: ratio of lambda / tau
            // n: arrival of nth customer
            // i: are i customers in queue
            // k : number of customers at t = 0
            // s: machines in use

            //proposition 1
            if(i == (n+k)){
                //print('Proposition 1')
                if(k >= s){
                    return(Math.pow(rho / (rho + 1), n));
                }else if( (k+n) <= s){
                    var product_iter = 1;
                    for(var j=1; j<= n; j++){
                        product_iter = product_iter * ( rho + (k + j - 1)/s );
                    }
                    return(Math.pow(rho, n) / product_iter);
                }else if( k < s && s < (k + n)){
                    var product_iter2 = 1;
                    for(var j=1; j<= s-k; j++){
                        product_iter2 = product_iter2 * ( rho + (k + j - 1)/s );
                    }
                    product_denom = Math.pow((rho + 1), (n-s+k)) * product_iter2;
                    return(Math.pow(rho, n) / product_denom);
                }
            }
            //proposition 2
            else if(k == 0 && n == i){
                if(n <= s){
                    var product_iter11 = 1;
                    for(var j=1; j<= n; j++){
                        product_iter11 = product_iter11 * (rho + (j - 1)/s);
                    }
                    return(Math.pow(rho, n) / product_iter11);
                }else if(n > s){
                    var product_iter12 = 1;
                    for(var j=1; j<= s; j++){
                        product_iter12 = product_iter12 * ( rho + (j - 1)/s );
                    }
                    product_denom12 = Math.pow((rho + 1), (n-s)) * product_iter12;
                    return(Math.pow(rho, n) / product_denom12);
                }
            }
            //if i = 1
            else if(i == 1){
                var upsum = 0;
                for(var j=2; j<= (n+k); j++){
                    upsum = upsum + f(rho, n, j, k, s);
                }
                return(1 - upsum);
            }
            //proposition 3
            else if(n == 1 && 2 <= i && i <= k){
                if(k <= s){
                    begin = rho / (rho + (i - 1)/s);

                    var product_iter13 = 1;
                    for(var j=1; j<= (k-i+1); j++){
                        product_iter13 = product_iter13 * (1 - rho / (rho + (k - j + 1)/s) );
                    }
                    return(begin * product_iter13);
                }else if(k > s && i > s){
                    return(rho / Math.pow((rho+1), (k-i+2)));
                }else if(i <= s && s <= k){

                    begin2 = rho / (Math.pow( (rho + 1), (k - s + 1)) * ( rho + (i - 1)/s));

                    var product_iter14 = 1;
                    for(var j=1; j<= (s-i); j++){
                        product_iter14 = product_iter14 * (1 - rho / (rho + (s - j)/s) );
                    }
                    return(begin2 * product_iter14);
                }

            }
            //proposition 4
            else if( n >= 2 && 2 <= i && i <= (k + n - 1)){
                if(i>s){
                    begin11 = rho/(rho+1);

                    var product_iter21 = 0;
                    for(var j=(i-1); j<= (k+n-1); j++){
                        product_iter21 = product_iter21 + Math.pow((1 / (rho+1)),(j-i+1)) * f(rho, n-1, j, k, s);
                    }
                    return(begin11 * product_iter21);
                }else if(i <= s){

                    begin12 = rho / (rho + (i-1)/s);
                    var summer1 = 0;
                    for(var j=(i-1); j<= (s-1); j++){

                        var product_iter22 = 1;
                        for(var h=1; h<=(j-1+1); h++){
                            product_iter22 = product_iter22 * (1 - rho / (rho + (j - h + 1) / s));
                        }
                        summer1 = summer1 + product_iter22 * f(rho, n-1, j, k, s);
                    }

                    var product_iter23 = 1;
                    for(var h=1; h<=(s-i); h++){
                        product_iter23 = product_iter23 * (1 - rho / (rho + (s-h) / s));
                    }

                    var summer2 = 0;
                    for(var j=s; j<= (k+n-1); j++){
                        summer2 = summer2 + (Math.pow((1 / (rho + 1)), (j-s+1)) * f(rho, n-1, j, k, s)) ;
                    }

                    bottom = product_iter23 * summer2;
                    inner = summer1 + bottom;
                    return(begin12 * inner);
                }
            }
        }

    }
    //Closure of f(), self-executing anonymous function
    return f;
})();

Your memoization has two flaws:

(1) You never add results to memo .

(2) args in memo casts args to a string. That will work for an array of numbers, but it might fail for other inputs.

I'd write a generic version of memo like this:

  const memo = fn => {
    const cache = {};
    return (...args) => {
      const key = JSON.stringify(args);
      if(key in memo) return memo[key];
      return memo[key] = fn(...args);
    };
  };

  const memoizedF = memo(f);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM