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

More webgl code size opts #7986

Merged
merged 14 commits into from
Feb 7, 2019
Merged

Conversation

juj
Copy link
Collaborator

@juj juj commented Feb 3, 2019

Should not be squashed when merging to help bisection if I made an oopsie anywhere. Saves ~3% of minimal WebGL demo code size.

@juj juj force-pushed the more_webgl_code_size_opts branch 3 times, most recently from d0a65ed to c098b6e Compare February 4, 2019 13:53
Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Nice work, everything I didn't comment on looks good.

@@ -1703,7 +1703,7 @@ var LibraryGL = {
#if GL_ASSERTIONS
GL.validateGLObjectID(GL.textures, texture, 'glBindTexture', 'texture');
#endif
GLctx.bindTexture(target, texture ? GL.textures[texture] : null);
Copy link
Member

Choose a reason for hiding this comment

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

Nice to rely on textures[0] === null!

Please add a comment in the source where we define those arrays, that index 0 contains null for that purpose (reduce the risk of later confusion).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is already a comment counter: 1, // 0 is reserved as 'null' in gl that has been around for a long time it looks like.

var length = name.length + 1;
if (length > ptable.maxUniformLength) {
ptable.maxUniformLength = length;
}
Copy link
Member

Choose a reason for hiding this comment

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

why is this change shorter? just because Math.max does not minify down in closure?

if that's the issue, I'd rather not do this, as it decreases readability. Instead closure, or we, could do var x = Math.max once at the toplevel, and we'd use the minified name here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's right, Math.max does not minify since it is an extern. Without Math.max the code becomes

var h=L.getActiveUniform(b,d),k=h.name,l=k.length+1;l>a.L&&(a.L=l);l=k.lastIndexOf("[")

whereas with Math.max() code is

var h=L.getActiveUniform(b,d),k=h.name;a.L=Math.max(a.L,k.length+1);var l=k.lastIndexOf("[");

Closure has a bug there it looks like, it could have done

var h=L.getActiveUniform(b,d),k=h.name,l;a.L=Math.max(a.L,k.length+1);l=k.lastIndexOf("[");

to save 2 more bytes, but that would still be 4 bytes longer than the version without Math.max.

Also,

var x=Math.max;p.m=x(p.m,l);

would be longer than just

p.m=Math.max(p.m,l);

so the assignment var x=Math.max; is only shorter if it can be amortized between multiple uses of Math.max. Searching candidates to amortize with shows only emscripten_set_timeout_loop() in asm.js mode as worthwhile, but not many apps would necessarily use that.

Code pattern

var length = ...;
if (length > max) {
  max = length;
}

does not look too unusual, it is after all what every beginner programmer writes when they start out and are not yet familiar with the language's built-in math primitives. Closure is just telling they are not wrong :)

Copy link
Member

Choose a reason for hiding this comment

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

What about Math_max, though? with a _ there, closure should minify it properly? We do already define that, so you can just use it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

var Math_max=Math.max; will minify down to var x=Math.max; from above. The define does not come free, it is not getting used anywhere, and it's getting optimized out. If it was not optimized out, it still would need to amortize for it to warrant its weight.

Copy link
Member

Choose a reason for hiding this comment

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

It's true we'd still have var x=Math.max, but that's a single cost compared to if(a<b)a=b; in multiple places (which we'd replace with a=x(a,b);. So with enough uses that's better. I don't think we should bet against not having more uses.

If we do want to super-optimize stuff like this, see my other comment about the new estree optimizer - we could write a specific pass to optimize this exact thing, to see how many max operations there are, and pick between the two forms globally.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm, I think that would be a complete overkill in terms of complexity.. It should be much simpler to have a if (x > max) max = x; in code, compared to writing a full optimization pass to do the job?

var len = length ? {{{ makeGetValue('length', 'i*4', 'i32') }}} : undefined;
source += UTF8ToString({{{ makeGetValue('string', 'i*4', 'i32') }}}, len >= 0 ? len : undefined);
var len = length ? {{{ makeGetValue('length', 'i*4', 'i32') }}} : -1;
source += UTF8ToString({{{ makeGetValue('string', 'i*4', 'i32') }}}, len < 0 ? 9e9/*short abbrv. for infinity*/ : len);
Copy link
Member

Choose a reason for hiding this comment

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

I think this would be clearer with Math.infinity, and we can assume closure will minify it (if not, we can file a bug, and could temporarily force it to do so with a toplevel var Math_infinity = Math.infinity and use Math_infinity here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It would be clearer with infinity, but similarly outlining var Math_infinity = Infinity; would be shorter only if multiple uses of Math_infinity could amortize the hit. (also all uses of such var Math_infinity should likely have to be in cold program paths, because a JIT can see that Infinity is always a constant literal, whereas a dereference via a Math_infinity could sneakily change under it, so it could not do JIT magic on such a variable)

9e9 is pretty large, no? :)

Copy link
Member

Choose a reason for hiding this comment

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

It seems safer to add Math_infinity and use that - otherwise 9e9 will eventually get us in trouble, or at least be confusing to read - it just looks arbitrary.

Another option may be to do var VERY_LARGE = 9e9; with a big comment on that, and use that in places like this. Or is this really the only one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That code site has several 'i32 accesses right next to it, so I am not concerned e.g. about wasm64 or similar. This is currently the only place in code that has a construct like this.

var VERY_LARGE = 9e9; is a good idea, but we cannot do it until google/closure-compiler#3187 is resolved. (Having a function local define and use of VERY_LARGE is practically about the same as 9e9/*VERY_LARGE*/)

Copy link
Member

Choose a reason for hiding this comment

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

If we can't rely on closure for such an optimization, another option is to write one ourselves. #7973 has a new estree-based optimization infrastructure, which is much nicer to write passes in (has a proper AST, instead of uglify1's node[1][3][2] etc.).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We cannot write a generic optimizer pass to optimize Infinity to 9e9, because we don't know about the use case, In this specific location 9e9 is large enough to serve as infinity, but in other places it may not be?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh you meant specifically for the literal expansion of a VERY_LARGE variable.. I am not sure that it would be something I can jump on right now, and the worth is not that high, especially since it would be to automatically optimize what was trivial to write in code in the first place.

@juj juj force-pushed the more_webgl_code_size_opts branch from c098b6e to e04c8a2 Compare February 4, 2019 20:55
@juj
Copy link
Collaborator Author

juj commented Feb 4, 2019

Updated the PR, what are your thoughts on a 2nd pass?

@juj juj force-pushed the more_webgl_code_size_opts branch 5 times, most recently from 5872019 to 918cd4f Compare February 6, 2019 11:53
@juj
Copy link
Collaborator Author

juj commented Feb 6, 2019

I think it is probably simplest and fastest to avoid the questionable optimizations. Pushed a revert to those, it would be good to land this to be able to bring the different optimizations together in the tree.

@juj juj force-pushed the more_webgl_code_size_opts branch from a28cfb7 to d754e8a Compare February 6, 2019 16:11
juj added 14 commits February 6, 2019 22:12
…ex 0, so testing "id ? GL.table[id] : null" is redundant. Optimizes -21 bytes (-0.13%) in minimal WebGL demo
…ne. Saves -17 bytes (-0.11%) on minimal WebGL demo
… Reduces minimal WebGL demo by -57 bytes (-0.92%)
… to collapse redundant assignments. Saves -16 bytes (-0.31%) in minimal WebGL demo.
…npack alignment. Saves -5 bytes (-0.10%). Also use GL_ASSERTIONS instead of ASSERTIONS in WebGL library.
…m srx/library_gl.js. Regresses hello webgl by 10 bytes (+0.17%)
@juj juj force-pushed the more_webgl_code_size_opts branch from d754e8a to 3730bae Compare February 6, 2019 20:17
Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

lgtm. I think we should think more about how to optimize stuff like max etc. - we can do more stuff than closure can, because it is fully general, while we could make stronger assumptions in our code (like Math.min not being changed).

@juj juj merged commit 1ec0503 into emscripten-core:incoming Feb 7, 2019
@juj
Copy link
Collaborator Author

juj commented Feb 7, 2019

Yeah, that sounds like a good path forward.

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

Successfully merging this pull request may close these issues.

None yet

2 participants