Search code examples
javascriptnode.jslodashv8

why _.defaults() in lodash takes a lot of cpu time?


I have found a performance issue in my application in production env.In order to Reproduce the issue, I have write a sample code in local. I download the data from the pro env, and ran the sample with V8 profilling.At last I found that copyObject() in lodash occupies the most cpu time.The V8 profiling screenshot is at below.

enter image description here

This is the copyObject func source code:

function copyObject(source, props, object, customizer) {
  var isNew = !object;
  object || (object = {});

  var index = -1,
      length = props.length;

  while (++index < length) {
    var key = props[index];

    var newValue = customizer
      ? customizer(object[key], source[key], key, object, source)
      : undefined;

    if (newValue === undefined) {
      newValue = source[key];
    }
    if (isNew) {
      baseAssignValue(object, key, newValue);
    } else {
      assignValue(object, key, newValue);
    }
  }
  return object;
}

Btw, I use the _.defaults() in a for-loop, and input data is large.

Because the keys of the object I need is known, I replaced the _.defaults() with the following code, the cpu time reduced by half more.

const res = {
  'key1': object.key1 || source1.key1 || source2.key1,
  'key2': object.key2 || source1.key2 || source2.key2,
  'key3': object.key3 || source1.key3 || source2.key3,
}

My question is what method in copyObject func cause the most cpu time? And is it normal? Thanks!


Solution

  • By looking at the lowdash code I see a number of slow things.

    1. it uses Array.prototype.forEach to loop over an array of objects. forEach is slow as is and creates a new function scope.
    2. It then makes an object out of the object in order to loop over its keys. The object() thing is for you waste of cpu and then looping over keys with for in is also very slow.
    3. Finally it performs value check as well as checking hasOwnProperty which both are wasted on you.

    In general it is always better to write your own set, clone, copy, default functions when you know the structure since a direct assign is faster than any thing else you can do.

    NOTE: Careful with the double pipes (||) since 0 might be an OK value but 0 || 1 === 1. Other values also follow this rule. Just make an if (or ? : ) statement and you will be safer.