JS performance and global eval() (attn: p01 and cb/adinpsz)

category: code [glöplog]
dont remember the error anymore, but i never used any eval() and i think i only used a with() once, when already optimizing, not sure if that was before or after trying closure. would have to check throwing the latest code to closure again. just found it curious you didnt have any problems with it.

and yeah uglifyjs sucks in some of those things, i ended up refactoring variable and function names by hand before passing it through uglifyjs. :S
added on the 2012-05-23 14:17:21 by psenough psenough
A little explanation of the observed performance difference.

Direct eval uses the scope of the caller (ie the function and it's enclosing scopes) and the variables in those scopes becomes "nonlocal/nonscoped" since to eval that enclosing scope is to be treated as the global object. So first of all, all evaled code needs to make accesses go through a proxy object acting as a global scope that includes the function scopes variables + the real global object. The real global object is probably optimized agains't for fast lookups when posible whereas when using the proxy object it is harder(not impossible though) to optimize for. Also this is just pure Ecmascript "limitation", in addition to this i think that there is some legacy "Javascript" thing that complicates things even more.

An indirect eval however uses the "eval function" and doesn't capture any scopes apart from the global object itself, thus landing the code at the same level as "normal" code. Optimizations with direct object addresses are valid.
added on the 2012-05-23 17:47:40 by whizzter whizzter
most appropriate reply ever! bdsu7890ads just won the thread!!
I love how the FC-UK got censored. :P
added on the 2012-05-24 08:59:03 by ClySuva ClySuva
this thread is awesome, thank a lot people, I learnt tons of things and that make me want to code some js effects again :D
added on the 2012-05-24 09:27:50 by rez rez
I don't think new version of JsExe really give better results with 1k demos, but progress have been made for 4k since version 1.0.1 :
- Fabrik's size drops from 3794b to 3769b,
- "Cyboman5" demo from 3694b to 3671b,
- and "Sway" demo from 4073b to 4043b.

BTW I found two possible ways to write a small bootstrapper for multirows PNG, fun to see that both of them have exactly the same byte length (they seem to have almost the same execution speed too) :

Code:// normal byte order $=V.width=W;C=V.getContext('2d');C.drawImage(this,0,0);for(Y=C.getImageData(_='',X=0,$,H).data;Z=Y[X];X+=4)_+=String.fromCharCode(Z);(1,eval)(_)

Code:// reversed byte order $=(X=V.width=W)*H;C=V.getContext('2d');for(C.drawImage(this,0,_='');$--;_+=String.fromCharCode(C.getImageData($%X,$/X,1,1).data[0]));(1,eval)(_)
added on the 2012-06-17 19:37:40 by cb cb
added on the 2012-06-17 20:52:11 by psenough psenough
cb: what/how did you change in the 1.0.1 version to get better/different compression ratio ? Are you trying multirow versions of the script with various "width" ?

For multirows bootstrap I'd definitely use the default width of 300px:
Code:// normal byte order a=V.getContext('2d');a.drawImage(this,i=e='',0);for(d=a.getImageData(0,0,300,150).data;t=d[i];i+=4)e+=String.fromCharCode(t);(1,eval)(e)

Code:// reverse byte order a=V.getContext('2d');a.drawImage(this,i=e='',0);for(d=a.getImageData(0,0,300,150).data;t=d[i];i+=4)e=String.fromCharCode(t)+e;(1,eval)(e)

Yes I'm assuming a \0x00 EOF character. Also, I've been terminating my scripts with a \0xff. Combined with the EOF, it does defeat any adverse effect of premultiplied alpha.
added on the 2012-06-17 21:59:11 by p01 p01
Or even shorter
Code:// normal byte order a=V.getContext('2d');for(a.drawImage(this,i=e='',0);t=a.getImageData(0,0,300,150 ).data[i];i+=4)e+=String.fromCharCode(t);(1,eval)(e)
Code:// reverse byte order a=V.getContext('2d');for(a.drawImage(this,i=e='',0);t=a.getImageData(0,0,300,150 ).data[i];i+=4)e=String.fromCharCode(t)+e;(1,eval)(e)
added on the 2012-06-17 22:10:02 by p01 p01
For now JsExe uses multirow mode only for JS larger than 32767px, because each PNG row has a cost. A 300x10 picture is always bigger than the corresponding 3000x1 picture.

A PNG with a width of 300px may be a better choice, if the loss due to PNG rows is compensated by the bootstrapper gain, but it's not obvious and I think we would save less than 5 bytes in the best case.

Did you test your code ? I can remember calling getImageData on the whole image at each iteration is very slow.
added on the 2012-06-17 23:47:54 by cb cb
ps : better ratios are mainly due to AdvanceCOMP and DeflOpt tools which improves PNG compression.
added on the 2012-06-17 23:51:09 by cb cb
Loss due to each new row is bigger than what I thought.

With the "flower" demo (1119b), the PNG without bootstrapper takes 730b in 1119x1 format, and 737b in 280x4 format (+7b).

For Fabrik, the 9209x1 picture takes 3599b and the 298x31 picture takes 3672b (+73b !).

It seems we lose approximately 2 bytes per row.
added on the 2012-06-18 00:18:20 by cb cb
Thanks for the info and extra tests.

The first multi-row bootstraps I posted have a single call to getImageData and only 3 bytes bigger ;) Use the last ones, with multiple calls to getImageData, as a very last resort. Or maybe it's only worth it for smaller intros.

Really good job on the PNG optimization.
added on the 2012-06-18 00:32:25 by p01 p01
Cool jpg/canvas hack by Apple: http://news.ycombinator.com/item?id=4531088
added on the 2012-09-18 16:46:35 by wullon wullon
Sublime text's site also feature fake "video" implemented using images/JSON/Canvas

Interesting technically, but in the case of Apple it's also because they can play inline videos on iPhone :)

These "codec" are very primitive. I wonder how Broadway.s and Route9.js would perform on devices.
added on the 2012-09-18 17:17:32 by p01 p01
would be interesting to have video editors with export to json+js functionality :)
added on the 2012-09-18 17:41:52 by psenough psenough
If only we had something like animated PNGs, oh wait - we do!
added on the 2012-09-18 21:46:21 by mog mog

So, I made a little enhancement to my pnginator script for Fake Plastic Cubes: Part 2. As I found this time last year while helping to squeeze Parsley State into 10k, we often want to include binary assets such as music data in a JS intro, and embedding them as part of the ASCII-based JS stream is not ideal. But since PNG compression is working with bytes rather than characters, it makes sense to include your binary data as separate segments alongside the JS text...

This new version of pnginator.rb (included in the FPCp2 source) accepts multiple input files on the command line, and executes the first one as JS as usual, but also exposes an array 'f' containing each file as a Uint8Array object. Using this mechanism, I happily compressed 45Kb of dumped AY chip register data to around 3K :-)

If you want a string instead (for example, in FPCp2 I'm also using those data segments to store shader code, and gl.shaderSource wants a string) you can convert it with an incantation like:
added on the 2013-06-30 01:40:29 by gasman gasman
gasman: awesome!
added on the 2013-06-30 16:45:28 by psenough psenough