Re: Observability of NaN distinctions — is this a concern?

Brendan Eich brendan at mozilla.com
Tue Mar 26 20:03:09 PDT 2013


Old SGI-hacker-me is on board with spec'ing as-is. Thanks,

/be

On Mar 26, 2013, at 7:02 PM, Kenneth Russell <kbr at google.com> wrote:

> Dmitry, thank you for prototyping and benchmarking this. There should
> be no question that a slowdown of 25% is too high a cost to pay.
> 
> Allen's analysis earlier in the thread indicates that no spec changes
> are necessary in order to allow multiple bit patterns to be used when
> storing NaNs into Float32Array and Float64Array. Can this topic be
> laid to rest?
> 
> -Ken
> 
> 
> 
> On Tue, Mar 26, 2013 at 12:14 PM, Dmitry Lomov <dslomov at google.com> wrote:
>> I would like to add some perf numbers to this discussion, if I may.
>> 
>> I have implemented a quick patch for V8 that implements NaN normalization on
>> Float64Array store. V8 does not use NaN boxing, so from implementation
>> perspective NaN normalization is not required by V8.
>> 
>> In my test, the perf penalty for Float64Array store is 25% (on ia32
>> architecture in optimized regime, using sse2 instructions).
>> I am attaching the test and my patch for your perusal (the patch should
>> easily apply to v8's bleeding_edge).
>> 
>> It feels like the performance hit is a very considerable factor here,
>> especially given that typed arrays are all about fast math.
>> 
>> 
>> Results on my machine:
>> 
>> Normalized stores:
>> $ ./out/ia32.release/d8 float-array.js
>> Start
>> 14033 msec
>> Start
>> 14059 msec
>> Start
>> 13983 msec
>> Start
>> 13979 msec
>> Non-normalized stores:
>> $ ./out.baseline/ia32.release/d8 float-array.js
>> Start
>> 11197 msec
>> Start
>> 11207 msec
>> Start
>> 11207 msec
>> Start
>> 11253 msec
>> 
>> Dmitry
>> 
>> 
>> 
>> On Tue, Mar 26, 2013 at 7:55 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>
>> wrote:
>>> 
>>> 
>>> On Mar 26, 2013, at 1:29 AM, Oliver Hunt wrote:
>>> 
>>> 
>>> Ok, I'll try to go over this again, because for whatever reason it doesn't
>>> appear to stick:
>>> 
>>> If you have a double-typed array, and access a member:
>>> typedArray[0]
>>> 
>>> Then in ES it is a double that can be one of these values: +Infinitity,
>>> -Infinity, NaN, or a discrete value representable in IEEE double spec.
>>> There are no signaling NaNs, nor is there any exposure of what the
>>> underlying bit pattern of the NaN is.
>>> 
>>> So the runtime loads this double, and then stores it somewhere, anywhere,
>>> it doesn't matter where, eg.
>>> var tmp = typedArray[0];
>>> 
>>> Now you store it:
>>> typedArray[whatever] = tmp;
>>> 
>>> The specification must allow a bitwise comparison of typedArray[whatever]
>>> to typedArray[0] to return false, as it is not possible for any NaN-boxing
>>> engine to maintain the bit equality that you would otherwise desire, as that
>>> would be trivially exploitable.  When I say security and correctness i mean
>>> it in the "can't be remotely pwned" sense.
>>> 
>>> Given that we can't guarantee that the bit pattern will remain unchanged
>>> the spec should mandate normalizing to the non-signalling NaN.
>>> 
>>> --Oliver
>>> 
>>> 
>>> Oliver,
>>> 
>>> Let's look at actual ES6 spec. language and see if there is actually
>>> anything you would like to see changed.  The encoding of NaNs is only
>>> discussed in three places:
>>> 
>>> Section 8.1.5, first paragraph says:
>>> 
>>> The Number type has exactly 18437736874454810627 (that is, 264253+3)
>>> values, representing the double-precision 64-bit format IEEE 754 values as
>>> specified in the IEEE Standard for Binary Floating-Point Arithmetic, except
>>> that the 9007199254740990 (that is, 2532) distinct “Not-a-Number” values of
>>> the IEEE Standard are represented in ECMAScript as a single special NaN
>>> value. (Note that the NaN value is produced by the program expression NaN.)
>>> In some implementations, external code might be able to detect a difference
>>> between various Not-a-Number values, but such behaviour is
>>> implementation-dependent; to ECMAScript code, all NaN values are
>>> indistinguishable from each other.
>>> 
>>> This is very old text, most of which dates to ES1. It is defining the ES
>>> Number type, which is an abstraction, not an actual bit-level encoding. To
>>> me it say these things:
>>>   1) Unlike the the IEEE 64-bit floating point type.  The ES Number type
>>> has only a single NaN value.
>>>   2) An implementations are free to internally encode the Number type any
>>> why it desires (as long is it observably conforms to all the requirements of
>>> section 8.1.5).
>>>   3) In particular, an implementation  might have multiple internal bit
>>> patterns all of which correspond to the single NaN element of the Number
>>> type.
>>>   4) A corollary of 3) is that implementation are not required to
>>> internally canonicalize NaNs, that is an implementation level design
>>> decision.
>>>   5) Implementations are not required to canonicalize NaNs when they are
>>> passed or otherwise made visible to non-ES code.  Hence such code may be
>>> able to observe details of the NaN encoding, including whether or not a
>>> canonical NaN value is used internally by the ES implementation.
>>> 
>>> Is there anything you think should change in the above specification text?
>>> 
>>> Section 15.13.5.1.3 defines the GetValueFromBuffer abstraction operation
>>> which is currently the only place in the ES6 spec. where a ES Number value
>>> is retrieved from an ArrayBuffer. Step 8 of the specification algorithm is:
>>> 
>>> 8 If type is “Float64” , then
>>> 
>>> rawValue is interpreted, taking into accont the value of isBigEndian,  as
>>> a bit string encoding of an IEEE 754-208 binary64 value.
>>> If rawValue is any an IEEE 754-208 binary64 NaN value, return the NaN
>>> Number value.
>>> Return the Number value that is encoded by rawValue.
>>> 
>>> Step 7 is similar but deals with Float32 values.
>>> 
>>> To me it say these things:
>>>   1) In all cases, an ES Number value (as defined in 8.1.5) is returned.
>>>   2) All IEEE NaN values are logically canonicalized as the single ES NaN
>>> value.
>>>   3) No additional requirements for ES Numbers, beyond those in 8.1.5 are
>>> introduced.  Actual representation remains up to implementation.
>>> 
>>> Should anything change? If 8.1.5 continues to not mandate any particular
>>> or single encoding representation of a NaN, I don't see why this should
>>> either.
>>> 
>>> Section 15.13.5.1.4 defines the SetValueInBuffer abstraction operation
>>> which is currently the only place in the ES6 spec. where a ES Number value
>>> is stored into a ArrayBuffer. Step 8 of the specification algorithm is:
>>> 
>>> 8. Else, if type is “Float64” , then
>>> 
>>> Set rawValue to the 8 bytes that are the IEEE-868-2005 binary64 format
>>> encoding of value. If isBigEndian is true, the bytes are arranged in big
>>> endian order.  Otherwise, the bytes are arranged in little endian order.  If
>>> value is NaN, rawValue is may be set to any implementation choosen
>>> non-signaling NaN encoding.
>>> 
>>> Step 7 is similar but deals with Float32 values.
>>> 
>>> To me it say these things:
>>>   1) When storing into an ArrayBuffer, canonicalization of ES NaN to some
>>> particular IEEE NaN is permitted but not required.
>>>   2) The actual ArrayBuffer bit-level representation of NaN values is
>>> completely implementation dependent.  It need not even be consistent across
>>> multiple stores. .
>>>   3) External observers of such a stored value may be able to detect NaN
>>> encoding differences by observing Numbers stored into ArrayBuffers.  This is
>>> allowed by 8.1.5
>>>   4) The actual encoding of a NaN value in an ArrayBuffer is observable
>>> by ES code by overly a non-float typed Array on an ArrayBuffer where a
>>> Number has been stored using the above steps.
>>> 
>>> Point 4 seems to be in conflict with the 8.1.5 requirement "to ECMAScript
>>> code, all NaN values are indistinguishable from each other.".  However,
>>> consider that what is being observed is not directly an ES Number NaN value
>>> but instead a bit pattern that according to15.13.5.1.4 above may not be a
>>> direct reflection of the Number NaN that was stored. So maybe it slips by.
>>> 
>>> So, again any change requests?
>>> 
>>> From a spec. perspective we could require canonicalization to some
>>> specific IEEE NaN (how do we pick one?) in 15.13.5.4.  If we are going to do
>>> that, we presumably also should require canonicalization of endian-ness
>>> which is also observable and arguably a bigger interop hazard than NaNs.
>>> However, I think the performance arguments for not canonicalizing
>>> endian-ness in typed array are stronger.  If we are going to continue to
>>> let endian-ness be implementation determined then I don't think there is
>>> much point in worrying about changing the the handling of NaNs in
>>> 15.13.5.1.4. But, I'm flexible.
>>> 
>>> Allen
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>> 
>> 
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> 


More information about the es-discuss mailing list