Tuesday, October 17, 2017

Re: Controlling Inling in GWT compiler

For the first one, chaining methods has the unfortunate detail that each method has a return value, which the next object is called on. To my knowledge, there is no optimization in the compiler which currently can undo the presence of the return value, and recognize that the method returns "this" (or a param, some other easily aliased value) as a special case.

The canonical example I like for this is StringBuilder - three main methods we care about, so it is easier to understand in an example. Each is followed by its compiled JS
constructor: new A(), does something like this.str = "";
append: object.b(string), or $b(static$this, string) when made static. Both have an impl that does this.str+=string (or this$static.str+=string, which is of course shorter when compiled)
toString(): object.c(), or c(static$this) when made static. Returns this.str, or static$this.str.

Example code:
String s = new StringBuilder().append("1").append(name).append("2").toString()
Trivial JS impl:
var s=new A().b("1").b(name).b("2").c();
Actual JS impl, once methods have been made final and devirtualized (made static, since there are no overrides possible here):
var s=c(b(b(b(new A),"1"),name),"2");
"Inlined" implementation (ignoring the trivial string rewrite, as your example would also still have to be separate statements):
var t=new A;t.str+="1";t.str+=name;t.str+="2";var s=t.str;

Lets assume that "str" was only one char, since we are compiling after all, 40 chars for the "idiomatic" js version, 37 chars for staticified version, 50 for the "inlined" version. Granted, we have to pay the costs of those static/inlined versions existing at all, but if your method is called more than once, it becomes worth it. So I would dispute "compact" form, at least without more detail - a chained "setter" of form a(b,c) is going to be shorter than b.a=c; by one character (the semicolon, or newline), and can itself be inlined into another expression, while the "field assignment" can't be.

Until you get to object literal creation - which can't be optimized for normal java objects (for reasons I don't fully understand, assume it has to do with possible side effects in clinits). Remember too that the GWT compiler is mostly geared toward Java objects, where calling super-super-superclass constructors, run all initializers and clinits while they are at it. A non-issue in JS, I'll admit, so GWT hasn't been geared toward this historically. Closure on the other hand...

https://closure-compiler.appspot.com/home#code%3D%252F%252F%2520%253D%253DClosureCompiler%253D%253D%250A%252F%252F%2520%2540compilation_level%2520ADVANCED_OPTIMIZATIONS%250A%252F%252F%2520%2540output_file_name%2520default.js%250A%252F%252F%2520%253D%253D%252FClosureCompiler%253D%253D%250A%250A%252F%252F%2520ADD%2520YOUR%2520CODE%2520HERE%250Avar%2520a%2520%253D%2520%257B%257D%253B%250A%250Aa.a%2520%253D%2520false%250Aa.b%2520%253D%2520%27asdf%27%253B%250Aa.c%2520%253D%25203%253B%250A%250Aconsole.log(a)%253B

...is expecting this case, and is prepared for it, in exactly the way that you expected it would.

I still suspect (having not experimented with it) that the "return this" aliasing will still confuse matters, at least without deliberate code in closure to handle it. At least with strings, it isn't smart enough:
https://closure-compiler.appspot.com/home#code%3D%252F%252F%2520%253D%253DClosureCompiler%253D%253D%250A%252F%252F%2520%2540compilation_level%2520ADVANCED_OPTIMIZATIONS%250A%252F%252F%2520%2540output_file_name%2520default.js%250A%252F%252F%2520%253D%253D%252FClosureCompiler%253D%253D%250A%250A%252F%252F%2520ADD%2520YOUR%2520CODE%2520HERE%250Afunction%2520A()%257Bthis.str%253D%27%27%257D%253B%250AA.prototype.b%2520%253D%2520function(string)%2520%257Bthis.str%252B%253Dstring%253B%2520return%2520this%253B%257D%253B%250AA.prototype.c%2520%253D%2520function()%2520%257Breturn%2520this.str%257D%253B%250A%250Aconsole.log(new%2520A().b(%2522a%2522).b(window.prompt()).b(%2522b%2522).c())%253B
I'm having a hard time modeling this in plain JS to get it to treat the constructor like an object, so as to correctly handle the "return this". Might be better as a @JsOverlay...

---

I played with a similar optimization in GWT a few years ago, but ended up not finishing it. It mostly dealt with trying to deal with compile-time strings and avoiding runtime regular expressions on them, but I abandoned it. If there is enough interest in this, I think I at least have the working knowledge to give it a shot... but lets be sure that we get something out of it.

create({a:'asdf',b:1,c:false});
create(c(b(a({},'asdf'),1),false);
Literal is 31 bytes, chained is 35, so two bytes per setter (plus the cost of the setter method, which can then be reused). Might not be worth more than a few hours of time, but then again, the work could lead to other unexpected benefits...


On Tuesday, October 17, 2017 at 7:58:23 PM UTC-5, Peter Donald wrote:
Hi,

So I am trying to figure out a way to control inlining in GWT compiler and potentially if it will be compatible with J2CL. My motivation is that I want to build type-safe builders that are compiled away at runtime. The idea is to compile something like
new InputBuilder().type( InputType.checkbox ).className( "foo" ).child( "Hello" ).build()
to
React.createElement('input', { className: 'foo' }, 'Hello')
And browsing the code I thought I could make use of @ForceInline and @HasNoSideEffects in appropriate places to control the inlining. The goal would essentially be to move most of the building code into caller which allow the data flow analyzer to zap it into a nice compact form. But I can not seem to get it to work.

It is unclear whether I am using it wrong or it just won't work and reading through the compiler source code is proving to be slow way to understand the problem. 

@ForceInline does not seem to force inlining but instead raises an error if code is not inlined? Am I understanding this correctly? Certainly adding the annotation to all the places that I wanted that occur causes a error like "Function X is marked as @ForceInline but it could not be inlined". Reading through the JsInliner source code has not enlightened me why.

So more code less talk. Here is the example I have been working with


Next question is around optimisation of the new jsinterop code. It seems that GWT compiler does not work particularly well and something like
final JsPropertyMapOfAny prop = JsPropertyMap.of();
prop.set( "a", 1 );
prop.set( "b", 2 );
prop.set( "c", 3 );
will not produce an object literal.
So do you think this is an achievable goal with GWT2, what about in J2CL? Any suggestions on how I could achieve this?
BTW I know using things like @ForceInline and @HasNoSideEffects is "dangerous" and unsupported but I am comfortable with the impact I would have on compile size/time. (Once worked on JikesRVM which is a JVM written in Java which has similar annotations and similar tradeoffs - see http://www.jikesrvm.org/JavaDoc/org/vmmagic/pragma/package-summary.html)

--
Cheers,

Peter Donald

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment