Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: using for loop #59

Merged
merged 4 commits into from
Apr 8, 2019
Merged

Idea: using for loop #59

merged 4 commits into from
Apr 8, 2019

Conversation

alexreardon
Copy link
Owner

Moving to for loop for max speed. Need to run some more performance tests

@alexreardon
Copy link
Owner Author

/cc @theKashey

@alexreardon alexreardon changed the title Using for loop Idea: using for loop Apr 1, 2019
): boolean =>
newArgs.length === lastArgs.length &&
newArgs.every(
(newArg: mixed, index: number): boolean =>
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer creating a new function on every call

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a performance concern for V8, every could be even faster!
You need a benchmark to compare with.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

@@ -1,18 +1,19 @@
// @flow
export type EqualityFn = (newArgs: mixed[], lastArgs: mixed[]) => boolean;

const shallowEqual = (newValue: mixed, oldValue: mixed): boolean =>
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function call not required: just doing it right in the for loop

src/index.js Outdated
return false;
}
// Using a for loop rather than array.every for max speed
for (let i = 0; i < newArgs.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbinnssmith
Copy link
Contributor

wbinnssmith commented Apr 4, 2019

It looks like using the for loop is considerably faster. Given the attached benchmark, which tests:

  • the worst case of every element being the same and never breaking early
  • Comparing half the elements and breaking early

...we get the following on Node 11.12 on a 2016 15" MacBook Pro:

shallowEvery with identical lists x 116,831,628 ops/sec ±1.49% (90 runs sampled)
shallowFor with identical lists x 148,396,749 ops/sec ±0.55% (90 runs sampled)
shallowEvery with half-identical lists x 118,280,711 ops/sec ±2.18% (85 runs sampled)
shallowFor with half-identical lists x 155,420,413 ops/sec ±0.58% (89 runs sampled)

Which suggests the for loop is somewhere around 25% or 30% faster all around.

Here's what I ran:

const Benchmark = require('benchmark');

const suite = new Benchmark.Suite();

function shallowEvery(a, b) {
  if (a.length !== b.length) {
    return false;
  }

  return a.every((e, i) => b[i] === e);
}

function shallowFor(a, b) {
  if (a.length !== b.length) {
    return false;
  }

  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) {
      return false;
    }
  }
  return true;
}

let a = {};
let b = {};

let listA = [a, b, {}, {}];
let listB = [a, b, {}, {}];

suite.add('shallowEvery with identical lists', () => {
  shallowEvery(listA, listA);
});

suite.add('shallowFor with identical lists', () => {
  shallowFor(listA, listA);
});

suite.add('shallowEvery with half-identical lists', () => {
  shallowEvery(listA, listB);
});

suite.add('shallowFor with half-identical lists', () => {
  shallowFor(listA, listB);
});

suite.on('cycle', e => console.log(String(e.target)));

suite.run({ async: true });

@wbinnssmith
Copy link
Contributor

Note that in the following case of longer arrays, the gap closes to about a 10% improvement with the c-style for loop.

let listA = [a, b, a, b, a, b, {}, {}, {}];
let listB = [a, b, a, b, a, b, {}, {}, {}];

Gives:

shallowEvery with identical lists x 69,278,060 ops/sec ±1.51% (89 runs sampled)
shallowFor with identical lists x 83,157,110 ops/sec ±1.29% (87 runs sampled)
shallowEvery with half-identical lists x 76,042,431 ops/sec ±1.56% (84 runs sampled)
shallowFor with half-identical lists x 85,637,833 ops/sec ±1.28% (86 runs sampled)

We should keep in mind this is all on V8, and a newer version of Node at that. Might be worth testing in other environments.

@alexreardon
Copy link
Owner Author

The difference seems significant enough to warrant a move

@wbinnssmith
Copy link
Contributor

Just ran the benchmark through browserify and tried other environments:

Safari 12.1:

~/s/memoize-one $ browserify benchmarks/shallowEqual.js  | browser-run -b safari | grep ops
shallowEvery with identical lists x 23,047,766 ops/sec ±1.26% (63 runs sampled)
shallowFor with identical lists x 44,252,598 ops/sec ±2.02% (61 runs sampled)
shallowEvery with half-identical lists x 23,925,428 ops/sec ±1.47% (63 runs sampled)
shallowFor with half-identical lists x 45,577,039 ops/sec ±1.26% (62 runs sampled)

Firefox 66:

~/s/memoize-one $ browserify benchmarks/shallowEqual.js  | browser-run -b firefox | grep ops
shallowEvery with identical lists x 71,403,414 ops/sec ±1.92% (62 runs sampled)
shallowFor with identical lists x 110,829,280 ops/sec ±0.76% (65 runs sampled)
shallowEvery with half-identical lists x 60,497,452 ops/sec ±2.50% (60 runs sampled)
shallowFor with half-identical lists x 66,830,408 ops/sec ±0.62% (63 runs sampled)

Definitely seems worthwhile 😄

@alexreardon alexreardon merged commit dc00d09 into master Apr 8, 2019
@alexreardon
Copy link
Owner Author

alexreardon commented Apr 8, 2019

On my local (node 8.11.3)

shallowEvery with identical lists x 7,977,614 ops/sec ±1.30% (84 runs sampled)
shallowFor with identical lists x 117,648,438 ops/sec ±0.59% (88 runs sampled)
shallowEvery with half-identical lists x 8,780,743 ops/sec ±1.54% (82 runs sampled)
shallowFor with half-identical lists x 124,660,454 ops/sec ±0.72% (88 runs sampled)

~16x faster 😲

@alexreardon alexreardon deleted the using-for-loop branch April 8, 2019 04:09
@theKashey
Copy link
Contributor

Look like for is not faster for you, but every is 10x slower.

@theKashey
Copy link
Contributor

node 10.9.0

shallowEvery with identical lists x 62,069,659 ops/sec ±2.19% (80 runs sampled)
shallowFor with identical lists x 89,964,575 ops/sec ±3.30% (82 runs sampled)
shallowEvery with half-identical lists x 65,688,491 ops/sec ±2.35% (80 runs sampled)
shallowFor with half-identical lists x 97,475,216 ops/sec ±2.91% (78 runs sampled)

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

Successfully merging this pull request may close these issues.

3 participants