# Performance-tuning a simple 2D Table construct

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.

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

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=DeveloperToPackedArray @ N @ Range[0,2π,π/12]},
Sin @ Outer[Times, pa, pa]
]; //RepeatedTiming//First

With[{pa=DeveloperToPackedArray @ 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

• I like the use of KroneckerProduct. I hadn't thought to use that. Oct 31, 2018 at 22:45
• 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. Oct 31, 2018 at 22:59
• @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... Oct 31, 2018 at 23:02
• @HenrikSchumacher another odd subtle point... Range[0., 2. \[Pi], \[Pi]/250] is much slower than Range[0., 2. \[Pi], \[Pi]/250.] Oct 31, 2018 at 23:06
With[{r = Range[0., 2Pi, Pi/12.] }, Sin @ Outer[Times, r, r]];// RepeatedTiming// First


0.000014