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

big perf regression in collapse_vars #3174

Closed
kzc opened this issue Jun 5, 2018 · 6 comments · Fixed by #3348
Closed

big perf regression in collapse_vars #3174

kzc opened this issue Jun 5, 2018 · 6 comments · Fixed by #3348

Comments

@kzc
Copy link
Contributor

kzc commented Jun 5, 2018

Bug report or feature request?

perf regression

JavaScript input

$ bin/uglifyjs -V
uglify-js 3.4.0
$ curl -s https://raw.githubusercontent.com/mlwilkerson/terser-collapse-vars-perf-regression/master/index.js > slow.js
$ /usr/bin/time bin/uglifyjs slow.js -c collapse_vars=1 | wc -c
       38.35 real        38.41 user         0.07 sys
     286
$ /usr/bin/time bin/uglifyjs slow.js -c collapse_vars=0 | wc -c
        0.54 real         0.57 user         0.04 sys
     286

More details in original report: terser/terser#50

@kzc
Copy link
Contributor Author

kzc commented Jun 6, 2018

In the course of looking into this issue, I came across some odd behavior as illustrated by using this uglify-js 3.4.0 patch with the JS input below:

--- a/lib/compress.js
+++ b/lib/compress.js
@@ -1363,6 +1363,7 @@ merge(Compressor.prototype, {
                     extract_candidates(expr.consequent);
                     extract_candidates(expr.alternative);
                 } else if (expr instanceof AST_Definitions) {
+                    console.error("*** AST_Definitions expr:", expr.print_to_string());
                     expr.definitions.forEach(extract_candidates);
                 } else if (expr instanceof AST_DWLoop) {
                     extract_candidates(expr.condition);
$ echo 'function f(){var x=0; var o = {a:1, b:2}; console.log(x);}' | bin/uglifyjs -c
*** AST_Definitions expr: var x=0,o_a=1,o_b=2
*** AST_Definitions expr: var x=0,o_a=1,o_b=2
function f(){console.log(0)}

Notice that the properties of var o were hoisted into vars of their own despite o not being otherwise referenced. They're dropped by default compress of course, but it appears to be unnecessary work.

@kzc
Copy link
Contributor Author

kzc commented Jun 6, 2018

Possible collapse_vars speed fix - only consider the last 64 variable definition candidates. Passes all tests.

--- a/lib/compress.js
+++ b/lib/compress.js
@@ -1363,7 +1363,12 @@ merge(Compressor.prototype, {
                     extract_candidates(expr.consequent);
                     extract_candidates(expr.alternative);
                 } else if (expr instanceof AST_Definitions) {
-                    expr.definitions.forEach(extract_candidates);
+                    var len = expr.definitions.length;
+                    var i = len - 64;
+                    if (i < 0) i = 0;
+                    for (; i < len; i++) {
+                        extract_candidates(expr.definitions[i]);
+                    }
                 } else if (expr instanceof AST_DWLoop) {
                     extract_candidates(expr.condition);
                     if (!(expr.body instanceof AST_Block)) {

Also verified using smaller AST_Definitions candidate limits (2, 3, 4, etc) and confirmed that all the expect_stdout test results are correct (ignoring the generated code which would be different).

With patch above:

$ /usr/bin/time bin/uglifyjs slow.js -c
function alpha(){"use strict";var obj42={alpha:"42",beta:"42",gamma:[42,42,[],"42","42"]},obj43={alpha:"43",beta:"43",gamma:[43,43,[],"43","43"]},obj44={alpha:"44",beta:"44",gamma:[44,44,[],"44","44"]};console.log(obj42.alpha),console.log(obj43.alpha),console.log(obj44.alpha)}alpha();
        0.55 real         0.59 user         0.05 sys

@jack-guy
Copy link

Just wanted to call a little more attention to this issue - this has huge performance implications, especially for the folks over at @angular/cli. I went from being unable to complete the uglify portion of a build to done in 10 seconds with this patch. Thanks for the investigation and reporting @kzc :)

@kzc
Copy link
Contributor Author

kzc commented Jun 12, 2018

The proposed fix above was incorporated into Terser terser/terser#51 and will be in its next release.

@kzc
Copy link
Contributor Author

kzc commented Nov 13, 2018

No point keeping this ticket open.

@kzc kzc closed this as completed Nov 13, 2018
@mlwilkerson
Copy link

Thanks for the quick fix on that, @kzc

alexlamsl added a commit to alexlamsl/UglifyJS that referenced this issue Mar 19, 2019
alexlamsl added a commit that referenced this issue Mar 19, 2019
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 a pull request may close this issue.

3 participants