Any particular reason to prefer setTimeout(T,1) over eval(T) ?
added on the 2012-04-10 16:34:09 by p01

Quote:

p01>cb tells me: "in the rush for the deadline, it's the only way I found to execute the code outside the JS context of the <img> tag. Calculation were too slow if not (even with local variables)". He will dig the thing. Feel free to contact him
added on the 2012-04-11 10:26:04 by wullon

This sounded exactly like the problem I had on Fake Plastic Cubes - the performance was dramatically worse when the demo was run from inside the packer - so I was really excited to hear about a possible workaround (and especially happy that cb found it just as we were 5 minutes away from cancelling the browser 4K compo at Revision :-) ) After a lot of hunting on the web, I found this blog post - in particular, see the comments about the (1,eval) trick, which would seem to be the most concise way to perform an indirect eval.

I don't completely follow the logic of why code optimisers have a problem dealing with indirect eval, or why the local scope cripples performance so much... but boy, does it work. I tried changing the setTimeout(T,1) call in Fabrik to eval(T), and sure enough, it took about three minutes (and about 10 'script unresponsive' messages) for the intro to start in Chrome. I then changed it to (1,eval)(T)... instant fix. I tried the same trick on Fake Plastic Cubes, and it stopped the audio from skipping on my macbook. (The visuals still have that crap jumpy framerate due to the single threading, but... meh.)

I suspect this will also fix the performance issue I was having with JSSpeccy v2, which generates and eval()s an enormous switch statement to use as the Z80 core. Another project I'll have to resurrect, then...

[offtopic]
Gasman saves the compo, again!
I think I never told you how glad, flattered and thankful I was for your amazing bugfixing skills, even if they're only a startup flag. Thank you! :)
[/offtopic]

ps: It's more like "avoid eval() unless you really know what you're doing". It's generally decent advice, since there are plenty of situations in JS where a novice programmer might try to use eval when they really shouldn't (like eval("myObject."+someProperty), and passing strings to setTimeout/setInterval) - but if you are actually generating and executing code at runtime (as a depacker does), and you're aware of the reasons not to do it *inside* performance-critical code, then it's a useful tool.

mog: You're welcome! I can't take any credit for saving this year's compo, though - I was mostly running around and panicking while everyone around me magically got stuff working :-) (I believe the other unsung hero of the compo was Bero, who along with Chaos tracked down the --use-gl=desktop switch to make Laser work on the compo machine...)

The (1,eval) is not only a clever solution for global eval problem, but also it saves two bytes in the unpacker :
"setTimeout(E,1)" which may already be reduced to "setTimeout(E)" (I found it out right after the party!) can be replaced by "(1,eval)(E)".

Code:(1, eval)('...')
(eval, eval)('...')
(1 ? eval : 0)('...')
(__ = eval)('...')
e = eval; e('...') // personal note : as concise as the first one, but less elegant :)
(function(e) { e('...') })(eval)
(function(e) { return e })(eval)('...')
(function() { arguments[0]('...') })(eval)
this.eval('...')
this['eval']('...')
[ eval][0]('...')
eval.call(this, '...')
eval('eval')('...')
Now it does not tell us why indirect eval is faster in our case. I can understand that execution in global scope could be more efficient if we use global variables since the scripting engine does not have to through the context's scope chain to find them (further in the chain, the slower the variable resolution seems to be), but why is the speed so different if we only do mathematic computation with defined variables ? (like during the music generation in Fabrik)

I never thought I'd get a Keanu Reeves 'whoa.' moment from five ASCII characters, butCode:src=#
has just done exactly that.

I don't know if it saves any bytes over having an external PNG, but a) it's more aesthetically pleasing / in tune with traditional-platform 4Ks to have everything packaged as a single file, and b) it gets around Chrome's security restrictions and the need for the --allow-file-access-from-files switch when running it as a local file. Sweet. Is there a js-to-self-extracting-png utility around yet? If not, we need one.

In "Magister" demo the trick seems to add 8 bytes to the total size, because the HTML code is included in a custom PNG chunk (called "jawh" in this demo) which header takes 4+4 bytes. But I think we can reduce the excess to only 4 bytes by using the beginning of HTML code as chunk 4-letters name ("<img").

Just been reading his blog post about the technique, and finding just how deep this rabbit-hole goes... :-) Apparently "<img" doesn't work, even though it should according to the PNG spec. It doesn't say whether he tried "x<im", which would seem to be the next logical step...

FWIW I did not experiment much with the header. I took an existing PNG, fiddled in an HEX editor and got the thing to work in Opera, Chrome, Firefox and Safari.

I don't have a proper HEX editor at the moment to double check but IIRC the chunk in my experiments where 4 bytes biggers than Daeken's . OTOH my HTML+JS bootstrap is 150-160 bytes. So All in all it's 30+ bytes smaller.

There's some semi-working code there for splitting the code onto multiple image rows (because certain browsers can't handle images wider than 10000px or so) but Daeken's bootstrap code doesn't support that as it stands. Care to share yours, p01? :-)

On the subject of compatibility - it turns out that Safari doesn't let you hack the length field of the IDAT chunk, although you can still abuse the checksum of the 'jawh' chunk (and omit the IDAT checksum and IEND chunk entirely), which accounts for the 4 byte difference p01 arrived at. Next job is to squirt the png through PNGOUT before applying the file format hacks - apparently it should do a better job at deflate compression than zlib.

(Oh, and there's another slight disadvantage in comparison to against external .png: your JS code has to clear away the excess document junk when the demo starts...)

Hey guys, figure I should chime in with some code and background. Both Magister and Fl0wer were built with my Windowpane framework, which is available at https://github.com/daeken/windowpane and handles everything from running a local webserver for testing to packing everything into PNGs. Provide the shader and it does the rest.

I've recently been working to get my bootstrap and compression down to the point where things like Fl0wer can be 512b demos rather than 1k. To that end, I've been trying just about everything. I no longer have the jawh chunk, but rather put the bootstrap code after the IDAT (which of course has no checksum -- nobody checks it anyway). I didn't do this in the first place because any less than symbol in the IDAT would cause the HTML parser to break my code, but detecting that and throwing in a beginning greater than symbol is trivial.

As for using another zlib implementation, I used deflate from 7zip and got a ~10b drop in some cases, but that was about the top end. I did some work in making it more compressible, e.g. applying a BWT to the code or applying delta coding (or both, in either order) and every single thing I tried increased the size, no matter what.

I'm pretty confident that the bootstrap can't be reduced any further from what's in the Windowpane repo as it stands; 4 or 5 of us worked for a couple days at it and got absolutely nowhere. However, I have this feeling that there's a way to use the bootstrap code to 'seed' the compression, thus reducing the size of the compressed code greatly. I don't know if it'll pan out, but we'll see -- hopefully I'll get to use that for my submissions to Solskogen!

If anyone has any questions about this stuff, feel free to ask -- most of the code-golf on this has been done in #stackoverflow on Freenode if you want to talk in real time.

Nice work on the new bootstrap -- I may have to pull that into Windowpane.

Btw, I'm currently talking with some other Mozilla people about fixing the bug where images drawn to a canvas come out premultiplied from getImageData. Everyone is in violation of the spec right now, and fixing that will make the bootstrap shorter by not having to loop over the data to get just one element ;)

p01: Tell me if I'm wrong but your bootstrap saves few bytes against Daeken's one because you don't initialize the canvas width. The fact is, it is possible only if your PNG have small width & height, otherwise the browser resizes the PNG to a smaller size and the unpacking becomes impossible. Since PNG compression is far better in Nx1 format than in NxN square (because each PNG row consumes an additional byte), you need a very large PNG, so a canvas width initialization.

In addition, according to my tests, calling getImageData multiple times is n't a problem at all if we read one byte each time, like in Daeken's bootstrap.

Daeken: Thanks. Ouch! about the premultiplied alpha. I didn't realize it forces to read more data to "un-multiply". I hope the read is cached!!!

cb: I didn't know that each row of PNG "cost" one extra byte. As Daeken said, calling getImageData multiple times can be a problem if the original data are premultiplied. Also, the default resolution of a canvas is 300x150. All browsers comply to that. These 300x150 pixels represent ~44Kb worth of data using a single component. I'd recommend to make the PNG 300px wide. OTOH you can make a slow, forward looping, bootstrap that takes a single strip PNG image without having to set the width of the canvas:Quote: