-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Conversation
d0a65ed
to
c098b6e
Compare
There was a problem hiding this 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); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
src/library_gl.js
Outdated
var length = name.length + 1; | ||
if (length > ptable.maxUniformLength) { | ||
ptable.maxUniformLength = length; | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
src/library_gl.js
Outdated
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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? :)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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*/
)
There was a problem hiding this comment.
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.).
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
c098b6e
to
e04c8a2
Compare
Updated the PR, what are your thoughts on a 2nd pass? |
5872019
to
918cd4f
Compare
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. |
a28cfb7
to
d754e8a
Compare
…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%)
…es (-0.03%) on minimal WebGL demo
… Optimizes -4 bytes (0.07%)
… 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%)
d754e8a
to
3730bae
Compare
There was a problem hiding this 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).
Yeah, that sounds like a good path forward. |
Should not be squashed when merging to help bisection if I made an oopsie anywhere. Saves ~3% of minimal WebGL demo code size.