Math.sincos(x)?

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Math.sincos(x)?

Robert Poor
[Preamble: this is my first post to es-discuss -- if this isn't the
right place to suggest language extensions, please let me know.
Thanks. - rdp]

How often have you seen code that calls Math.sin() and Math.cos() with
the same argument, e.g:

   x = r * Math.cos(theta);
   y = r * Math.sin(theta);

?  This trope is repeated for polar coordinates, complex arithmetic, FTTs, etc.

However, most implementations for cos(theta) actually generate
sin(theta) as a "byproduct" (and vice-versa).  Since trigonometric
functions are relatively expensive, and this is a common pattern,
wouldn't it make sense to define a function that returns cos(theta)
and sin(theta) at the same time?  A polyfill might look like this:

   function sincos(theta) {
      return { sin: sin(theta), cos: cos(theta) };
   }

(or whatever the preferred style is for returning multiple values),
but a real implementation would make a single call to the underlying
trigonometric routine.

- rdp
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

Алексей
If you think that performance of cos is too big (did you measure it?) than just calculate cos from sin with the formula sin^2 + con^2 = 1

```js
function sincos (a) {
    const sin = Math.sin(a)
    const cos = (1 - sin**2)**0.5
    return {sin, cos}
}
```

2017-06-22 21:02 GMT+03:00 Robert Poor <[hidden email]>:
[Preamble: this is my first post to es-discuss -- if this isn't the
right place to suggest language extensions, please let me know.
Thanks. - rdp]

How often have you seen code that calls Math.sin() and Math.cos() with
the same argument, e.g:

   x = r * Math.cos(theta);
   y = r * Math.sin(theta);

?  This trope is repeated for polar coordinates, complex arithmetic, FTTs, etc.

However, most implementations for cos(theta) actually generate
sin(theta) as a "byproduct" (and vice-versa).  Since trigonometric
functions are relatively expensive, and this is a common pattern,
wouldn't it make sense to define a function that returns cos(theta)
and sin(theta) at the same time?  A polyfill might look like this:

   function sincos(theta) {
      return { sin: sin(theta), cos: cos(theta) };
   }

(or whatever the preferred style is for returning multiple values),
but a real implementation would make a single call to the underlying
trigonometric routine.

- rdp
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

Boris Zbarsky
In reply to this post by Robert Poor
On 6/22/17 2:02 PM, Robert Poor wrote:
> However, most implementations for cos(theta) actually generate
> sin(theta) as a "byproduct" (and vice-versa).

At first glance, the v8 implementation of cos typically uses a Taylor
series and does not compute sin.

And the SpiderMonkey implementation of cos looks like it just calls the
C standard library cos, so also doesn't compute sin(theta) in a way
visible to it.

-Boris
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

Boris Zbarsky
In reply to this post by Алексей
On 6/22/17 2:13 PM, Алексей wrote:
> function sincos (a) {
>     const sin = Math.sin(a)
>     const cos = (1 - sin**2)**0.5

This will completely fail for a near pi/2.  It will return zero instead
of the correct small but nonzero value it should be returning.

-Boris
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

kdex
Also the sign of the `cos` component is wrong for a bunch of inputs.

On Thursday, June 22, 2017 8:16:21 PM CEST Boris Zbarsky wrote:

> On 6/22/17 2:13 PM, Алексей wrote:
> > function sincos (a) {
> >
> >     const sin = Math.sin(a)
> >     const cos = (1 - sin**2)**0.5
>
> This will completely fail for a near pi/2.  It will return zero instead
> of the correct small but nonzero value it should be returning.
>
> -Boris
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

Nicolas B. Pierron
In reply to this post by Robert Poor
Hi Robert,

On Thu, Jun 22, 2017 at 6:02 PM, Robert Poor <[hidden email]> wrote:

> How often have you seen code that calls Math.sin() and Math.cos() with
> the same argument, e.g:
>
>    x = r * Math.cos(theta);
>    y = r * Math.sin(theta);
>
> ?  This trope is repeated for polar coordinates, complex arithmetic, FTTs, etc.
>
> However, most implementations for cos(theta) actually generate
> sin(theta) as a "byproduct" (and vice-versa).  Since trigonometric
> functions are relatively expensive, and this is a common pattern,
> wouldn't it make sense to define a function that returns cos(theta)
> and sin(theta) at the same time?

For your information, the optimizing compiler of SpiderMonkey already
try to optimize consecutive calls to cos and sin by the sincos
function when possible.
I do not know if other optimizing compilers are doing this
optimization as well, but I would expect that it would be quite easy
to add.

http://searchfox.org/mozilla-central/rev/3291398f10dcbe192fb52e74974b172616c018aa/js/src/jit/Ion.cpp#1310

On Thu, Jun 22, 2017 at 6:02 PM, Robert Poor <[hidden email]> wrote:
>  A polyfill might look like this:
>
>    function sincos(theta) {
>       return { sin: sin(theta), cos: cos(theta) };
>    }

I would expect all engines to use escape analysis to remove this
object allocation, when this function is inlined.

Until this code gets inlined, by allocating an object this code will
add pressure to the nursery of the garbage collector. This will also
add memory writes/reads to/from memory. Both are not desirable when
you are doing math-intensive operations.

--
Nicolas B. Pierron
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

Robert Poor
I previously wrote:

>> most implementations for cos(theta) actually generate sin(theta) as a "byproduct"

As others several have pointed out, that's not really true.  V8, for example, uses a (different) Taylor series approximation for sin and cos.  But given the amount of work that goes into reducing theta to a small interval, it still might make sense.  (See comment about SpiderMonkey below...)

@Nicolas:

> SpiderMonkey already try to optimize consecutive calls to cos and sin

Very cool!  

>  `return { sin: sin(theta), cos: cos(theta) };` will add pressure to the nursery of the garbage collector.

True -- forgive my ignorance, but is there a way to return multiple values that does not cause heap allocation?  That was my intention.

Another possible optimization: cache theta with its interval-reduced equivalent.  If you get a subsequent call with the same theta, you've already done half the work.




On Fri, Jun 23, 2017 at 3:00 AM, Nicolas B. Pierron <[hidden email]> wrote:
Hi Robert,

On Thu, Jun 22, 2017 at 6:02 PM, Robert Poor <[hidden email]> wrote:
> How often have you seen code that calls Math.sin() and Math.cos() with
> the same argument, e.g:
>
>    x = r * Math.cos(theta);
>    y = r * Math.sin(theta);
>
> ?  This trope is repeated for polar coordinates, complex arithmetic, FTTs, etc.
>
> However, most implementations for cos(theta) actually generate
> sin(theta) as a "byproduct" (and vice-versa).  Since trigonometric
> functions are relatively expensive, and this is a common pattern,
> wouldn't it make sense to define a function that returns cos(theta)
> and sin(theta) at the same time?

For your information, the optimizing compiler of SpiderMonkey already
try to optimize consecutive calls to cos and sin by the sincos
function when possible.
I do not know if other optimizing compilers are doing this
optimization as well, but I would expect that it would be quite easy
to add.

http://searchfox.org/mozilla-central/rev/3291398f10dcbe192fb52e74974b172616c018aa/js/src/jit/Ion.cpp#1310

On Thu, Jun 22, 2017 at 6:02 PM, Robert Poor <[hidden email]> wrote:
>  A polyfill might look like this:
>
>    function sincos(theta) {
>       return { sin: sin(theta), cos: cos(theta) };
>    }

I would expect all engines to use escape analysis to remove this
object allocation, when this function is inlined.

Until this code gets inlined, by allocating an object this code will
add pressure to the nursery of the garbage collector. This will also
add memory writes/reads to/from memory. Both are not desirable when
you are doing math-intensive operations.

--
Nicolas B. Pierron


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

Nicolas B. Pierron
On Fri, Jun 23, 2017 at 10:38 AM, Robert Poor <[hidden email]> wrote:
>>  `return { sin: sin(theta), cos: cos(theta) };` will add pressure to the
>> nursery of the garbage collector.
>
> True -- forgive my ignorance, but is there a way to return multiple values
> that does not cause heap allocation?  That was my intention.

The only one I know, is by setting the properties of an object given
as arguments, assuming that the property do exists, and that the
object is reused for multiple calls.

> Another possible optimization: cache theta with its interval-reduced
> equivalent.  If you get a subsequent call with the same theta, you've
> already done half the work.

As far as I know the spec does not provide any mean to specify an
acceptable precision to math operations so having an interval-reduced
equivalent would not be compliant with the spec.

--
Nicolas B. Pierron
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

Florian Bösch
In reply to this post by Robert Poor
On Thu, Jun 22, 2017 at 8:02 PM, Robert Poor <[hidden email]> wrote:
   function sincos(theta) {
      return { sin: sin(theta), cos: cos(theta) };
   }

Allocating, filling, accessing and GC'ing the return object will take more time than calling the underlying C library function which emits the machine code for the processor to consult his trigonometric functions. 

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Math.sincos(x)?

Robert Poor
> Allocating, filling, accessing and GC'ing the return object will take more time than calling the underlying C 

That seems like the nail in the coffin for this suggestion.  Thanks.  I don't have anything to add.


On Fri, Jun 23, 2017 at 9:34 AM, Florian Bösch <[hidden email]> wrote:
On Thu, Jun 22, 2017 at 8:02 PM, Robert Poor <[hidden email]> wrote:
   function sincos(theta) {
      return { sin: sin(theta), cos: cos(theta) };
   }

Allocating, filling, accessing and GC'ing the return object will take more time than calling the underlying C library function which emits the machine code for the processor to consult his trigonometric functions. 


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss