Parallelizable JavaScript Subset
30 April 2013
I want to look at an interesting topic: what subset of JavaScript do we intend to support for parallel execution, and how long will it take to get that working? As my dear and loyal readers already know, our current engine supports a simple subset of JavaScript but we will want to expand it and make the result more predictable.
From my point of view, the subset below includes basically all the JavaScript syntax that I ever use. There are two primary limitations that I think people will encounter in practice:
- The fact that mutating shared state boots you out of parallel mode. This restriction is (I think) easy to understand, but it will often take some restructuring to obey it.
- The fact that strings and many native objects (regular expressions, DOM objects, etc) are currently not supported. I expect we’ll improve the support for strings in the near term and also for some native objects, but for others—notably DOM—it will be very challenging to make things work in parallel, due to the many complex implementation details.
OK, let’s get to the details. In the list that follows, we often refer
to a plain old JavaScript object (POJO), which means an object
defined in JavaScript, not a built-in object. It should be created
either with a literal ({...}
, [...]
) or a new C
expression where
C
is a user-defined function. I’ve also written bug for cases
where the current implementation is more limited than it should be.
a
: Variable access- You should be able to access any variable in scope, so long as you
do not use
with
oreval
. I think this all works fine today, though there may be errors in the implementation today relating to infrequently used access patterns, such as"use strict"
.
- You should be able to access any variable in scope, so long as you
do not use
a.b
: Property access- If
a
is a POJO andb
is a data property- no getters (should work someday)
- If
a[e]
: Element access- If
a
is a POJO or a TypedArray
- If
a + b
,a - b
, etc: Binary operators- for any primitive values (someday we should be able to support all objects)
- bug: today, only works if
a
andb
are both numbers or bools (in any combination)
a === b
,a !== b
: Strict equality and unequality- always works
a == b
,a > b
, etc: Loose relational operators- works if
a
andb
are both numbers or bools (in any combination)
- works if
a[i] = b
: Numeric property assignment- if
a
is a POJO or TypedArray owned by current task, andi <= a.length
(no holes—not yet, anyway) - bug: today, we must successfully predict that
a
will be an array or typed array. In practice, this preciction is reasonably successful, but problems arise when the same function is called many times from different contexts.
- if
a.e = b
ora["e"] = b
: String property assignment- if
a
is a POJO owned by the current task - bug: today, we must successfully predict the offset of
e
withina
. In practice, this prediction often fails, as the code must be written very, very carefully for it to work.
- if
{...}
,[...]
,new C(...)
: object literals and creation- for
new C(...)
, C must be a JavaScript function
- for
f()
anda.m(...)
: Function and method calls- If the function being called is a user-implemented function, or
one of the functions in the following list:
- higher-order functions like
map
,reduce
, etc - parallel higher-order functions like
pmap
,preduce
, etc Array.push
(presuming the receiver is writable)- bug: today only
a[a.length] = e
works, nota.push(e)
- bug: today only
Math.*
- bug: today, most but not all
Math
functions work, and only if we predict the function that will be called and are able to inline it. In practice, this prediction is almost always successful.
- bug: today, most but not all
- (more to come)
- higher-order functions like
- If the function being called is a user-implemented function, or
one of the functions in the following list:
function(a, b) { ... }
or(a, b) => { ... }
: closure creation- this should basically always work, I think
- bug: the
=>
syntax doesn’t work yet in parallel execution, I don’t believe
if
,while
, etc- works fine
One caveat I should point out in the current implementation: even if you stick to the above subset, it is possible that some parallel iterations will abort, generally because of mispredicted types or other transient errors. But I consider a parallel abort to be ok if the engine will eventually stabilize and all subsequent runs will be successful. This is the same as what happens with JIT engines, which often generate code that is later invalidated and recompiled.