Immutable.js Map with numeric key (performance test included)

隐身守侯 提交于 2020-01-13 10:05:13

问题


I'm using Immutable.js with Redux in my React Native app.

Metadata, such as lookup tables, are fetched from a server and persisted locally in the app as Immutable.Map. The keys for the lookup values are integers (the primary key in the db).

When I fetch the data all integer keys are coerced into strings. This is normal behaviour for js objects. Thus, when a lookup Map is created, the keys will be strings.

Example:

let colors = Immutable.Map({
  '10': 'yellow',
  '20': 'pink',
  ..
})

The objects that are referencing the lookup values has the reference stored as numeric like this:

let michael = Immutable.Map({
  name: 'Michael',
  colorId: 10
})

Since Immutable.js does not coerce numeric keys to string I cannot do this:

let color = colors.get(michael.get('colorId'))

The above is the same as:

let color = colors.get(10)

This won't work, because the id is a string. This would work:

let color = colors.get('10')

I usually use Immutable.List to store datasets fetched from the server. To get an item I use find(). But the small lookup tables (essentially app metadata) are accessed very often and need to be speedy.

How do you solve this? Here are some alternatives to consider:

1. Manually convert keys to strings when searching

let color = colors.get(michael.get('colorId') + '') 

cons:

  • Not an attractive solution. Seems error prune to me.

2. Use plain js objects for lookup lists

pros:

  • Object key lookup are fast in js
  • Automatic coercion of keys to strings, both ways (writing and reading)

cons:

  • Ideally I'd like to use Immutable.js everywhere.

3. Use List.find() for lookup tables

pros:

  • Same concept as for the big datasets

cons:

  • I don't know the performance impact of using List.find() instead of Map.get()

4. Carefully craft the Immutable.Map to use numeric key

let colors = new Immutable.Map()
  .set(10, 'yellow')
  .set(20, 'pink')

pros:

  • Keys in the maps will match the value referencing the key (i.e. both are numeric)

cons:

  • cumbersome
  • Keys are already coerced to strings in the json object sent over the wire, so I'd essentially be converting them back to numeric (I think)

Edit.. did some performance tests

Interesting data from a simple performance test.

  • Lookup list of 10 items
  • Find a value one million times
  • Mac mini core i7 2.6

Immutable.Map with numeric keys: 185 ms

Immutable.List using find(): 972 ms

Plain JS object with coerced keys: 8 ms

Plain JS array using find(): 127 ms

As I'm using React Native I always have to look out for the 16 ms limit if I want to achieve 60 fps. The benchmark values does not seem to be linear. Running the test with only 100 lookups takes 1 ms with Map and 2 ms with List. That's quite expensive.

Have I done something wrong in the benchmarks? Otherwise I guess I'll have to leave Immutable.js out of this for now :(

Test code

let Immutable = require('immutable');

let mapTest = Immutable.Map()
  .set(1, Immutable.Map({value: 'one'}))
  .set(2, Immutable.Map({value: 'two'}))
  .set(3, Immutable.Map({value: 'three'}))
  .set(4, Immutable.Map({value: 'four'}))
  .set(5, Immutable.Map({value: 'five'}))
  .set(6, Immutable.Map({value: 'six'}))
  .set(7, Immutable.Map({value: 'seven'}))
  .set(8, Immutable.Map({value: 'eight'}))
  .set(9, Immutable.Map({value: 'nine'}))
  .set(10, Immutable.Map({value: 'ten'}));

let listTest = Immutable.fromJS([
  {key: 1,  value: 'one'},
  {key: 2,  value: 'two'},
  {key: 3,  value: 'three'},
  {key: 4,  value: 'four'},
  {key: 5,  value: 'five'},
  {key: 6,  value: 'six'},
  {key: 7,  value: 'seven'},
  {key: 8,  value: 'eight'},
  {key: 9,  value: 'nine'},
  {key: 10, value: 'ten'}
])

let objTest = {
  1:  {value: 'one'},
  2:  {value: 'two'},
  3:  {value: 'three'},
  4:  {value: 'four'},
  5:  {value: 'five'},
  6:  {value: 'six'},
  7:  {value: 'seven'},
  8:  {value: 'eight'},
  9:  {value: 'nine'},
  10: {value: 'ten'}
};

let arrayTest = [
  {key: 1,  value: 'one'},
  {key: 2,  value: 'two'},
  {key: 3,  value: 'three'},
  {key: 4,  value: 'four'},
  {key: 5,  value: 'five'},
  {key: 6,  value: 'six'},
  {key: 7,  value: 'seven'},
  {key: 8,  value: 'eight'},
  {key: 9,  value: 'nine'},
  {key: 10, value: 'ten'}
];

const runs = 1e6;
let i;
let key;
let hrStart;

console.log(' ')
console.log('mapTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
  let result = mapTest.getIn([key, 'value'] )
  key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);


console.log(' ')
console.log('listTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
  let result = listTest
    .find(item => item.get('key') === key)
    .get('value');
  key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);

console.log(' ')
console.log('arrayTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
  let result = arrayTest
    .find(item => item.key === key)
    .value

  key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);


console.log(' ')
console.log('objTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
  let result = objTest[key].value
  key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);

来源:https://stackoverflow.com/questions/41999419/immutable-js-map-with-numeric-key-performance-test-included

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!