nano-memoize
Advanced tools
Comparing version 1.1.1 to 1.1.2
@@ -0,0 +0,0 @@ /* |
@@ -1,6 +0,5 @@ | ||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||
(function() { | ||
"use strict"; | ||
const vrgs = f => { | ||
const s = f+"", | ||
var vrgs = f => { | ||
var s = f+"", | ||
i = s.indexOf("..."); | ||
@@ -12,7 +11,7 @@ return i>=0 && i<s.indexOf(")" || s.indexOf("arguments")>=0); | ||
equals, // equality tester, will force use of slower multiarg approach even for single arg functions | ||
maxAge, // max cache age is ms, set higher than 0 if you want automatic clearing | ||
maxAge, // max cache age is ms, set > 0 && < Infinity if you want automatic clearing | ||
maxArgs, // max args to use for signature | ||
vargs = vrgs(fn) // set to true if function may have variable or beyond-signature arguments, default if best attempt at infering | ||
}={}) => { | ||
const s = Object.create(null), // single arg function key/value cache | ||
var s = Object.create(null), // single arg function key/value cache | ||
k = [], // multiple arg function arg key cache | ||
@@ -24,5 +23,5 @@ v = [], // multiple arg function result cache | ||
},maxAge), | ||
I = Infinity; | ||
let f, // memoized function to return | ||
u; // flag indicating a unary arg function is in use | ||
I = Infinity, | ||
f, // memoized function to return | ||
u; // flag indicating a unary arg function is in use for clear operation | ||
if(fn.length===1 && !equals && !vargs) { | ||
@@ -38,7 +37,7 @@ // for single argument functions, just use a JS object key look-up | ||
// strings must be serialized because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1" | ||
const t = typeof a, | ||
var t = typeof a, | ||
key = t === "number" || t === "boolean" || (!p && t === "object") ? a : t === "string" ? JSON.stringify(t) : p(a); | ||
// set chng timeout only when new value computed, hits will not push out the tte, but it is arguable they should not | ||
if(!p && t==="object") { | ||
let r; | ||
var r; | ||
return wm.m.get(key) || ((!c||c(key,wm.m)),wm.m.set(key,r = fn.call(this, a)),r); | ||
@@ -52,3 +51,3 @@ } | ||
wm, | ||
maxAge && maxAge<I ? d : 0, | ||
maxAge>0 && maxAge<I ? d : 0, | ||
serializer | ||
@@ -68,17 +67,10 @@ ); | ||
f = (function(f,k,v,e,c,m,...a) { | ||
const l = m||a.length; | ||
let i; | ||
for(i=0;i<k.length;i++) { // an array of arrays of args, each array represents a call signature | ||
let p = k[i]; | ||
if(p && p.length===a.length) { | ||
for(let j=0;j<=l;j++) { | ||
if(e ? !e(p[j],a[j]) : p[j]!==a[j]) break; // go to next call signature if args don't match | ||
if(j===l) { // the args matched | ||
if(v[i]!==undefined) return v[i]; | ||
} | ||
} | ||
var i,j,l = m||a.length; // use var, slightly smaller and faster for loops, and i needs more scope | ||
for(i=0;i<k.length && v[i]!==undefined;i++) { // an array of arrays of args, each array represents a call signature | ||
for(j=0;j<=l && (k[i][j]===a[j] || (e && e(k[i][j],a[j])));j++) { // compare each arg //if(p[j]!==a[j] && (!e || !e(p[j],a[j]))) break; // go to next call signature if args don't match | ||
if(j===l) return v[i]; // the args matched | ||
} | ||
} | ||
// set chng timeout only when new value computed, hits will not push out the tte, but it is arguable they should not | ||
if(c) c(i,v); | ||
!c||c(i,v); | ||
return v[i] = fn.apply(this,k[i] = a); | ||
@@ -91,3 +83,3 @@ }).bind( | ||
equals, | ||
maxAge && maxAge<I ? d : 0, | ||
maxAge>0 && maxAge<I ? d : 0, | ||
maxArgs | ||
@@ -94,0 +86,0 @@ ); |
@@ -1,1 +0,1 @@ | ||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){(function(){"use strict";const vrgs=f=>{const s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)},nanomemoize=(fn,{serializer:serializer,equals:equals,maxAge:maxAge,maxArgs:maxArgs,vargs:vargs=vrgs(fn)}={})=>{const s=Object.create(null),k=[],v=[],wm={m:new WeakMap},d=(key,c)=>setTimeout(()=>{c instanceof WeakMap?c.delete(key):delete c[key]},maxAge),I=Infinity;let f,u;if(fn.length===1&&!equals&&!vargs){f=function(f,s,wm,c,p,a){const t=typeof a,key=t==="number"||t==="boolean"||!p&&t==="object"?a:t==="string"?JSON.stringify(t):p(a);if(!p&&t==="object"){let r;return wm.m.get(key)||(!c||c(key,wm.m),wm.m.set(key,r=fn.call(this,a)),r)}return s[key]||(!c||c(key,s),s[key]=fn.call(this,a))}.bind(this,fn,s,wm,maxAge&&maxAge<I?d:0,serializer);u=1}else{f=function(f,k,v,e,c,m,...a){const l=m||a.length;let i;for(i=0;i<k.length;i++){let p=k[i];if(p&&p.length===a.length){for(let j=0;j<=l;j++){if(e?!e(p[j],a[j]):p[j]!==a[j])break;if(j===l){if(v[i]!==undefined)return v[i]}}}}if(c)c(i,v);return v[i]=fn.apply(this,k[i]=a)}.bind(this,fn,k,v,equals,maxAge&&maxAge<I?d:0,maxArgs)}f.clear=(_=>{Object.keys(s).forEach(k=>delete s[k]);wm.m=new WeakMap;k.length=0;v.length=0});f.keys=(_=>u?null:k.slice());f.values=(_=>u?null:v.slice());f.keyValues=(_=>u?{primitives:Object.assign({},s),objects:wm.m}:null);return f};if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this)},{}]},{},[1]); | ||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){(function(){var vrgs=f=>{var s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)},nanomemoize=(fn,{serializer:serializer,equals:equals,maxAge:maxAge,maxArgs:maxArgs,vargs:vargs=vrgs(fn)}={})=>{var s=Object.create(null),k=[],v=[],wm={m:new WeakMap},d=(key,c)=>setTimeout(()=>{c instanceof WeakMap?c.delete(key):delete c[key]},maxAge),I=Infinity,f,u;if(fn.length===1&&!equals&&!vargs){f=function(f,s,wm,c,p,a){var t=typeof a,key=t==="number"||t==="boolean"||!p&&t==="object"?a:t==="string"?JSON.stringify(t):p(a);if(!p&&t==="object"){var r;return wm.m.get(key)||(!c||c(key,wm.m),wm.m.set(key,r=fn.call(this,a)),r)}return s[key]||(!c||c(key,s),s[key]=fn.call(this,a))}.bind(this,fn,s,wm,maxAge>0&&maxAge<I?d:0,serializer);u=1}else{f=function(f,k,v,e,c,m,...a){var i,j,l=m||a.length;for(i=0;i<k.length&&v[i]!==undefined;i++){for(j=0;j<=l&&(k[i][j]===a[j]||e&&e(k[i][j],a[j]));j++){if(j===l)return v[i]}}!c||c(i,v);return v[i]=fn.apply(this,k[i]=a)}.bind(this,fn,k,v,equals,maxAge>0&&maxAge<I?d:0,maxArgs)}f.clear=(_=>{Object.keys(s).forEach(k=>delete s[k]);wm.m=new WeakMap;k.length=0;v.length=0});f.keys=(_=>u?null:k.slice());f.values=(_=>u?null:v.slice());f.keyValues=(_=>u?{primitives:Object.assign({},s),objects:wm.m}:null);return f};if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this)},{}]},{},[1]); |
(function() { | ||
"use strict"; | ||
const vrgs = f => { | ||
const s = f+"", | ||
var vrgs = f => { | ||
var s = f+"", | ||
i = s.indexOf("..."); | ||
@@ -11,7 +10,7 @@ return i>=0 && i<s.indexOf(")" || s.indexOf("arguments")>=0); | ||
equals, // equality tester, will force use of slower multiarg approach even for single arg functions | ||
maxAge, // max cache age is ms, set higher than 0 if you want automatic clearing | ||
maxAge, // max cache age is ms, set > 0 && < Infinity if you want automatic clearing | ||
maxArgs, // max args to use for signature | ||
vargs = vrgs(fn) // set to true if function may have variable or beyond-signature arguments, default if best attempt at infering | ||
}={}) => { | ||
const s = Object.create(null), // single arg function key/value cache | ||
var s = Object.create(null), // single arg function key/value cache | ||
k = [], // multiple arg function arg key cache | ||
@@ -23,5 +22,5 @@ v = [], // multiple arg function result cache | ||
},maxAge), | ||
I = Infinity; | ||
let f, // memoized function to return | ||
u; // flag indicating a unary arg function is in use | ||
I = Infinity, | ||
f, // memoized function to return | ||
u; // flag indicating a unary arg function is in use for clear operation | ||
if(fn.length===1 && !equals && !vargs) { | ||
@@ -37,7 +36,7 @@ // for single argument functions, just use a JS object key look-up | ||
// strings must be serialized because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1" | ||
const t = typeof a, | ||
var t = typeof a, | ||
key = t === "number" || t === "boolean" || (!p && t === "object") ? a : t === "string" ? JSON.stringify(t) : p(a); | ||
// set chng timeout only when new value computed, hits will not push out the tte, but it is arguable they should not | ||
if(!p && t==="object") { | ||
let r; | ||
var r; | ||
return wm.m.get(key) || ((!c||c(key,wm.m)),wm.m.set(key,r = fn.call(this, a)),r); | ||
@@ -51,3 +50,3 @@ } | ||
wm, | ||
maxAge && maxAge<I ? d : 0, | ||
maxAge>0 && maxAge<I ? d : 0, | ||
serializer | ||
@@ -67,17 +66,10 @@ ); | ||
f = (function(f,k,v,e,c,m,...a) { | ||
const l = m||a.length; | ||
let i; | ||
for(i=0;i<k.length;i++) { // an array of arrays of args, each array represents a call signature | ||
let p = k[i]; | ||
if(p && p.length===a.length) { | ||
for(let j=0;j<=l;j++) { | ||
if(e ? !e(p[j],a[j]) : p[j]!==a[j]) break; // go to next call signature if args don't match | ||
if(j===l) { // the args matched | ||
if(v[i]!==undefined) return v[i]; | ||
} | ||
} | ||
var i,j,l = m||a.length; // use var, slightly smaller and faster for loops, and i needs more scope | ||
for(i=0;i<k.length && v[i]!==undefined;i++) { // an array of arrays of args, each array represents a call signature | ||
for(j=0;j<=l && (k[i][j]===a[j] || (e && e(k[i][j],a[j])));j++) { // compare each arg //if(p[j]!==a[j] && (!e || !e(p[j],a[j]))) break; // go to next call signature if args don't match | ||
if(j===l) return v[i]; // the args matched | ||
} | ||
} | ||
// set chng timeout only when new value computed, hits will not push out the tte, but it is arguable they should not | ||
if(c) c(i,v); | ||
!c||c(i,v); | ||
return v[i] = fn.apply(this,k[i] = a); | ||
@@ -90,3 +82,3 @@ }).bind( | ||
equals, | ||
maxAge && maxAge<I ? d : 0, | ||
maxAge>0 && maxAge<I ? d : 0, | ||
maxArgs | ||
@@ -93,0 +85,0 @@ ); |
@@ -1,1 +0,1 @@ | ||
(function(){"use strict";const vrgs=f=>{const s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)},nanomemoize=(fn,{serializer:serializer,equals:equals,maxAge:maxAge,maxArgs:maxArgs,vargs:vargs=vrgs(fn)}={})=>{const s=Object.create(null),k=[],v=[],wm={m:new WeakMap},d=(key,c)=>setTimeout(()=>{c instanceof WeakMap?c.delete(key):delete c[key]},maxAge),I=Infinity;let f,u;if(fn.length===1&&!equals&&!vargs){f=function(f,s,wm,c,p,a){const t=typeof a,key=t==="number"||t==="boolean"||!p&&t==="object"?a:t==="string"?JSON.stringify(t):p(a);if(!p&&t==="object"){let r;return wm.m.get(key)||(!c||c(key,wm.m),wm.m.set(key,r=fn.call(this,a)),r)}return s[key]||(!c||c(key,s),s[key]=fn.call(this,a))}.bind(this,fn,s,wm,maxAge&&maxAge<I?d:0,serializer);u=1}else{f=function(f,k,v,e,c,m,...a){const l=m||a.length;let i;for(i=0;i<k.length;i++){let p=k[i];if(p&&p.length===a.length){for(let j=0;j<=l;j++){if(e?!e(p[j],a[j]):p[j]!==a[j])break;if(j===l){if(v[i]!==undefined)return v[i]}}}}if(c)c(i,v);return v[i]=fn.apply(this,k[i]=a)}.bind(this,fn,k,v,equals,maxAge&&maxAge<I?d:0,maxArgs)}f.clear=(_=>{Object.keys(s).forEach(k=>delete s[k]);wm.m=new WeakMap;k.length=0;v.length=0});f.keys=(_=>u?null:k.slice());f.values=(_=>u?null:v.slice());f.keyValues=(_=>u?{primitives:Object.assign({},s),objects:wm.m}:null);return f};if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this); | ||
(function(){var vrgs=f=>{var s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)},nanomemoize=(fn,{serializer:serializer,equals:equals,maxAge:maxAge,maxArgs:maxArgs,vargs:vargs=vrgs(fn)}={})=>{var s=Object.create(null),k=[],v=[],wm={m:new WeakMap},d=(key,c)=>setTimeout(()=>{c instanceof WeakMap?c.delete(key):delete c[key]},maxAge),I=Infinity,f,u;if(fn.length===1&&!equals&&!vargs){f=function(f,s,wm,c,p,a){var t=typeof a,key=t==="number"||t==="boolean"||!p&&t==="object"?a:t==="string"?JSON.stringify(t):p(a);if(!p&&t==="object"){var r;return wm.m.get(key)||(!c||c(key,wm.m),wm.m.set(key,r=fn.call(this,a)),r)}return s[key]||(!c||c(key,s),s[key]=fn.call(this,a))}.bind(this,fn,s,wm,maxAge>0&&maxAge<I?d:0,serializer);u=1}else{f=function(f,k,v,e,c,m,...a){var i,j,l=m||a.length;for(i=0;i<k.length&&v[i]!==undefined;i++){for(j=0;j<=l&&(k[i][j]===a[j]||e&&e(k[i][j],a[j]));j++){if(j===l)return v[i]}}!c||c(i,v);return v[i]=fn.apply(this,k[i]=a)}.bind(this,fn,k,v,equals,maxAge>0&&maxAge<I?d:0,maxArgs)}f.clear=(_=>{Object.keys(s).forEach(k=>delete s[k]);wm.m=new WeakMap;k.length=0;v.length=0});f.keys=(_=>u?null:k.slice());f.values=(_=>u?null:v.slice());f.keyValues=(_=>u?{primitives:Object.assign({},s),objects:wm.m}:null);return f};if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this); |
40
index.js
(function() { | ||
"use strict"; | ||
const vrgs = f => { | ||
const s = f+"", | ||
var vrgs = f => { | ||
var s = f+"", | ||
i = s.indexOf("..."); | ||
@@ -11,7 +10,7 @@ return i>=0 && i<s.indexOf(")" || s.indexOf("arguments")>=0); | ||
equals, // equality tester, will force use of slower multiarg approach even for single arg functions | ||
maxAge, // max cache age is ms, set higher than 0 if you want automatic clearing | ||
maxAge, // max cache age is ms, set > 0 && < Infinity if you want automatic clearing | ||
maxArgs, // max args to use for signature | ||
vargs = vrgs(fn) // set to true if function may have variable or beyond-signature arguments, default if best attempt at infering | ||
}={}) => { | ||
const s = Object.create(null), // single arg function key/value cache | ||
var s = Object.create(null), // single arg function key/value cache | ||
k = [], // multiple arg function arg key cache | ||
@@ -23,5 +22,5 @@ v = [], // multiple arg function result cache | ||
},maxAge), | ||
I = Infinity; | ||
let f, // memoized function to return | ||
u; // flag indicating a unary arg function is in use | ||
I = Infinity, | ||
f, // memoized function to return | ||
u; // flag indicating a unary arg function is in use for clear operation | ||
if(fn.length===1 && !equals && !vargs) { | ||
@@ -37,7 +36,7 @@ // for single argument functions, just use a JS object key look-up | ||
// strings must be serialized because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1" | ||
const t = typeof a, | ||
var t = typeof a, | ||
key = t === "number" || t === "boolean" || (!p && t === "object") ? a : t === "string" ? JSON.stringify(t) : p(a); | ||
// set chng timeout only when new value computed, hits will not push out the tte, but it is arguable they should not | ||
if(!p && t==="object") { | ||
let r; | ||
var r; | ||
return wm.m.get(key) || ((!c||c(key,wm.m)),wm.m.set(key,r = fn.call(this, a)),r); | ||
@@ -51,3 +50,3 @@ } | ||
wm, | ||
maxAge && maxAge<I ? d : 0, | ||
maxAge>0 && maxAge<I ? d : 0, | ||
serializer | ||
@@ -67,17 +66,10 @@ ); | ||
f = (function(f,k,v,e,c,m,...a) { | ||
const l = m||a.length; | ||
let i; | ||
for(i=0;i<k.length;i++) { // an array of arrays of args, each array represents a call signature | ||
let p = k[i]; | ||
if(p && p.length===a.length) { | ||
for(let j=0;j<=l;j++) { | ||
if(e ? !e(p[j],a[j]) : p[j]!==a[j]) break; // go to next call signature if args don't match | ||
if(j===l) { // the args matched | ||
if(v[i]!==undefined) return v[i]; | ||
} | ||
} | ||
var i,j,l = m||a.length; // use var, slightly smaller and faster for loops, and i needs more scope | ||
for(i=0;i<k.length && v[i]!==undefined;i++) { // an array of arrays of args, each array represents a call signature | ||
for(j=0;j<=l && (k[i][j]===a[j] || (e && e(k[i][j],a[j])));j++) { // compare each arg //if(p[j]!==a[j] && (!e || !e(p[j],a[j]))) break; // go to next call signature if args don't match | ||
if(j===l) return v[i]; // the args matched | ||
} | ||
} | ||
// set chng timeout only when new value computed, hits will not push out the tte, but it is arguable they should not | ||
if(c) c(i,v); | ||
!c||c(i,v); | ||
return v[i] = fn.apply(this,k[i] = a); | ||
@@ -90,3 +82,3 @@ }).bind( | ||
equals, | ||
maxAge && maxAge<I ? d : 0, | ||
maxAge>0 && maxAge<I ? d : 0, | ||
maxArgs | ||
@@ -93,0 +85,0 @@ ); |
{ | ||
"name": "nano-memoize", | ||
"version": "v1.1.1", | ||
"version": "v1.1.2", | ||
"description": "Faster than fast, smaller than micro ... a nano speed and nano size memoizer.", | ||
@@ -8,3 +8,3 @@ "engines": {}, | ||
"scripts": { | ||
"prepare": "cp ./src/nano-memoize.js ./index.js && cp ./src/nano-memoize.js dist/nano-memoize.js && browserify ./src/nano-memoize.js -o browser/nano-memoize.js && uglifyjs browser/nano-memoize.js -o browser/nano-memoize.min.js && uglifyjs src/nano-memoize.js -o dist/nano-memoize.min.js" | ||
"prepare": "cp ./src/nano-memoize.js ./index.js && cp ./src/nano-memoize.js dist/nano-memoize.js && npx bread-compressor dist && browserify ./src/nano-memoize.js -o browser/nano-memoize.js && uglifyjs browser/nano-memoize.js -o browser/nano-memoize.min.js && uglifyjs src/nano-memoize.js -o dist/nano-memoize.min.js" | ||
}, | ||
@@ -30,2 +30,3 @@ "repository": { | ||
"blanket": "^1.2.3", | ||
"bread-compressor-cli": "^1.0.6", | ||
"chai": "^3.4.1", | ||
@@ -32,0 +33,0 @@ "cli-table2": "^0.2.0", |
205
README.md
@@ -8,133 +8,132 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/30ce201484754fa5b0a6c6046abb842d)](https://www.codacy.com/app/syblackwell/nano-memoize?utm_source=github.com&utm_medium=referral&utm_content=anywhichway/nano-memoize&utm_campaign=Badge_Grade) | ||
During development we also discovered that despite its popularity and goal to be the fastest possible memoizer, `fast-memoize` is actually one of the slowest out-of-the-box when it comes to multiple argument functions. It uses `JSON.stringify` as key generator. It also only memoizes out to 3 arguments. This is not to say it should not be used, it also seems to have the cleanest software architecture and it may be theoretically possible to write a high-speed multi-argument plugin. And, MANY people are very happy with it. | ||
During development we also discovered that despite its popularity and goal to be the fastest possible memoizer, `fast-memoize` is actually one of the slowest out-of-the-box when it comes to multiple argument functions because it uses `JSON.stringify` to generate a single key generator for all arguments. It also only memoizes out to 3 arguments, which may cause issues. This is not to say it should not be used, it also seems to have the cleanest software architecture and it may be theoretically possible to write a high-speed multi-argument plugin. And, MANY people are very happy with it. | ||
Special appreciation to @titoBouzout and @popbee who spent a good bit of time reviewing code for optimization and making recommendations. See [Issue 4](https://github.com/anywhichway/nano-memoize/issues/4) for the conversation. | ||
The minified/brotli size is 640 Brotli bytes for `nano-memoize` v1.1.0 vs 2020 bytes for `micro-memoize` v3.0.1. And, `nano-memoize` has slightly more functionality. | ||
The minified/brotli size is 665 bytes for `nano-memoize` v1.1.0 vs 1,356 bytes for `micro-memoize` v3.0.1. And, `nano-memoize` has slightly more functionality. | ||
The speed tests are below. In most cases `nano-memoize` is the fastest. | ||
The speed tests are below. | ||
* For single primitive argument functions it is comparable to, but slightly and probably un-importantly faster that `fast-memoize`. | ||
* For single primitive argument functions `nano-memoize` and `fast-memoize` will trade-off first position across multiple test runs with `nano-memoize` winning slightly more frequently. They are 4x faster than `micro-memoize`. | ||
* For single primitive argument functions it is comparable to, but slightly and probably un-importantly faster that `fast-memoize`. | ||
* For single object argument functions it is, 10-15% faster than `fast-memoize` and 20-25% faster than `micro-memoize`. | ||
* For multiple primitive argument functions`nano-memoize` is slightly and probably un-importantly faster than `micro-memoize`. | ||
* For multiple primitive argument functions `nano-memoize` and `micro-memoize` will trade-off first position across multiple test runs with `nano-memoize` winning slightly more frequently. They are 60x faster than `fast-memoize`. | ||
* For multiple object argument functions `nano-memoize` is slightly and probably un-importantly faster than `micro-memoize`. | ||
* For multiple object argument functions `nano-memoize` and `micro-memoize` will trade-off first position across multiple test runs with `nano-memoize` winning slightly more frequently. They are 60x faster than `fast-memoize`. | ||
* When `deepEquals` tests are used, `micro-memoize` rules the day. | ||
We have found that benchmarks can vary dramatically from O/S to O/S or Node version to Node version. These tests were run on a Windows 10 64bit 2.4ghz machine with 8GB RAM and Node v11.6.0. Also, even with multiple samplings, garbage collection can have a substative impact and multiple runs in different orders are really required for apples-to-apples comparisons. | ||
We have found that benchmarks can vary dramatically from O/S to O/S or Node version to Node version. These tests were run on a Windows 10 Pro 64bit 1.8ghz i7 machine with 16GB RAM and Node v11.6.0. Also, even with multiple samplings, garbage collection can have a substative impact and multiple runs in different orders are really required for apples-to-apples comparisons. | ||
Functions with a single primitive parameter... | ||
``` | ||
+----------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+----------------------------------------------------------------------+ | ||
� nano-memoize � 408,626,233 � � 1.86% � 81 � | ||
+----------------------------------------------------------------------+ | ||
� fast-memoize � 368,639,842 � � 1.79% � 81 � | ||
+----------------------------------------------------------------------+ | ||
� micro-memoize � 102,964,021 � � 1.39% � 84 � | ||
+----------------------------------------------------------------------+ | ||
� moize � 93,623,511 � � 1.70% � 83 � | ||
+----------------------------------------------------------------------+ | ||
� iMemoized � 83,667,946 � � 1.58% � 83 � | ||
+----------------------------------------------------------------------+ | ||
� lru-memoize � 71,258,447 � � 1.90% � 82 � | ||
+----------------------------------------------------------------------+ | ||
� lodash � 46,706,263 � � 1.87% � 82 � | ||
+----------------------------------------------------------------------+ | ||
� memoizee � 36,960,053 � � 1.67% � 81 � | ||
+----------------------------------------------------------------------+ | ||
� underscore � 34,650,172 � � 1.67% � 79 � | ||
+----------------------------------------------------------------------+ | ||
� memoizerific � 6,854,333 � � 2.14% � 79 � | ||
+----------------------------------------------------------------------+ | ||
� addy-osmani � 6,076,478 � � 1.80% � 80 � | ||
+----------------------------------------------------------------------+ | ||
``` | ||
Functions with a single object parameter... | ||
``` | ||
+----------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+----------------------------------------------------------------------+ | ||
� nano-memoize � 120,054,209 � � 1.77% � 86 � | ||
+----------------------------------------------------------------------+ | ||
� micro-memoize � 88,968,257 � � 1.13% � 84 � | ||
+----------------------------------------------------------------------+ | ||
� moize � 85,218,895 � � 1.42% � 84 � | ||
+----------------------------------------------------------------------+ | ||
� fast-memoize � 73,730,097 � � 5.40% � 71 � | ||
+----------------------------------------------------------------------+ | ||
� iMemoized � 58,513,510 � � 1.26% � 80 � | ||
+----------------------------------------------------------------------+ | ||
� lodash � 46,264,060 � � 1.88% � 80 � | ||
+----------------------------------------------------------------------+ | ||
� lru-memoize � 30,648,600 � � 1.61% � 81 � | ||
+----------------------------------------------------------------------+ | ||
� underscore � 28,901,663 � � 2.98% � 75 � | ||
+----------------------------------------------------------------------+ | ||
� memoizee � 17,213,563 � � 1.68% � 80 � | ||
+----------------------------------------------------------------------+ | ||
� addy-osmani � 6,379,759 � � 1.75% � 81 � | ||
+----------------------------------------------------------------------+ | ||
� memoizerific � 5,789,710 � � 3.67% � 74 � | ||
+----------------------------------------------------------------------+ | ||
``` | ||
Functions with multiple parameters that contain only primitives... | ||
``` | ||
+---------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+---------------------------------------------------------------------+ | ||
� nano-memoize � 59,229,772 � � 0.72% � 75 � | ||
� nano-memoize � 64,477,579 � � 1.77% � 83 � | ||
+---------------------------------------------------------------------+ | ||
� fast-memoize � 53,557,422 � � 4.65% � 61 � | ||
� moize � 56,501,764 � � 2.20% � 79 � | ||
+---------------------------------------------------------------------+ | ||
� micro-memoize � 11,102,228 � � 9.04% � 56 � | ||
� micro-memoize � 39,469,612 � � 6.47% � 72 � | ||
+---------------------------------------------------------------------+ | ||
� iMemoized � 10,207,666 � � 11.93% � 40 � | ||
� lru-memoize � 19,361,408 � � 6.19% � 70 � | ||
+---------------------------------------------------------------------+ | ||
� moize � 7,753,586 � � 41.02% � 57 � | ||
� memoizee � 11,381,474 � � 4.35% � 73 � | ||
+---------------------------------------------------------------------+ | ||
� lodash � 6,364,484 � � 11.60% � 43 � | ||
� iMemoized � 5,733,044 � � 9.82% � 71 � | ||
+---------------------------------------------------------------------+ | ||
� lru-memoize � 4,383,453 � � 39.83% � 59 � | ||
� addy-osmani � 3,258,073 � � 2.34% � 86 � | ||
+---------------------------------------------------------------------+ | ||
� underscore � 4,159,229 � � 13.33% � 64 � | ||
� memoizerific � 1,965,125 � � 7.60% � 64 � | ||
+---------------------------------------------------------------------+ | ||
� memoizee � 4,067,506 � � 19.14% � 40 � | ||
� fast-memoize � 834,173 � � 7.38% � 65 � | ||
+---------------------------------------------------------------------+ | ||
� memoizerific � 1,145,407 � � 4.27% � 65 � | ||
+---------------------------------------------------------------------+ | ||
� addy-osmani � 639,076 � � 22.97% � 57 � | ||
+---------------------------------------------------------------------+ | ||
``` | ||
Functions with a single object parameter... | ||
Functions with multiple parameters that contain objects... | ||
``` | ||
+--------------------------------------------------------------------+ | ||
+---------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+--------------------------------------------------------------------+ | ||
� nano-memoize � 20,377,511 � � 2.18% � 71 � | ||
+--------------------------------------------------------------------+ | ||
� fast-memoize � 15,132,122 � � 6.55% � 60 � | ||
+--------------------------------------------------------------------+ | ||
� micro-memoize � 15,128,905 � � 4.30% � 62 � | ||
+--------------------------------------------------------------------+ | ||
� moize � 11,712,302 � � 4.93% � 61 � | ||
+--------------------------------------------------------------------+ | ||
� iMemoized � 10,145,254 � � 3.17% � 62 � | ||
+--------------------------------------------------------------------+ | ||
� lodash � 7,161,180 � � 3.72% � 59 � | ||
+--------------------------------------------------------------------+ | ||
� underscore � 5,789,882 � � 2.62% � 70 � | ||
+--------------------------------------------------------------------+ | ||
� lru-memoize � 3,881,960 � � 4.34% � 61 � | ||
+--------------------------------------------------------------------+ | ||
� memoizee � 2,566,037 � � 1.66% � 67 � | ||
+--------------------------------------------------------------------+ | ||
� memoizerific � 1,111,770 � � 1.80% � 78 � | ||
+--------------------------------------------------------------------+ | ||
� addy-osmani � 1,001,119 � � 4.98% � 61 � | ||
+--------------------------------------------------------------------+ | ||
+---------------------------------------------------------------------+ | ||
� nano-memoize � 63,382,702 � � 1.88% � 83 � | ||
+---------------------------------------------------------------------+ | ||
� moize � 61,349,765 � � 1.78% � 82 � | ||
+---------------------------------------------------------------------+ | ||
� micro-memoize � 54,322,737 � � 4.53% � 72 � | ||
+---------------------------------------------------------------------+ | ||
� lru-memoize � 23,824,559 � � 2.34% � 81 � | ||
+---------------------------------------------------------------------+ | ||
� memoizee � 11,161,431 � � 1.97% � 84 � | ||
+---------------------------------------------------------------------+ | ||
� memoizerific � 5,416,184 � � 3.89% � 79 � | ||
+---------------------------------------------------------------------+ | ||
� addy-osmani � 1,199,529 � � 2.78% � 84 � | ||
+---------------------------------------------------------------------+ | ||
� fast-memoize � 1,057,876 � � 1.75% � 83 � | ||
+---------------------------------------------------------------------+ | ||
``` | ||
Functions with multiple parameters that contain only primitives... | ||
``` | ||
+--------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+--------------------------------------------------------------------+ | ||
� nano-memoize � 7,109,499 � � 3.64% � 67 � | ||
+--------------------------------------------------------------------+ | ||
� micro-memoize � 7,052,676 � � 5.64% � 61 � | ||
+--------------------------------------------------------------------+ | ||
� moize � 6,470,805 � � 2.41% � 72 � | ||
+--------------------------------------------------------------------+ | ||
� lru-memoize � 3,128,711 � � 3.89% � 69 � | ||
+--------------------------------------------------------------------+ | ||
� memoizee � 1,770,348 � � 16.76% � 57 � | ||
+--------------------------------------------------------------------+ | ||
� iMemoized � 1,004,645 � � 19.13% � 58 � | ||
+--------------------------------------------------------------------+ | ||
� memoizerific � 690,537 � � 11.49% � 57 � | ||
+--------------------------------------------------------------------+ | ||
� addy-osmani � 421,059 � � 5.01% � 68 � | ||
+--------------------------------------------------------------------+ | ||
� fast-memoize � 253,637 � � 2.84% � 66 � | ||
+--------------------------------------------------------------------+ | ||
``` | ||
Functions with multiple parameters that contain objects... | ||
``` | ||
+--------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+--------------------------------------------------------------------+ | ||
� nano-memoize � 7,115,350 � � 3.16% � 66 � | ||
+--------------------------------------------------------------------+ | ||
� micro-memoize � 6,868,295 � � 3.63% � 66 � | ||
+--------------------------------------------------------------------+ | ||
� moize � 4,196,397 � � 24.14% � 61 � | ||
+--------------------------------------------------------------------+ | ||
� lru-memoize � 3,284,142 � � 2.82% � 68 � | ||
+--------------------------------------------------------------------+ | ||
� memoizee � 1,333,993 � � 3.18% � 70 � | ||
+--------------------------------------------------------------------+ | ||
� memoizerific � 807,252 � � 6.46% � 72 � | ||
+--------------------------------------------------------------------+ | ||
� addy-osmani � 218,191 � � 3.84% � 73 � | ||
+--------------------------------------------------------------------+ | ||
� fast-memoize � 175,937 � � 8.04% � 59 � | ||
+--------------------------------------------------------------------+ | ||
``` | ||
Deep equals ... | ||
@@ -158,9 +157,3 @@ | ||
We were puzzled about the multiple argument performance on `fast-memoize` given its stated goal of being the "fastest possible". We discovered that the default caching and serialization approach used by fast-memoize only performs well for single argument functions for two reasons: | ||
1) It uses `JSON.stringify` to create a key for an entire argument list. This can be slow. | ||
2) Because a single key is generated for all arguments when perhaps only the first argument differs in a call, a lot of extra work is done. The `moize` and `micro-memoize` approach adopted by `nano-memoize` is far faster for multiple arguments. | ||
# Usage | ||
@@ -195,5 +188,5 @@ | ||
maxAge: number, | ||
// the serializer/key generator to use for single argument functions (multi-argument functionsuse equals) | ||
// the serializer/key generator to use for single argument functions (optional, not recommended) | ||
serializer: function, | ||
// the equals function to use for multi-argument functions, e.g. deepEquals for objects (single-argument functions serializer) | ||
// the equals function to use for multi-argument functions (optional, try to avoid) e.g. deepEquals for objects | ||
equals: function, | ||
@@ -209,2 +202,4 @@ // forces the use of multi-argument paradigm, auto set if function has a spread argument or uses `arguments` in its body. | ||
2019-04-02 v1.1.2 Speed improvements for multiple arguments. Now consistently faster than `fast-memoize` and `nano-memoize` across multiple test runs. Benchmarks run in a new test environment. The benchmarks for v1.1.1 although correct from a relative perspective, grossly understated actual performance due to a corrupt testing environment. | ||
2019-03-25 v1.1.1 Pushed incorrect version with v1.1.0. This corrects the version push. | ||
@@ -211,0 +206,0 @@ |
(function() { | ||
"use strict"; | ||
const vrgs = f => { | ||
const s = f+"", | ||
var vrgs = f => { | ||
var s = f+"", | ||
i = s.indexOf("..."); | ||
@@ -11,7 +10,7 @@ return i>=0 && i<s.indexOf(")" || s.indexOf("arguments")>=0); | ||
equals, // equality tester, will force use of slower multiarg approach even for single arg functions | ||
maxAge, // max cache age is ms, set higher than 0 if you want automatic clearing | ||
maxAge, // max cache age is ms, set > 0 && < Infinity if you want automatic clearing | ||
maxArgs, // max args to use for signature | ||
vargs = vrgs(fn) // set to true if function may have variable or beyond-signature arguments, default if best attempt at infering | ||
}={}) => { | ||
const s = Object.create(null), // single arg function key/value cache | ||
var s = Object.create(null), // single arg function key/value cache | ||
k = [], // multiple arg function arg key cache | ||
@@ -23,5 +22,5 @@ v = [], // multiple arg function result cache | ||
},maxAge), | ||
I = Infinity; | ||
let f, // memoized function to return | ||
u; // flag indicating a unary arg function is in use | ||
I = Infinity, | ||
f, // memoized function to return | ||
u; // flag indicating a unary arg function is in use for clear operation | ||
if(fn.length===1 && !equals && !vargs) { | ||
@@ -37,7 +36,7 @@ // for single argument functions, just use a JS object key look-up | ||
// strings must be serialized because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1" | ||
const t = typeof a, | ||
var t = typeof a, | ||
key = t === "number" || t === "boolean" || (!p && t === "object") ? a : t === "string" ? JSON.stringify(t) : p(a); | ||
// set chng timeout only when new value computed, hits will not push out the tte, but it is arguable they should not | ||
if(!p && t==="object") { | ||
let r; | ||
var r; | ||
return wm.m.get(key) || ((!c||c(key,wm.m)),wm.m.set(key,r = fn.call(this, a)),r); | ||
@@ -51,3 +50,3 @@ } | ||
wm, | ||
maxAge && maxAge<I ? d : 0, | ||
maxAge>0 && maxAge<I ? d : 0, | ||
serializer | ||
@@ -67,17 +66,10 @@ ); | ||
f = (function(f,k,v,e,c,m,...a) { | ||
const l = m||a.length; | ||
let i; | ||
for(i=0;i<k.length;i++) { // an array of arrays of args, each array represents a call signature | ||
let p = k[i]; | ||
if(p && p.length===a.length) { | ||
for(let j=0;j<=l;j++) { | ||
if(e ? !e(p[j],a[j]) : p[j]!==a[j]) break; // go to next call signature if args don't match | ||
if(j===l) { // the args matched | ||
if(v[i]!==undefined) return v[i]; | ||
} | ||
} | ||
var i,j,l = m||a.length; // use var, slightly smaller and faster for loops, and i needs more scope | ||
for(i=0;i<k.length && v[i]!==undefined;i++) { // an array of arrays of args, each array represents a call signature | ||
for(j=0;j<=l && (k[i][j]===a[j] || (e && e(k[i][j],a[j])));j++) { // compare each arg //if(p[j]!==a[j] && (!e || !e(p[j],a[j]))) break; // go to next call signature if args don't match | ||
if(j===l) return v[i]; // the args matched | ||
} | ||
} | ||
// set chng timeout only when new value computed, hits will not push out the tte, but it is arguable they should not | ||
if(c) c(i,v); | ||
!c||c(i,v); | ||
return v[i] = fn.apply(this,k[i] = a); | ||
@@ -90,3 +82,3 @@ }).bind( | ||
equals, | ||
maxAge && maxAge<I ? d : 0, | ||
maxAge>0 && maxAge<I ? d : 0, | ||
maxArgs | ||
@@ -93,0 +85,0 @@ ); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 4 instances in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
70118
21
23
913
242
5