Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
memoizerific
Advanced tools
Fast, small, most-efficient JavaScript memoization lib to memoize JS functions
memoizerific is a JavaScript library that provides a simple and efficient way to memoize functions. Memoization is a technique used to speed up function execution by caching the results of expensive function calls and returning the cached result when the same inputs occur again.
Basic Memoization
This feature allows you to memoize a function with a specified cache size. In this example, the function adds two numbers and caches the result for up to 100 different input combinations.
const memoizerific = require('memoizerific');
const memoizedFunction = memoizerific(100)((a, b) => a + b);
console.log(memoizedFunction(1, 2)); // 3
console.log(memoizedFunction(1, 2)); // 3 (cached result)
Cache Size Limitation
This feature demonstrates how the cache size limitation works. When the cache exceeds the specified size, the oldest cached result is evicted.
const memoizerific = require('memoizerific');
const memoizedFunction = memoizerific(2)((a, b) => a + b);
memoizedFunction(1, 2); // Cache: {(1, 2): 3}
memoizedFunction(2, 3); // Cache: {(1, 2): 3, (2, 3): 5}
memoizedFunction(3, 4); // Cache: {(2, 3): 5, (3, 4): 7} (1, 2) is evicted
Function Argument Handling
This feature shows how memoizerific can handle functions with variable numbers of arguments. The function sums all its arguments and caches the result.
const memoizerific = require('memoizerific');
const memoizedFunction = memoizerific(100)((...args) => args.reduce((sum, val) => sum + val, 0));
console.log(memoizedFunction(1, 2, 3)); // 6
console.log(memoizedFunction(1, 2, 3)); // 6 (cached result)
lodash.memoize is a function from the Lodash library that provides similar memoization capabilities. It allows you to memoize functions and customize the cache key resolver. Compared to memoizerific, lodash.memoize is part of a larger utility library and may offer more flexibility in terms of cache key customization.
fast-memoize is a lightweight and fast memoization library. It focuses on performance and simplicity, making it a good alternative to memoizerific for scenarios where speed is critical. However, it may not offer as many features as memoizerific, such as cache size limitation.
memoizee is a comprehensive memoization library that offers a wide range of features, including cache size limitation, cache expiration, and support for various cache storage mechanisms. It is more feature-rich compared to memoizerific but may be more complex to use.
Fast (see benchmarks), small (1k min/gzip), efficient, JavaScript memoization lib to memoize JS functions. Fully supports multiple complex object arguments. Implements LRU caching (least recently used caching) to maintain only the most recent results.
Made for the browser and nodejs. Uses JavaScript Map() for instant object lookups, or a performant polyfill if Map is not available - does not do serialization or string manipulation.
Memoization is the process of caching function results, so that they can be returned cheaply without re-running the function when it is called again with the same arguments. This is especially useful with the rise of redux-philosophy, and the push to calculate derived data on the fly to maintain minimal state.
npm install memoizerific --save
Or use one of the compiled distributions compatible in any environment (umd):
var memoizerific = require('memoizerific');
var myExpensiveFunctionMemoized = memoizerific(50)(function(arg1, arg2, arg3) {
// so many long expensive calls in here
});
myExpensiveFunctionMemoized(1, 2, 3); // that took looooong to process
myExpensiveFunctionMemoized(1, 2, 3); // wow, that one was instant!
myExpensiveFunctionMemoized(2, 3, 4); // expensive again :(
myExpensiveFunctionMemoized(2, 3, 4); // woah, this one was dirt cheap, I'll take 2!
Or with complex arguments:
var complexArg1 = { a: { b: { c: 99 }}},
complexArg2 = [{ z: 1}, { q: [{ x: 3 }]}],
complexArg3 = new Map([['d', 55],['e', 66]]),
complexArg4 = new Set();
myExpensiveFunctionMemoized(complexArg1, complexArg2, complexArg3, complexArg4); // slow
myExpensiveFunctionMemoized(complexArg1, complexArg2, complexArg3, complexArg4); // instant!
There is one option available:
limit:
the max number of results to cache.
memoizerific(limit)(fn);
memoizerific(1)(function(){}); // memoize 1 result
memoizerific(10000)(function(){}); // memoize 10,000 results
memoizerific(0)(function(){}); // memoize infinity results (not recommended)
The cache works using LRU logic, purging the least recently used results when the limit is reached. For example:
// memoize 1 result
var myMemoized = memoizerific(1)(function(arg1, arg2, arg3, arg4) {});
myMemoized(1, 2, 3, 'a'); // function runs, result is cached
myMemoized(1, 2, 3, 'a'); // cached result is returned
myMemoized(1, 2, 3, 'X'); // function runs again, new result is cached, old cached result is purged
myMemoized(1, 2, 3, 'X'); // new cached result is returned
myMemoized(1, 2, 3, 'a'); // function runs again...
The internals of the memoized function are available for introspection. They should not be manipulated directly, but can be useful to read. The following properties are available:
memoizedFn.limit : The cache limit that was passed in. This will never change.
memoizedFn.wasMemoized : Returns true if the last invocation was a cache hit, otherwise false.
memoizedFn.cache : The cache object that stores all the memoized results.
memoizedFn.lru : The lru object that stores the most recent arguments called.
There are many memoization libs available for JavaScript. Some of them have specialized use-cases, such as memoizing file-system access, or server async requests. While others, such as this one, tackle the more general case of memoizing standard synchronous functions. Following are the minimum criteria I look for in a production-worthy memoization solution:
Using this list, we can narrow down the field of possible candidates quite a bit. The popular lodash memoize, for example, only supports one argument out of the box and has no cache control. Others support multiple complex arguments, but do not offer mechanisms to manage the cache-size:
:heavy_multiplication_x: Memoizejs (@addyosmani)
:heavy_multiplication_x: Memoize-strict (@jshanson7)
:heavy_multiplication_x: Deep-memoize (@rjmk)
:heavy_multiplication_x: Mem (@sindresorhus)
Three libs with reasonable traction seem to meet the basic criteria:
After some quick testing, however, we found the library by @neilk to be producing incorrect results, leaving only two viable candidates.
Time to test performance.
This library is intended for real-world use-cases, and is therefore benchmarked using large, complex, real-world data. There are enough fibonacci solvers out there. Example arguments look like this:
myMemoized(
{ a: 1, b: [{ c: 2, d: { e: 3 }}] }, // 1st argument
[{ x: 'x', q: 'q', }, { b: 8, c: 9 }, { b: 2, c: [{x: 5, y: 3}, {x: 2, y: 7}] }, { b: 8, c: 9 }, { b: 8, c: 9 }], // 2nd argument
{ z: 'z' }, // 3rd argument
... // 4th, 5th... argument
);
We generated sets of thousands of random argument combinations of varying variance (to increase and decrease cache hits and misses) and fed them to each library.
Following is data from 5000 iterations of each test on firefox 44:
Cache Size | Num Args | Approx. Cache Hits (variance) | LRU-Memoize | Memoizee | Memoizerific | % Faster |
---|---|---|---|---|---|---|
10 | 2 | 99% | 19ms | 31ms | 10ms | 90% |
10 | 2 | 62% | 212ms | 319ms | 172ms | 23% |
10 | 2 | 7% | 579ms | 617ms | 518ms | 12% |
100 | 2 | 99% | 137ms | 37ms | 20ms | 85% |
100 | 2 | 69% | 696ms | 245ms | 161ms | 52% |
100 | 2 | 10% | 1,057ms | 649ms | 527ms | 23% |
500 | 4 | 95% | 476ms | 67ms | 62ms | 8% |
500 | 4 | 36% | 2,642ms | 703ms | 594ms | 18% |
500 | 4 | 11% | 3,619ms | 880ms | 725ms | 21% |
1000 | 8 | 95% | 1,009ms | 52ms | 65ms | 25% |
1000 | 8 | 14% | 10,477ms | 659ms | 635ms | 4% |
1000 | 8 | 1% | 6,943ms | 1,501ms | 1,466ms | 2% |
Cache Size : The maximum number of results to cache.
Num Args : The number of arguments the memoized function accepts, ex. fn(arg1, arg2, arg3) is 3.
Approx. Cache Hits (variance) : How varied the passed in arguments are. If the exact same arguments are always used, the cache would be hit 100% of the time. If the same arguments are never used, the cache would be hit 0% of the time.
% Faster : How much faster the 1st best performer was from the 2nd best performer (not against the worst performer).
The results from the tests are interesting. While LRU-Memoize performed quite well with few arguments and lots of cache hits, it quickly started to fall apart as the environment became more challenging. At 4+ arguments, it was 5x-10x-20x slower than the other contenders, and began to hit severe performance issues that could potentially cause real-world problems. I would not recommend it for heavy production use.
Memoizee came in a solid second place, around 31% less performant than Memoizerific. In most scenarios this will not be very noticeable. In other, especially demanding ones, such as memoizing in a loop, or through a long recursion chain, it might be. Importantly though, it degraded very gracefully, and remained within sub 1s levels almost all the time. Memoizee is a sturdy, well-built library that I would recommend for production use.
Memoizerific was the performance winner. It is built with complex real-world use in mind. I would, of course, recommend it for serious production use.
Released under an MIT license.
FAQs
Fast, small, efficient JavaScript memoization lib to memoize JS functions
The npm package memoizerific receives a total of 5,432,698 weekly downloads. As such, memoizerific popularity was classified as popular.
We found that memoizerific demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.