tc39 / ecmascript_simd

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

Value semantics for SIMD types #157

Closed littledan closed 9 years ago

littledan commented 9 years ago

In the current JS polyfill, as well as (AFAICT) the Mozilla implementation, SIMD values are objects. ==/=== works on object identity: if you make two SIMD objects with the same values in each lane, they are not equal (either way, and Object.is is also false). However, they are equal to themselves. Equality based on object identity is difficult to preserve in the presence of compiler optimizations, which may want to duplicate or de-duplicate SIMD values in the course of applying algebraic identities.

I’d suggest that we make SIMD values primitives, in a way which could eventually generalize into value types. Like primitives, equality is structural, in this case recursive on elements. Also like primitives, they have wrapper objects (so they can implement ToObject). The wrapper objects have prototypes, which allows methods to be implemented on SIMD values the same way as they are on strings.

For example, take SIMD.Float32x4. (Capitalizing the constructor would make it analogous to Number and String). To make it analogous to Number, it could be invoked in two ways:

Here are some things you might want to do on float32x4 values, or other SIMD types:

johnmccutchan commented 9 years ago

@sunfishcode @PeterJensen

PeterJensen commented 9 years ago

We didn't intend to support the constructor invocation (with new) for any of the SIMD types. Would it be useful? Having SIMD values wrapped in object wrappers will incur a perf penalty that might not be obvious to the programmer. The main purpose of the SIMD operations are to extract performance on platforms supporting SIMD instruction sets. I can see the language purity aspect of allowing a constructor invocation as well, though, but does it add real value, or just complication?

sunfishcode commented 9 years ago

I don't know what to say about most of these questions yet. I can confirm though that .x_ isn't meant to be surfaced in the spec. And the .x in Mozilla's implementation is there because it had been in the spec until very recently. The spec was recently changed to remove the .x etc. accessors and replace them with extractLane functions, and we haven't made that change in our implementation yet.

littledan commented 9 years ago

PeterJensen, my idea behind this was, typically you just work with the primitives, not the wrapped objects which take the extra allocation. For these, there are no additional allocations, and their equality semantics give the compiler more power to do optimizations without making the manipulations observable to the program (== has to be well-defined, but identity-based equality as Mozilla seems to do now restricts the compiler excessively). The wrappers are only used in particular cases: Currently, all Javascript values define a meaning for being coerced to objects, and this coercion can be useful for a number of purposes, including being able to call methods on them. Some implementations may be able to unbox the coercions, but anyway, wrappers probably won't come up within asm.js--it's just that, if we make a new thing available in the language, we have to define semantics for it all the time. I was trying to make the semantics as closely analogous to existing objects as possible.

sunfishcode, I'm just curious, what motivated the removal of the .x accessors? Certainly they could be removed from the above as well. The wrappers would still probably need to exist even if we didn't do anything like that with them, though.

BrendanEich commented 9 years ago

ES7/2016 value objects (see https://github.com/nikomatsakis/typed-objects-explainer/blob/master/valuetypes.md) need some work but should provide the basis for value-not-reference identity semantics we all seek (I think we all do -- check me, everyone). Cc'ing @nikomatsakis and @kg.

/be

kg commented 9 years ago

I can't honestly think of a reason to want to store SIMD vectors by themselves on the heap in a boxed form. They'd probably be a property of some larger object that is already on the heap.

The value objects proposal from the typed objects explainer is one that I'm in favor of, but SIMD types raise the question of how structural equivalence would work in such a world: Do all value objects with four float-sized values (int x 4, float x 4, int x 2 + float x 2, etc) get promoted to single-register SIMD values? Do they all get blessed SIMD behavior?

BrendanEich commented 9 years ago

Value types have operators. Dyadic operators would not be provided via double dispatch methods, rather (details to be worked out separately), assume some dispatch machinery that supports extensibility with "monotonicity", so new types cannot hork existing types' operators, only specialize further, e.g. complex (two doubles) * int64.

Each value type induces its own typeof result (may be symbol not string but I'm still in favor of string only codomain for typeof). There's an old two-way relation in JS:

typeof x == typeof y && x == y <=> x === y

which we'd like to preserve for all value types. This says that === will evaluate to false for two value types not having the same typeof result. So to answer your question, an Int32x4 and a Float32x4 that held the same bits would be !==.

The == operator (not !=, it would be derived algebraically to preserve identities; same for !) would be type-provided, unlike === which would be structural-within-same-typeof-result. So user-defined value types could go nuts with == sloppiness, but I don't recommend it (experience speaking here :-P).

/be

littledan commented 9 years ago

Yes, I agree that good behavior for typeof, == and === is really important for getting this all together. While I think overloading + and friends on SIMD would be nice, for the purposes of getting SIMD.js in ES7 with reasonable semantics, I think we can leave it for later. Probably asm code would want to use functions like SIMD.float32x4.add rather than + to make things easier to infer anyway, and overloading + is more for pure Javascript code.

The typed objects explainer looks good to me, but I think it will still be some work to get in in spec'd form. I'm interested in working these out. I think the remaining issues generally have to do with how types work when transmitted across realms and postMessage, which SIMD types aren't subject to because they are intrinsic.

What if we added SIMD types as primitives something like what I suggested above, and we do it with an eye towards sticking to what might eventually generalize to value types? If we do it right, then when value types are added, SIMD types can be explained as value types that were built-in. Unless we want value types to be objects (which I think would make things really hard on implementations), the remaining open questions for value types might not affect the intrinsic SIMD types.

littledan commented 9 years ago

kg, To clarify re your comment about boxing SIMD values on the heap: Probably SIMD primitives already have to be boxed most of the time, with unboxing only occurring sometimes, when possible (e.g, in asm code, or in optimized normal Javascript code). This is similar to numbers in many JS implementations (unless they are doing NaN-coding and making everything a double). The wrapper types would box that again. But still, they wouldn't come up so often, and effective use of SIMD would typically be completely unboxed.

BrendanEich commented 9 years ago

@littledan, Ecma TC39 is not going to add more ad-hoc primitives to JS. We need a principled approach, which is what we have in the form of the value types/objects proposal under development. If memory serves, the proposal addresses cross-realm and postMessage issues, which definitely will arise with SIMD types. You can't avoid crossing these streams ;-).

/be

kg commented 9 years ago

Typed objects & value types also address a much wider set of scenarios than SIMD types do, so I'd argue that they shouldn't be an afterthought and should, if anything, encompass SIMD work. (Naturally, the reality is that people want SIMD now, so they shouldn't be denied it if there's a way to get it to them sooner...)

SIMD types are a way to improve performance of specific workloads considerably, but a good typed objects implementation can considerably improve performance & heap usage for a vast subset of all production JS applications. Value types can improve that further and enable more expressive, more robust code. SIMD nestles comfortably into a world with typed objects by layering additional gains on top.

littledan commented 9 years ago

I have a very early draft proposal for how SIMD value semantics could work here: http://littledan.github.io/simd.html . While this describes SIMD types as primitives, I hope it can be integrated into a larger value types proposal. I'd appreciate any feedback here. Thanks.

johnmccutchan commented 9 years ago

@littledan What do you think about moving that document to this repository?

BrendanEich commented 9 years ago

@littledan: thanks for doing that, but since we (TC39) won't hardcode new value types in ECMA-262, I have a suggestion: how about I fork your repo and work it into a value-objects form that can land in the JS spec? I'll do that ASAP since int64/uint64 want it too. WDYT?

A minor issue you raise, we can get into it more elsewhere (issue on new repo): falsy testing of all-zeroes SIMD types seems better to me, all else equal. Just throwing this out in case anyone has a killer argument against.

/be

littledan commented 9 years ago

@BrendanEich Yes, a fork like that with a full value types proposal sounds great. Be aware, however, that my SIMD proposal is still very much in flux. Based on implementer feedback, I'm working on changing the internal spec representation from a Data Block to a list of Numbers (to make it more explicit where NaNs need to be dealt with), and of course there are many more operations and types to specify. So there will be merges ahead.

BrendanEich commented 9 years ago

@littledan: no worries, I'll send PRs for anything primal that seems important to get upstream. Or could just send PRs, I don't care which repo is canonical so much as that we have something that isn't a dead end with respect to the JS standard.

Thanks again for taking this on.

/be

PeterJensen commented 9 years ago

@littledan @BrendanEich Really appreciate you chipping in. The SIMD value object story has been a bit of hand-waving up till now. We originally intended to tie SIMD values to Niko's generic value object proposal, but it hasn't happened. The various prototype implementations have all introduced hard-coded SIMD primitive/value types. For proof of concept that has served us well, though.

littledan commented 9 years ago

This has been in the spec for a long time now and multiple implementations are in progress.