Hi,
I've been looking into some performance issues in Chrome Canary for a HTML5 game I released a little while back. At present, Firefox Nightly runs this game a lot faster than Canary does, which is surprising to me because Chrome has a much better Canvas backend and used to do much better at running this game. From doing some profiling and comparing profiles between the browsers, I am pretty sure I am running into a V8 issue here - perhaps because something is wrong with my JS.
I ran the game with trace-opt, trace-bailout and trace-deopt turned on. I see tons and tons of marking and optimization happening while the game is running, and it never settles down, despite the fact that the game is not particularly dynamic once it gets going - it reaches a steady state where it is not generating tons of code on the fly, and types are not changing, so it shouldn't be necessary to constantly recompile functions.
Worse, though, I see a lot of these messages:
[disabled optimization for Game_EnqueueTick, reason: optimized too many times]
[disabled optimization for KinematicBody_get_SupportingBody, reason: optimized too many times]
[disabled optimization for SpriteBatch_InternalDraw, reason: optimized too many times]
[disabled optimization for DrawScaleF, reason: optimized too many times]
[disabled optimization for SpriteBatch_DeferBlit, reason: optimized too many times]
From looking at the code, this appears to be a deopt that is hit once a function has been optimized 1000 times. I can't imagine why these functions would be optimized 1000 times in the first place, and the deopt seems to be hurting them because profiles in the Web Inspector show some of these functions as bottlenecks - but in SpiderMonkey they barely contribute to CPU time in comparison (and some of them are extremely simple).
For example:
function DrawScaleF (texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth) {
var sourceX = 0, sourceY = 0, sourceWidth = 0, sourceHeight = 0;
if (sourceRectangle !== null) {
sourceX = sourceRectangle.X;
sourceY = sourceRectangle.Y;
sourceWidth = sourceRectangle.Width;
sourceHeight = sourceRectangle.Height;
} else {
sourceWidth = texture.Width;
sourceHeight = texture.Height;
}
this.InternalDraw(
texture, position.X, position.Y, sourceWidth, sourceHeight,
sourceX, sourceY, sourceWidth, sourceHeight,
color, rotation,
origin.X, origin.Y,
scale, scale,
effects, layerDepth
);
}
This function is pretty simple, and the type information should basically never change. sourceRectangle is always either null or an instance of one specific class, texture is always an instance of one specific class, etc. The function isn't doing any arithmetic or calling complex functions, it's basically just a wrapper around InternalDraw.
Normally I would expect this function to entirely get optimized out, or at least get reduced down to some really simple code. That appears to be what happens in SpiderMonkey.
So, for stuff like this, what steps should I take to understand why a function is being recompiled lots of times, and how can I work around this? If I manage to get the hydrogen IR dumped, can I look for particular warning signs in the IR for these functions? Are there some other debug flags I can pass to the runtime to get diagnostic information here?
If you want to test the game yourself with flags set, it's at
http://www.playescapegoat.com/. Just playing one or two stages should be enough to generate lots of those messages.
Possibly related: In the logs, sometimes I see it reoptimize the same function like ten times back to back. Is this right? It seems like it shouldn't happen.
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 1.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[optimizing: DrawScaleF / 2c69a039 - took 0.000, 0.000, 0.000 ms]
[marking DrawScaleF 0x2c69a038 for recompilation, reason: small function, ICs with typeinfo: 11/11 (100%)]
[disabled optimization for DrawScaleF, reason: optimized too many times]
I've uploaded a log of one run with the trace options set here, if you want to look at it:
http://pastebin.com/7aCyeZC2You can see some of the stuff I describe.
Thanks,
-kg