Ran into this while profiling a hot path in our application where we follow the pattern var thing = fooMap.get('key', new Immutable.Map()) in order to ensure we have an object to work with. The v8 profile flagged that line as a surprisingly high share of CPU load so I put together a quick benchmark gist with various ways to format the line, my results:
// map has key and returns the value
baseline 169ms
// map has the key, but pass a new Immutable.Map() as the second parameter
successful accessor, always creating new map 539ms
// map has the key, fall back via || operator to a new Immutable.Map()
successful accessor, conditionally creating new map 417ms
// map doesn't have the key, pass new Immutable.Map()
failed accessor, always creating new map 667ms
// map doesn't have the key, fall back via || operator to new Immutable.Map()
failed accessor, conditionally creating new map 517ms
// map doesn't have the key, pass an existing empty Map as the second parameter
failed accessor, locally cached map 50ms
// map doesn't have the key, pass a an empty Map that was attached to Immutable.Map
failed accessor, immutable cached map 67ms
The results show that re-using an empty Map object is over 10x (in this microbenchmark) faster than calling new Immutable.Map(); I debugged the call to create an empty Map and verified it is properly re-using EMPTY_MAP, so it looks like this may all be due JavaScript's function call & stack overhead we know and love.
That said, I was wondering if you would be willing to include empty collections objects statically, something like Immutable.Map.Empty and Immutable.List.Empty which, while still somewhat slower (the last benchmark value above) it remains much faster than calling new Immutable.Map() and would allow us to not define a new empty Map in various modules.
You may argue this is over-optimizing (and it may be) but by passing the cached Map we had a noticeable impact on our server-side rendering.
Right, calling Immutable.Map() is expensive. Thus my question of can an empty Map, List, etc, be produced & exported on startup so applications can use that variable instead of calling Immutable.Map() ?
Closing this aging issue.
The solution is to not use new. The JS VM will allocate new memory when call new even though Immutable.js does not use that memory, and that can create a performance hit.
In fact, Immutable.Map() when called with no arguments does in fact return the same instance of the empty Map every time, and VMs are very fast at optimizing paths like this.
Most helpful comment
Closing this aging issue.
The solution is to not use
new. The JS VM will allocate new memory when callneweven though Immutable.js does not use that memory, and that can create a performance hit.In fact,
Immutable.Map()when called with no arguments does in fact return the same instance of the empty Map every time, and VMs are very fast at optimizing paths like this.