Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Even faster compiledGetValueFromFn #110

Open
OlsonDev opened this issue Aug 14, 2015 · 1 comment
Open

Even faster compiledGetValueFromFn #110

OlsonDev opened this issue Aug 14, 2015 · 1 comment

Comments

@OlsonDev
Copy link
Contributor

While working on #109 I noticed the compiled function's source was pretty winded for longer paths and was curious if it could be made less verbose. I was also curious how much faster it was than the prototype non-evald version. So... I put together a benchmark comparing 6 different things:

  1. Looping over an array of keys to traverse using ["1.2"] syntax for float-looking keys
    • Doing the same but using [1.2] syntax for float-looking keys
  2. Compling as is with long if conditions using ["1.2"] syntax for float-looking keys
    • Doing the same but using [1.2] syntax for float-looking keys
  3. obj = obj.foo; (hardcoded), if obj is null then returning defaultValue, move to the next key obj = obj.bar, repeat (using ["1.2"] syntax for float-looking keys)
    • Doing the same but using [1.2] syntax for float-looking keys

Findings:

  • Reusing a local variable either performed the same or substantially beat the current methodology.
  • Using a floating index for a key which would otherwise be a string either tanked performance or had no effect (Firefox)

Example of something generated/compiled with current methodology:

function windedIfConditionCoalescingStringFloat(obj, defaultValue) {
  if (obj != null &&
    obj.foo != null &&
    obj.foo.bar != null &&
    obj.foo.bar.baz != null &&
    obj.foo.bar.baz[42] != null &&
    obj.foo.bar.baz[42]["1.2"] != null &&
    obj.foo.bar.baz[42]["1.2"].a != null &&
    obj.foo.bar.baz[42]["1.2"].a["-9.4"] != null &&
    obj.foo.bar.baz[42]["1.2"].a["-9.4"].taco != null &&
    obj.foo.bar.baz[42]["1.2"].a["-9.4"].taco[" 1 \'\""] != null &&
    "bacon" in obj.foo.bar.baz[42]["1.2"].a["-9.4"].taco[" 1 \'\""])
    return obj.foo.bar.baz[42]["1.2"].a["-9.4"].taco[" 1 \'\""].bacon;
  else
    return defaultValue;
}

Example of highest performing generated/compiled function:

function getValueFrom(obj, defaultValue) {
  if (obj == null) return defaultValue;
  obj = obj.foo;
  if (obj == null) return defaultValue;
  obj = obj.bar;
  if (obj == null) return defaultValue;
  obj = obj.baz;
  if (obj == null) return defaultValue;
  obj = obj[42];
  if (obj == null) return defaultValue;
  obj = obj["1.2"];
  if (obj == null) return defaultValue;
  obj = obj.a;
  if (obj == null) return defaultValue;
  obj = obj["-9.4"];
  if (obj == null) return defaultValue;
  obj = obj.taco;
  if (obj == null) return defaultValue;
  obj = obj[" 1 \'\""];
  return obj != null && "bacon" in obj ? obj.bacon : defaultValue;
}

Notes:

  • Chrome 42.0.2311 on jsperf is actually IE Edge; Browserscope is confused.
  • My IE11 results refuse to upload because of a CSRF token issue. Here's a screenshot:
    image
@OlsonDev OlsonDev changed the title Even faster compiled compiledGetValueFromFn Even faster compiledGetValueFromFn Aug 14, 2015
@jmesserly
Copy link
Contributor

Awesome findings (yet again)! Yeah, the current pattern also has the disadvantage of being O(D^2) where D is the depth of the path, vs O(D) in your "fastest" version.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants