# Peculiar timing result with Blend function

First, we load some colorbar data and define val for testing

Get["https://pastebin.com/raw/gN4wGqxe"]
vals = N@Subdivide[200000];


then, definition

ClearAll[ParulaCM, ParulaCM2];
ParulaCM =
With[{colorlist = RGBColor @@@ parulaColors}, Blend[colorlist, #] &];
ParulaCM2 =
With[{colorlist = RGBColor @@@ parulaColors}, Blend[colorlist, #] &];


You may notice that ParulaCM and ParulaCM2 is exactly the same. I do it on purpose.

Now timing it

ParulaCM /@ vals; // AbsoluteTiming
ParulaCM2 /@ vals; // AbsoluteTiming


with a fresh start you may probably got this

{0.400419, Null}
{1.03218, Null}


This is already odd, because ParulaCM is the same as ParulaCM2. However, ParulaCM2 is slower

Now you run the previous definition a second time, and time it again. This time you probably got

ParulaCM /@ vals; // AbsoluteTiming
ParulaCM2 /@ vals; // AbsoluteTiming
(*{1.12729, Null}
{1.07233, Null}*)


Yeah, they are same again!!

Now even more peculiar, now you run this

Wow, the ParulaCM is fast again.

I am totally lost. What kind of issue is this?

• On TIO, whichever being called first is faster. But the slowdown of the other function is indeed weird. – user202729 Jan 28 '18 at 8:32
• Have you tried timing with RepeatedTiming? – Henrik Schumacher Feb 2 '18 at 10:40
• @HenrikSchumacher Nope, but I think we don't need RepeatedTiming..Because, the timing difference is quite clear. – matheorem Feb 2 '18 at 10:57
• Hm. In the meantime, I had the opportunity to try it myself. You are right. That's really weird... – Henrik Schumacher Feb 2 '18 at 11:21
• @user202729 I've never seed TIO before. Does TIO actually use the Wolfram kernel? Or is it just a 3rd party emulator? – QuantumDot Feb 2 '18 at 16:56

Just speculating here, as this isn't my area.

This is some kind of caching issue. Blend seems to be caching based on the first argument, and then the second time you feed the same expression it's not using the cache and for some reason not able to make another cache. So in the slower timing, you don't get the benefit of the cache.

If I just use a second list, it doesn't get around the issue,

colors2 = parulaColors;

ClearAll[ParulaCM, ParulaCM2];
ParulaCM =
With[{colorlist = RGBColor @@@ parulaColors}, Blend[colorlist, #] &];
ParulaCM2 =
With[{colorlist = RGBColor @@@ colors2}, Blend[colorlist, #] &];

ParulaCM /@ vals; // AbsoluteTiming
ParulaCM2 /@ vals; // AbsoluteTiming
(* {0.137829, Null} *)
(* {0.461343, Null} *)


But if I 'roundtrip' the expression through a string and back, it creates a big enough difference that a new cache can be formed

colors2 = parulaColors // ToString // ToExpression;

ClearAll[ParulaCM, ParulaCM2];
ParulaCM =
With[{colorlist = RGBColor @@@ parulaColors}, Blend[colorlist, #] &];
ParulaCM2 =
With[{colorlist = RGBColor @@@ colors2}, Blend[colorlist, #] &];

ParulaCM /@ vals; // AbsoluteTiming
ParulaCM2 /@ vals; // AbsoluteTiming
(* {0.136792, Null} *)
(* {0.130454, Null} *)


This is because the // ToString // ToExpression acts on the numeric array to render it not SameQ with the original list. Other round-tripping methods don't work if the result is SameQ with the input.

• Hi, Jason B. So you mean that Blend will check whether it already met the exactly same first argument before? But why add such a check? Is it necessary? – matheorem Feb 3 '18 at 2:14
• Caching is done for time saving, but this is definitely a bug. The caching it's doing is in c code so I don't know a way to disable it. – Jason B. Feb 3 '18 at 13:30
• actually, I still don't understand. Because ParulaCM===ParulaCM2 gives true. The fullform are the same. Why there is a difference in caching. What is caching mechanism in the evaluation sequence? Is there any doc about this? – matheorem Feb 4 '18 at 1:35
• There wouldn't be docs about this, it's deep in the kernel code. ParulaCM and ParulaCM2 are SameQ because they have the exact same underlying structure. But at the kernel level they are pointers to different addresses, different but possibly duplicate data structures. If this caching were working properly, all you'd notice is that when you use a previously constructed Blend function, it doesn't have to make a new interpolation function every time, because it already made one and cached it. – Jason B. Feb 4 '18 at 3:09