The Wayback Machine - https://web.archive.org/web/20200908093743/https://github.com/scala-js/scala-js/issues/2675/
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

Minimize the number of IIFEs generated by Desugar for Closure IR nodes #2675

Open
Atry opened this issue Dec 5, 2016 · 16 comments
Open

Minimize the number of IIFEs generated by Desugar for Closure IR nodes #2675

Atry opened this issue Dec 5, 2016 · 16 comments
Labels

Comments

@Atry
Copy link

@Atry Atry commented Dec 5, 2016

Was: Scala.js with Scala 2.12.0 creates too many unnecessary IIFE

I created a web framework Binding.scala, which generates deeply nested blocks for XHTML literal.

These kind of blocks will be converted to IIFE for ECMAScript 5 like this:

                              x(function(a, b, c) {
                                return function(e) {
                                  var f = B();
                                  return S((new G).d(P(C(new D, function(a) {
                                    return function() {
                                      return (new G).d(P(C(new D, function() {
                                        return function() {
                                          return (new F).g(U(Q(), "\n        "));
                                        };
                                      }(a))));
                                    };
                                  }(a, f)))), x(function(a, b, c, e) {
                                    return function(b) {
                                      return E((new G).d(P(C(new D, function(a) {
                                        return function() {
                                          return x(function() {
                                            return function(a) {
                                              return a;
                                            };
                                          }(a));
                                        };
                                      }(a)))), x(function(a, b, c, e) {
                                        return function(a) {
                                          var f = (new F).g((new J).n([b, c, e]));
                                          return (new Eb).Y((new Fb).Y(f, a), x(function() {
                                            return function(a) {
                                              return a;
                                            };
                                          }(f)));
                                        };
                                      }(a, c, e, b)));
                                    };
                                  }(a, b, c, e)));
                                };
                              }(a, b, c))

The entire JS file can be found at https://github.com/ThoughtWorksInc/todo/blob/3f173cd/js/js-opt.js

These IIFEs are very slow. In some benchmarks, even the output ECMAScript 2015 code (which creates const instead of IIFE) with fast optimizer runs two times faster than ECMAScript 5 code with full optimizer!

I expect that Closure Compiler would inline IIFE, or enabling Closure Compiler for ECMAScript 2015.

@sjrd
Copy link
Member

@sjrd sjrd commented Dec 5, 2016

I don't really understand what you want Scala.js to do here? Could you make that more precise? We don't develop Closure, so changing what it does is not within our ability.

@sjrd
Copy link
Member

@sjrd sjrd commented Dec 5, 2016

It's also not clear to me what "the ECMAScript 2015" code is. Generated how, by what, from what?

@Atry
Copy link
Author

@Atry Atry commented Dec 5, 2016

I mean some benchmarks run faster with the option scalaJSOutputMode := org.scalajs.core.tools.linker.backend.OutputMode.ECMAScript6 than the default setting, even if the previous setting disables Closure Compiler. I guess the reason is that OutputMode.ECMAScript6 avoids IIFE.

I noticed that Closure Compiler supports ECMAScript 2015 input format. I wonder if we could enable Closure Compiler for OutputMode.ECMAScript6, so that the output JavaScript would eliminate IIFE along with other optimizations from the Closure Compiler.

@sjrd
Copy link
Member

@sjrd sjrd commented Dec 5, 2016

I guess the reason is that OutputMode.ECMAScript6 avoids IIFE.

It doesn't, so that's not the actual reason.

The actual reason is that Closure typically degrades performance a bit, up to 50% in some of our benchmarks. If you want to fairly compare the ES5 version versus ES2015, you should either compare their fastOpt variants, or explicitly disable Closure in fullOpt for ES5.

That said, we should indeed enable Closure in the ES2015 output now that it supports it.

@Atry
Copy link
Author

@Atry Atry commented Dec 5, 2016

@sjrd I re-checked the ECMAScript 2015 output. You are right, there are many IIFEs in the output.

I used to think that ECMAScript 2015 target does not use IIFE in favor of const and let.

@Atry
Copy link
Author

@Atry Atry commented Dec 5, 2016

AFAIK, the Scala.js compiler inline regular functions, however, it brings more IIFE function calls.
I wonder what the purpose is for so many IIFEs.

@sjrd
Copy link
Member

@sjrd sjrd commented Dec 5, 2016

In general, they are necessary so that captures are truly immutable (and not a read-only version of a variable whose value changes in each iteration of a loop). Not all of them are necessary. A more sophisticated translation in JSDesugaring could avoid some of them. I have never done so because I lack proper macro benchmarks doing lots of anonymous functions to evaluate the changes.

@Atry
Copy link
Author

@Atry Atry commented Dec 5, 2016

I noticed that Scala 2.11.x does not create IIFEs (creating prototype classes instead), and has better performance for most cases than Scala 2.12.x.
FYI.

@Atry Atry changed the title Closure Compiler does not properly inline IIFE Scala.js with Scala 2.12.0 creates too many unnecessary IIFE Dec 6, 2016
@Atry
Copy link
Author

@Atry Atry commented Dec 7, 2016

The result of the benchmark can be found at https://atry.github.io/scala-js/table.html , and the source code is at https://github.com/krausest/js-framework-benchmark

You may notice that Scala 2.12 is slower than 2.11 for almost every benchmark and consumes 122% memory comparing to Scala 2.11 version.

@sjrd
Copy link
Member

@sjrd sjrd commented Dec 7, 2016

Could you also benchmark with 2.11 with the -Ydelambdafy:method flag? It should cause 2.11 to emit lambdas using a scheme closer to what 2.12 does. That would give more insight on whether it is the lambda encoding's fault or something else in 2.12.

@Atry
Copy link
Author

@Atry Atry commented Dec 7, 2016

Yes, Scala 2.11 with -Ydelambdafy:method yields the same result as Scala 2.12

@sjrd
Copy link
Member

@sjrd sjrd commented Dec 7, 2016

Thanks. I guess it would be worth trying to improve the emitter about that, then.

@sjrd sjrd changed the title Scala.js with Scala 2.12.0 creates too many unnecessary IIFE Minimize the number of IIFEs generated by Desugar for Closure IR nodes Dec 7, 2016
@sjrd sjrd added the optimization label Dec 7, 2016
@Atry
Copy link
Author

@Atry Atry commented Dec 29, 2016

@sjrd , As long as Closure compiler supports ECMAScript 6 input, which allows scoped let and const, is it possible to not generate any IIFE at all?

@sjrd
Copy link
Member

@sjrd sjrd commented Dec 29, 2016

With the ECMAScript6 output mode, yes.

@sjrd sjrd added this to the Post-v1.0.0 milestone Oct 11, 2017
@gzm0 gzm0 removed this from the Post-v1.0.0 milestone Apr 8, 2020
@gzm0
Copy link
Contributor

@gzm0 gzm0 commented May 4, 2020

Is this still an issue? My understanding of the discussion is that using ES6 entirely mitigates the need for this.

@sjrd This had me toying with the idea of always generating ES6 when using closure and have closure compile ES6 down to ES5.1.

This would:

  • Probably give some shorter code to ES5 users in fullOptJS
  • Let us remove the javascript.Trees.DocComment tree.

WDYT?

gzm0 added a commit to gzm0/scala-js that referenced this issue May 4, 2020
In turn, we configure closure to compile ES6 down to ES5. This gives
closure more structured information about the relevant trees.

As a side-effect, this fixes scala-js#2675.
gzm0 added a commit to gzm0/scala-js that referenced this issue May 4, 2020
In turn, we configure closure to compile ES6 down to ES5. This gives
closure more structured information about the relevant trees.

As a side-effect, this fixes scala-js#2675.
gzm0 added a commit to gzm0/scala-js that referenced this issue May 4, 2020
In turn, we configure closure to compile ES6 down to ES5. This gives
closure more structured information about the relevant trees.

As a side-effect, this fixes scala-js#2675.
@sjrd
Copy link
Member

@sjrd sjrd commented May 4, 2020

Is this still an issue? My understanding of the discussion is that using ES6 entirely mitigates the need for this.

ES6 has the features that would allow us not to generate IIFEs, but we don't leverage them.

This had me toying with the idea of always generating ES6 when using closure and have closure compile ES6 down to ES5.1.

This would:

* Probably give some shorter code to ES5 users in fullOptJS
* Let us remove the `javascript.Trees.DocComment` tree.

WDYT?

In general, one cannot compile down ES 2015 semantics entirely accurately to ES 5.1. There are choices involved, and not all compilers agree. We cannot guarantee that GCC makes the same choices (and will continue to make the same choices) regarding the particular semantics to give to things that do not have an exact equivalent in ES 5.1. Therefore I think that giving ES 2015 to GCC while we give ES 5.1 in fastOpt would be dangerous. We could leave things go unchecked/broken in production mode.

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

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.