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 linear search on list traversing #12302

Merged
merged 2 commits into from Nov 5, 2020
Merged

Conversation

JLHwung
Copy link
Contributor

@JLHwung JLHwung commented Nov 3, 2020

Q                       A
Fixed Issues? Fixes #11527
Tests Added + Pass? No, see below
Documentation PR Link
Any Dependency Changes?
License MIT

Checkout this branch, build Babel and run https://gist.github.com/robmcl4/080bf388c95de3b2761307b39d6f895a

The output on my local machine is attached here for reference

Current main
2,0.049
4,0.037
8,0.017
16,0.018
32,0.028
64,0.057
128,0.114
256,0.306
512,0.900
1024,3.106
2048,11.738
4096,45.160
This PR
2,0.046
4,0.043
8,0.022
16,0.020
32,0.028
64,0.053
128,0.083
256,0.164
512,0.336
1024,0.658
2048,1.343
4096,2.764

In other words, traversing an ArrayLiteral of 4096 elements can be up to 16x faster. The execution time also looks linear.

Note that this issue is not limited to ArrayLiteral, I can reproduce it on any list-style AST structures: e.g. Program, ObjectLiteral, BlockStatement, etc. Although it is uncommon that an array literal may contain 4096 elements, a Program/BlockStatement, especially bundled, could contain thousands of statements as their list children.

@JLHwung JLHwung added pkg: traverse PR: Performance 🏃‍♀️ A type of pull request used for our changelog categories labels Nov 3, 2020
@codesandbox-ci
Copy link

codesandbox-ci bot commented Nov 3, 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 3a365bf:

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

@babel-bot
Copy link
Collaborator

babel-bot commented Nov 3, 2020

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

Copy link
Member

@kaicataldo kaicataldo left a comment

Choose a reason for hiding this comment

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

🎉

@JLHwung JLHwung marked this pull request as draft November 3, 2020 20:12
@nicolo-ribaudo
Copy link
Member

This is awesome 😄

@JLHwung
Copy link
Contributor Author

JLHwung commented Nov 3, 2020

@nicolo-ribaudo @kaicataldo Thanks! I converted this PR to draft since it is breaking TDZ implementation, which I am investigating.

visited.push(path.node);
const { node } = path;
if (visited.has(node)) continue;
if (node) visited.add(node);
Copy link
Contributor Author

@JLHwung JLHwung Nov 4, 2020

Choose a reason for hiding this comment

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

Note that when path.node is null, the current behaviour is to visit this path once but skip other nullish node paths since null can be hold in array. In this PR they will all be visited. This could be a breaking change in a way but I think the equality of two paths whose path.node is null, is not well-defined.

The usage of WeakSet is intentional, so we don't leak memory when path.node is GCed.

if (!pathCache.has(parent)) {
let paths = pathCache.get(parent);
if (!paths) {
paths = new Map();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

We can create an IterableWeakMap, falling back to a normal Map on Node.js <13

nodejs/node#35915

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh the IterableWeakMap is not a builtin object nor supposed to be a public Node.js API, I suggest we hold off a bit since WeakRef (relied by IterableWeakMap) is still stage 3 and @babel/traverse is heavily used.

@jedwards1211
Copy link
Contributor

jedwards1211 commented Dec 30, 2020

@JLHwung I just bisected and determined that this introduced the regression #12570, do you have any thoughts on how to solve that?

@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 Mar 31, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 31, 2021
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 pkg: traverse PR: Performance 🏃‍♀️ A type of pull request used for our changelog categories
Projects
None yet
Development

Successfully merging this pull request may close these issues.

traverse of Array Literal takes O(n^2) time
6 participants