5
$\begingroup$

I've recently been playing with some performance tuning over a seemingly simple construction where I build a 2D array of Sin[x*y] for {x, y}∈Interval[{0, 2π}, {0, 2π}].

I think it's also a good example of how vastly different results one can get depending on how efficiently the operation is performed.

Here's the operation in the simplest/least efficient way I could think up:

Table[N@Sin[x*y], 
    {x, 0, 2π, π/250},  
    {y, 0, 2π, π/250}
    ]; // RepeatedTiming // First

2.01

Now, how can we speed this up? (or slow it down in non-intuitive ways)

For reference, the best I managed to get was 0.0011 but all answers are good answers, especially if they provide analysis of how they speed up/slow down the problem.

Also feel free to cut down on the number of points 250 was simply most evocative.

$\endgroup$
  • $\begingroup$ BTW, in our culture, 250 can mean the word "fool(ish)". Is it a coincidence? $\endgroup$ – Αλέξανδρος Ζεγγ Oct 31 '18 at 23:32
  • $\begingroup$ @ΑλέξανδροςΖεγγ 1000%. I didn't know that. $\endgroup$ – b3m2a1 Oct 31 '18 at 23:32
  • $\begingroup$ Then what a coincidence! (For "least efficient") $\endgroup$ – Αλέξανδρος Ζεγγ Oct 31 '18 at 23:43
6
$\begingroup$

Use packed arrays, and vector operations. Here are 2 possibilities:

Table[N@Sin[x*y],{x,0,2π,π/12},{y,0,2π,π/12}]; //RepeatedTiming//First

With[{pa=Developer`ToPackedArray @ N @ Range[0,2π,π/12]},
    Sin @ Outer[Times, pa, pa]
]; //RepeatedTiming//First

With[{pa=Developer`ToPackedArray @ N @ Range[0,2π,π/12]},
    Sin @ KroneckerProduct[pa, pa]
]; //RepeatedTiming//First

0.0025

0.0000322

0.0000314

Update

It seems that converting to a packed array takes the most time, so here is a version that avoids packing:

With[{pa = Range[0, 24] N[Pi/12]},
    Sin @ KroneckerProduct[pa, pa]
]; //RepeatedTiming //First

8.3*10^-6

$\endgroup$
  • $\begingroup$ I like the use of KroneckerProduct. I hadn't thought to use that. $\endgroup$ – b3m2a1 Oct 31 '18 at 22:45
  • 2
    $\begingroup$ Range[0., 2 \[Pi], 2 \[Pi]/ 500] or Subdivide[0., 2 Pi, 500] are even faster because they generate packed arrays right from the start. $\endgroup$ – Henrik Schumacher Oct 31 '18 at 22:59
  • $\begingroup$ @HenrikSchumacher oh good thinking with Subdivide. My brain just immediately jumps to Range but there's really no reason for that. It also looks like Subdivide blows Range out of the water here... $\endgroup$ – b3m2a1 Oct 31 '18 at 23:02
  • $\begingroup$ @HenrikSchumacher another odd subtle point... Range[0., 2. \[Pi], \[Pi]/250] is much slower than Range[0., 2. \[Pi], \[Pi]/250.] $\endgroup$ – b3m2a1 Oct 31 '18 at 23:06
5
$\begingroup$
With[{r = Range[0., 2Pi, Pi/12.] }, Sin @ Outer[Times, r, r]];// RepeatedTiming// First

0.000014

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.