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

Reduce dependency on lodash functions: values, extends #11798

Merged
merged 21 commits into from Jul 8, 2020
Merged

Reduce dependency on lodash functions: values, extends #11798

merged 21 commits into from Jul 8, 2020

Conversation

jayaddison
Copy link
Contributor

@jayaddison jayaddison commented Jul 6, 2020

Q                       A
Patch: Bug Fix? No
Major: Breaking Change? No
Minor: New Feature? No
Tests Added + Pass? No
Any Dependency Changes? No
License MIT

As mentioned by @hzoo in #11790 (comment), we can remove the (only) use of lodash.values within the codebase with a slightly longer-form but logically-equivalent iteration over the target object properties followed by retrieval of each object value.

  • lodash/values is replaced by Object.keys in combination with map and object property access-by-key
  • lodash/extend is replaced by use of native JavaScript objecty property iteration

@jayaddison
Copy link
Contributor Author

(NB: Using Object.values was considered as an alternative during #11790 but was discounted since that prototype method isn't reliably available until Node 7.x)

@codesandbox-ci
Copy link

codesandbox-ci bot commented Jul 6, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 2b03b2e:

Sandbox Source
babel-repl-custom-plugin Configuration
babel-plugin-multi-config Configuration

@babel-bot
Copy link
Collaborator

babel-bot commented Jul 6, 2020

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/25613/

@jayaddison jayaddison changed the title Replace lodash 'values' usage with Object.keys => .map(obj[key]) Reduce dependency on lodash functions: values, extends Jul 6, 2020
@existentialism existentialism added the PR: Internal 🏠 A type of pull request used for our changelog categories label Jul 6, 2020
@jayaddison
Copy link
Contributor Author

jayaddison commented Jul 6, 2020

❓ Looks like I've introduced a performance regression here somehow; the CI unit tests appear to be timing out.

@jayaddison
Copy link
Contributor Author

I'm pretty confused about how/where I might have introduced the performance regression here - I'd appreciate any ideas and suggestions on how to track that down :)

@AviVahl
Copy link
Contributor

AviVahl commented Jul 7, 2020

Could be that the Maps are costing way more than objects if they contain low number of items.
Also, in:

  for (const key of Object.keys(obj)) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      map.set(key, obj[key]);
    }
  }

You could remove the if and just set the key, as Object.keys returns own enumerable keys. The Object.prototype.hasOwnProperty lookup itself on every iteration is also costly (if not removed, at least destructured once outside the function).

@jayaddison
Copy link
Contributor Author

Thanks @AviVahl - the timeouts during continuous integration had been occurring before the if condition was introduced, so I'm not sure that it'll turn out to be the cause, but it's good to know that it's redundant code and can be removed -- I've gone ahead and reverted it.

…; access performance appears much improved for Object property access
@JLHwung
Copy link
Contributor

JLHwung commented Jul 7, 2020

@jayaddison Sorry for the late reply. The performance regression, as I can observe, is not introduced by Map but an implicit infinite loop.

One of key differences between Object.keys() and Map.prototype.keys() is that the latter returns an iterator, which means if you add new element to the Map in a loop, the iterator will eventually visit the new element.

In the following example, the for loop is infinite because we keep adding new key to the map. (0, -1, 0, -1 ...) though the original map has only one element ( 0 => undefined ).

const map = new Map([[0]]);
for (const k of map.keys()) {
  a.delete(k);
  a.set(~k, undefined);
}

Now let's take a look at our codebase,

for (const name of outsideRefs.keys()) {
const id = outsideRefs.get(name);
if (
this.scope.hasGlobal(id.name) ||
this.scope.parentHasBinding(id.name)
) {
outsideRefs.delete(id.name);
this.letReferences.delete(id.name);
this.scope.rename(id.name);
this.letReferences.set(id.name, id);
outsideRefs.set(id.name, id);
}
}

hangs because in every loop scope.rename() generates a new name, assigns to id.name and this name is added into outsideRefs. Since scope.rename() avoids name conflicts, the loop will never terminate.

To mitigate we can take a snapshot of all the keys in outsideRefs

[...outsideRefs.keys()]

… storage; access performance appears much improved for Object property access"

This reverts commit 2119acc.
@jayaddison
Copy link
Contributor Author

Brilliant, thanks @JLHwung - oddly an infinite loop was my initial guess for the CI problems but I couldn't figure out where that might have been happening, your explanation makes complete sense.

It looks like there are now some legitimate unit test failures here; I'm finishing up for the day but will take a look at those soon (and will finally make another effort to get unit tests running correctly locally).

@JLHwung
Copy link
Contributor

JLHwung commented Jul 7, 2020

@jayaddison I am really appreciated for your efforts!

It looks like there are now some legitimate unit test failures here

I believe they are due to

const declared = state.letReferences[node.name];

@jayaddison
Copy link
Contributor Author

@jayaddison I am really appreciated for your efforts!

It looks like there are now some legitimate unit test failures here

I believe they are due to

const declared = state.letReferences[node.name];

Glad to help - and thanks, yep - that was it!

Copy link
Contributor

@JLHwung JLHwung left a comment

Choose a reason for hiding this comment

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

Nice work, thanks.

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

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

Thanks!

@nicolo-ribaudo nicolo-ribaudo merged commit bff6298 into babel:main Jul 8, 2020
@jayaddison jayaddison deleted the dependencies/reduce-lodash-usage-values branch July 8, 2020 11:55
@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Oct 8, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated A closed issue/PR that is archived due to age. Recommended to make a new issue PR: Internal 🏠 A type of pull request used for our changelog categories
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants