On Mon, Jan 20, 2014 at 4:21 PM, Gustavs Tēbergs <fixplzsecr...@gmail.com>wrote:

> Following up with a benchmark.
>
> (I thought of this idea while working on a parser generator library,
> but unfortunately for my argument the library turned out really
> fast...)
>
> I decided to write a converter for Asm.js code to see roughly what
> happens when building code expression by expression, and it gives me
> existing programs to test with.
>
> https://gist.github.com/fixplz/8529003
>
> Results:
>
> * I took Asm.js code from https://github.com/kripken/ammo.js as a testing
> source
> * Tested with Node 0.10.17 Win7 64
> * The JS engine parses the source (1.6 MB) in 180 ms
> * The opcode reader traverses the corresponting opcode binary (1.8 MB)
> in 120 ms (the binary could be some 30% smaller by adding special case
> codes)
> * With conversion enabled the opcode reader takes 260 ms to convert back
> to JS
>
> The output concatenation is very fast. But it could be better.
>

Your `load` routine is not how I described and is inducing a ton of
cache-busting heap traffic. You need to put *all* strings in a single,
constant array and append references to those constant strings to a single
growing array. There should be no concatenations except for the final
.join(). I.e. something like:

var STRINGPOOL = ['do{', 'function', '(', ')', ...];
var KW_DO = 0;
var KW_FUNCTION = 1;
var LPAREN = 2;
var RPAREN = 3;
...
var outArr = [];

and then in order to "concatenate" a string to the output do something like:

outArr.push(STRINGPOOL[KW_DO]);

and then finally, after outArr contains the entire output, do:

return outArr.join('');

Really, what you need to do is to run a slight modification of your current
`load` routine *during compilation* and trace all strings that are
appended. Then uniquify all the needed strings and add them to a string
pool. Then you send down the wire:
1. The string pool.
2. A binary blob of indices into that string pool (or other "opcodes").

Then your "decoder" can be as simple as:
var stringPool = getStringPool();
var indices = getIndices();
var outArr = [];
for (var i = 0, e = indices.length; i !== e; ++i) {
    outArr.push(stringPool[indices[i]]);
}
return outArr.join('');

Really, this is a coding problem, and you just need to develop an efficient
codec for describing a string of JS. The approach in the Gist you linked is
not a very efficient codec. The codec used in the above simple loop is
probably not the most efficient, but if you add a couple special case
opcodes and/or use a cheap varint encoding for the indices (and
appropriately optimize the order of the string pool array to make good use
of it), then you can really get fast and small.

-- Sean Silva


>
> The conversion forgoes minimizing parentheses so the output is larger
> than the source (2.5 MB), so the total time of using a converter like
> this would be 260 ms to convert + 300 ms to parse.
>
> That means about 80% of the running time is taken up by conversion to
> JS here. If the conversion involved something more complex, like
> reading Ruby code, the proportion might decrease.
>
> I think my point stands - this performance is undesirable. It might be
> difficult to deploy web apps with dependencies on large foreign
> codebases, especially on mobile devices which likely take much longer
> to perform all of this.
>
> This is combined with the fact that use of Function() seems to be
> discouraged by the CSP proposal.
>
>
> On the other hand - I learned a bit about execution semantics in V8
> and found it effectively does what I wanted, only a little indirectly
> by building a string of JS syntax instead of IR. (So the problem
> severity in my mind is downgraded from mysterious action to
> "Function() kinda sucks".)
> _______________________________________________
> es-discuss mailing list
> es-discuss@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to