You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					146 lines
				
				3.5 KiB
			
		
		
			
		
	
	
					146 lines
				
				3.5 KiB
			| 
											3 years ago
										 | # reusify
 | ||
|  | 
 | ||
|  | [![npm version][npm-badge]][npm-url] | ||
|  | [![Build Status][travis-badge]][travis-url] | ||
|  | [![Coverage Status][coveralls-badge]][coveralls-url] | ||
|  | 
 | ||
|  | Reuse your objects and functions for maximum speed. This technique will | ||
|  | make any function run ~10% faster. You call your functions a | ||
|  | lot, and it adds up quickly in hot code paths. | ||
|  | 
 | ||
|  | ``` | ||
|  | $ node benchmarks/createNoCodeFunction.js | ||
|  | Total time 53133 | ||
|  | Total iterations 100000000 | ||
|  | Iteration/s 1882069.5236482036 | ||
|  | 
 | ||
|  | $ node benchmarks/reuseNoCodeFunction.js | ||
|  | Total time 50617 | ||
|  | Total iterations 100000000 | ||
|  | Iteration/s 1975620.838848608 | ||
|  | ``` | ||
|  | 
 | ||
|  | The above benchmark uses fibonacci to simulate a real high-cpu load. | ||
|  | The actual numbers might differ for your use case, but the difference | ||
|  | should not. | ||
|  | 
 | ||
|  | The benchmark was taken using Node v6.10.0. | ||
|  | 
 | ||
|  | This library was extracted from | ||
|  | [fastparallel](http://npm.im/fastparallel). | ||
|  | 
 | ||
|  | ## Example
 | ||
|  | 
 | ||
|  | ```js | ||
|  | var reusify = require('reusify') | ||
|  | var fib = require('reusify/benchmarks/fib') | ||
|  | var instance = reusify(MyObject) | ||
|  | 
 | ||
|  | // get an object from the cache, | ||
|  | // or creates a new one when cache is empty | ||
|  | var obj = instance.get() | ||
|  | 
 | ||
|  | // set the state | ||
|  | obj.num = 100 | ||
|  | obj.func() | ||
|  | 
 | ||
|  | // reset the state. | ||
|  | // if the state contains any external object | ||
|  | // do not use delete operator (it is slow) | ||
|  | // prefer set them to null | ||
|  | obj.num = 0 | ||
|  | 
 | ||
|  | // store an object in the cache | ||
|  | instance.release(obj) | ||
|  | 
 | ||
|  | function MyObject () { | ||
|  |   // you need to define this property | ||
|  |   // so V8 can compile MyObject into an | ||
|  |   // hidden class | ||
|  |   this.next = null | ||
|  |   this.num = 0 | ||
|  | 
 | ||
|  |   var that = this | ||
|  | 
 | ||
|  |   // this function is never reallocated, | ||
|  |   // so it can be optimized by V8 | ||
|  |   this.func = function () { | ||
|  |     if (null) { | ||
|  |       // do nothing | ||
|  |     } else { | ||
|  |       // calculates fibonacci | ||
|  |       fib(that.num) | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | The above example was intended for synchronous code, let's see async: | ||
|  | ```js | ||
|  | var reusify = require('reusify') | ||
|  | var instance = reusify(MyObject) | ||
|  | 
 | ||
|  | for (var i = 0; i < 100; i++) { | ||
|  |   getData(i, console.log) | ||
|  | } | ||
|  | 
 | ||
|  | function getData (value, cb) { | ||
|  |   var obj = instance.get() | ||
|  | 
 | ||
|  |   obj.value = value | ||
|  |   obj.cb = cb | ||
|  |   obj.run() | ||
|  | } | ||
|  | 
 | ||
|  | function MyObject () { | ||
|  |   this.next = null | ||
|  |   this.value = null | ||
|  | 
 | ||
|  |   var that = this | ||
|  | 
 | ||
|  |   this.run = function () { | ||
|  |     asyncOperation(that.value, that.handle) | ||
|  |   } | ||
|  | 
 | ||
|  |   this.handle = function (err, result) { | ||
|  |     that.cb(err, result) | ||
|  |     that.value = null | ||
|  |     that.cb = null | ||
|  |     instance.release(that) | ||
|  |   } | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Also note how in the above examples, the code, that consumes an istance of `MyObject`, | ||
|  | reset the state to initial condition, just before storing it in the cache. | ||
|  | That's needed so that every subsequent request for an instance from the cache, | ||
|  | could get a clean instance. | ||
|  | 
 | ||
|  | ## Why
 | ||
|  | 
 | ||
|  | It is faster because V8 doesn't have to collect all the functions you | ||
|  | create. On a short-lived benchmark, it is as fast as creating the | ||
|  | nested function, but on a longer time frame it creates less | ||
|  | pressure on the garbage collector. | ||
|  | 
 | ||
|  | ## Other examples
 | ||
|  | If you want to see some complex example, checkout [middie](https://github.com/fastify/middie) and [steed](https://github.com/mcollina/steed). | ||
|  | 
 | ||
|  | ## Acknowledgements
 | ||
|  | 
 | ||
|  | Thanks to [Trevor Norris](https://github.com/trevnorris) for | ||
|  | getting me down the rabbit hole of performance, and thanks to [Mathias | ||
|  | Buss](http://github.com/mafintosh) for suggesting me to share this | ||
|  | trick. | ||
|  | 
 | ||
|  | ## License
 | ||
|  | 
 | ||
|  | MIT | ||
|  | 
 | ||
|  | [npm-badge]: https://badge.fury.io/js/reusify.svg | ||
|  | [npm-url]: https://badge.fury.io/js/reusify | ||
|  | [travis-badge]: https://api.travis-ci.org/mcollina/reusify.svg | ||
|  | [travis-url]: https://travis-ci.org/mcollina/reusify | ||
|  | [coveralls-badge]: https://coveralls.io/repos/mcollina/reusify/badge.svg?branch=master&service=github | ||
|  | [coveralls-url]:  https://coveralls.io/github/mcollina/reusify?branch=master |