tc39 / ecmascript_simd

SIMD numeric type for EcmaScript
Other
540 stars 71 forks source link

Consider restricting mutability of SIMD object to enable optimizations #203

Closed littledan closed 9 years ago

littledan commented 9 years ago

In the current polyfill and spec text, the SIMD object and the wrappers like Float32x4 are ordinary, unrestricted objects. However, the Mozilla implementation restricts the SIMD object. This is done to make it easier for optimizations to assume that SIMD hasn't changed, so they don't need to do as much checking at runtime. For asm.js, the 'linking' step already does these checks ahead of time, so the main concern here is really optimization for non-asm code. We definitely don't want to prevent extensions, just hold in what's there already.

At the very least, the global SIMD binding needs to be mutable so that the whole thing can be replaced (e.g., for an extreme polyfill, or for making a restricted environment like SES), and objects need to remain extensible (so that future polyfills can add additional functions and types that are designed in the future). The issue is just, should properties like SIMD.Float32x4 and SIMD.Float32x4.add be non-configurable and non-writeable?

Arguments in favor:

Arguments against:

How much power for optimizations does making a property non-configurable and non-writable really give? Is it worth the loss of flexibility? How important is it to eliminate the check in the non-asm case? Do we have benchmarks to present to TC-39 demonstrating the importance (if they push back against this as being different from the existing spec)?

ljharb commented 9 years ago

fwiw, I'm totally fine with one of non-writable or non-configurable, but having both leaves me out of options for a polyfill.

littledan commented 9 years ago

@ljharb Well, without both, you couldn't really do any optimizations with it. The whole point is to disallow overwriting it. Would a polyfill ever need to overwrite an existing function, or would it just need to add new functions?

ljharb commented 9 years ago

Yes - existing functions have bugs virtually constantly, and often even things that are perfectly working to ES5 spec violate the ES6 spec and need overwriting, and the same will continue to be true for future spec editions. The only things that currently are immutable are number constants, and symbols, both of which are primitives.

BrendanEich commented 9 years ago

Cc'ing @bnjbvr @tschneidereit and @jandem. We need to hear from the horse's mouth (if not from@horse_js :-P) whether it's really important to seal or freeze SIMD objects.

/be

nbp commented 9 years ago

Asm.js does not suffer from the mutable properties, as the functions are verified at link-time, but @bnjbvr should know better.

The problem of having mutable object properties is that we would have to type-check the object to ensure that the property is not changed. Thus SIMD.Float32x4.add expression will produce 2 type checks instead of none, before inlining the function. I guess that in the worst case scenario, we might have to do 2 / 3 type checks per loop iteration in case of OSR.

Also, be aware that making wrappers for SIMD operations can be deadly for performances: 1/ Any Asm.js code will have to create SIMD objects to be able to call the functions with them. 2/ The inlining policy of the engine might not inline the wrapper. For example, if the wrapper is not inlined because it contains too much code (or any other heuristics), then the engine will have to create a SIMD object and give it as argument of the function.

jandem commented 9 years ago

The problem of having mutable object properties is that we would have to type-check the object to ensure that the property is not changed. Thus SIMD.Float32x4.add expression will produce 2 type checks instead of none, before inlining the function.

That's not true; SpiderMonkey can optimize configurable/writable properties like this just fine, without any runtime overhead (type constraints will invalidate code when these properties change). We use the same mechanism for things like Math.foo() for instance.

bnjbvr commented 9 years ago

Confirm what's been said for asm.js: we checked at link time that the SIMD object is the VM SIMD object, and that the SIMD functions are the native SIMD functions, otherwise we bail out from asm.js and get back to classic JS.

I think having non-configurable non-writable properties was needed (at least for asm.js) when the SIMD values had all the lanes getters / setters, but now that they're gone, we should be fine with mutable / configurable properties.

abchatra commented 9 years ago

Chakra doesn't do invalidation of jit code yet. So we still have type check overhead if these built-ins are allowed to be configurable. Even if we built the invalidation of jit code, there will be data structure overhead to maintain and track if any of the built-ins are overridden.

Seal and freeze also prevent the extensions. We should allow extensions on these SIMD types, only disable configurability of the SIMD built-ins essentially disallowing overriding of existing SIMD functions.

I am open if someone has a good use case to override existing SIMD functions.

ljharb commented 9 years ago

@abchatra see https://github.com/johnmccutchan/ecmascript_simd/issues/203#issuecomment-110613341 - everything that might ever change in the spec needs to be somehow overrideable, otherwise a shim can't fix it: not just now, but in the unknowable future.

littledan commented 9 years ago

@nbp About the wrappers: The SIMD spec proposal has wrappers, but these are just like the Number, Boolean, String, etc wrappers that other primitive types have. Wrapped primitives are typically not used; they are just intermediate values to enable methods to be called on primitives. Because functions to manipulate SIMD are properties of the constructor, not properties of the prototype, the wrappers shouldn't come up in typical code (unless a user decides to define a bunch of methods on the wrapper prototype).

ljharb commented 9 years ago

@littledan so wait, you're suggesting SIMD values are primitives? If so, what does typeof report as their value? Will Object(simdPrimitive) result in wrappedSimdObject like it does for all the other object-coercible primitives?

littledan commented 9 years ago

@ljharb Yes, SIMD values are primitives. It is important for compilers to be able to freely apply algebraic identities (e.g., unboxing and re-boxing). In JavaScript, we like === to be well-defined, rather than allowing programmers to observe these transformations. Primitives are a useful solution to these constraints, and they are basically what we want for these sorts of values anyway, within a general value types world.

For typeof, see http://littledan.github.io/simd.html#typeof-evaluation for the values for typeof for various SIMD types.

Yes, Object(simdPrimitive) will result in a wrapper. See http://littledan.github.io/simd.html#to-object .

WDYT?

ljharb commented 9 years ago

While that all makes sense, that effectively makes SIMD unpolyfillable, just like Symbols, which is unfortunate.

bterlson commented 9 years ago

Anything depending on Value Types will not be polyfillable either, which I bet will grow to be a large set of things. Although maybe clever type inferring transpiler would be able to do it?

It's a shame, though, that things like bignum and decimal won't be polyfillable :(

littledan commented 9 years ago

Yeah, that's the unfortunate thing--we won't be able to make a complete polyfill for SIMD. But our alternatives are also unpalatable: force ECMAScript implementations to track object identity, or make ==/=== not well-defined (so that users can observe optimizations changing what's equal, as they can in Firefox today if I understand correctly). The former would partially defeat the gains of SIMD (the whole point is performance) and the latter would not be fitting with the broader direction of security and lack of undefined behavior in ECMAScript.

A polyfill could get relatively close by sealing SIMD objects and interning them. Then ==, ===, Map and Set would work. But you wouldn't get typeof, proper wrappers with their identity, and you'd take a big performance hit with the wrappers. A transpiler could get you typeof pretty easily. It'd be a lot of work to transpile wrappers in, but I think it's possible. I don't know if it's worth it for those minor features though. These are really getting at edge cases. Even ==/=== is an edge case--users will typically not compare entire SIMD vectors but instead compare element-wise with, e.g., SIMD.Float32x4.equals().

I don't think people will really want to use a SIMD polyfill most of the time. They'll probably want to fall back to scalar code, since that's significantly faster than the current polyfill (and likely always will be).

littledan commented 9 years ago

But once there are value types, a polyfill can be done in terms of those! (We still couldn't make a Symbol polyfill but at least we could do a SIMD one.)

littledan commented 9 years ago

Discussed with @abchatra by email. It seems we're in agreement that we can proceed with leaving the SIMD object fully mutable, and Edge will consider the SpiderMonkey-style optimization if it seems required.