-
Notifications
You must be signed in to change notification settings - Fork 336
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
Optimize the performance #211
Comments
👍 I've sometimes prefered other tools to escodegen because of the bad performance when I needed fast compile-times; but my heart is here :P. This is really good news! |
Fantastic news. I know it's extra work but let me recommend a nice macro/micro performance suite. It will aid in pinpointing issues early and prevent future regressions. Also, it promotes drive-by performance related contributions and helps build confidence that community contributions have not caused obvious regressions. Out of curiosity why not try to unite the two projects? |
If @inikulin is OK, I'd like to merge esotope's work into the master :) |
I will be happy if you do that. I've learned some tips during this optimization which I would like to share with you. I found it surprisingly hard to optimize code generator and it was not because of the initial code quality, but because of how code generation works. Use statement/expression generator maps instead of huge switch statementsMy first intentation was to get rid of big switch statements. As you know v8's optimization unit is a function. Having two huge functions with Deep call stacksHowever, this optimization didn't introduced really significant performance gain. This was surprising, because this technique worked really well e.g. in parse5. I started to realize why this effect occured in code gen: parser works on plain input (string and then token stream), so parsing process doesn't involve deep call stacks. Codegen works with hierarchical input(AST), so anyway you need traverse the tree. There is no big choice here: you depth-first search or breadth-first search. My first intention was to switch to the breadth-first search. However it involved an overhead due to the increased algorithm complexity and I didn't win any performace gain with this path. So, I've tried to improve depth-first performance. Here I get rid of Get rid of arraysThis was a tough decision and my "point of no return". escodegen uses arrays in many cases as an IR. Arrays are much slower than string then it comes to the concatenations. Unfortunately it's killed source maps support, and I don't have a good idea how to workaround it at the moment. Reduce the amount of the stringsStrings are immutable, so each time you return string from the gen unit you end up with 2 strings internally, then it concatenated to the result you end up with 2 more additional string and so on. So I use global var to which gen unit concatenate their results directly. This gave a great performance gain, however Gen unit flagsThe next idea was to get rid of option objects for gen units to avoid allocation of the new object for each call. So, at first I generalized all used option sets and end up having just 11 sets. Then I decided to get rid of objects and use binary flags (https://github.com/inikulin/esotope/blob/master/esotope.js#L759). Integer binary arithmetic is ultra-fast and it helped to reduce amount of code in gen units. This increased the possibility of that the gen units will be inlined, since v8 doesn't inline long functions and functions with big cummulative AST. MicroptimizationsI've also made a bunch of microoptimization. E.g. esutils Please, let me know if you need any farther assistance on this. |
Awesome!!! Your assistance is really really great.
Yes. It seems the nice start point. I'll do this first.
It's very interesting insights. I'll investigate it more.
Yes. This causes significant overhead I think.
It's very intersting. Currently, options objects are used for readability. But if it incurs significant overhead, we need to drop it and I prefer to drop it.
It's very interesting.
Interesting. I need to investigate it more. It is a little problematic, but if it has very very large contribution, I'll merge esutils code into |
I've moved |
Importing @inikulin 's benchmark suite into escodegen.... Good benchmark is mandatory for optimization :) |
👍 |
👍 collaboration makes me happy |
Temporary released it as |
@inikulin 's brilliant work inikulin/esotope shows that escoregen should be cleaned up and it can get further performance gains.
To fix #209 at the same time, I'm planning to reconstruct the code base.
My plan is,
Originally it is chosen for simplicity. escodegen's first version ( it can be seen at esprima's old issue's striker.js) takes this. At that time, code is very simple, so using switch makes code readable.
But now, escodegen has a lo of options and has a lot of edge case code. So using methods based dispatch makes code simple, makes functions small, and makes it optimizable in V8.
Introducing dumper. And each functions add fragments unto dumper. It manages SourceMap carefully and It removes Array based code generation.
The text was updated successfully, but these errors were encountered: