I can't reproduce claimed speedup on "11.0.1 for Linux x86 (64-bit) (September 21, 2016)"
.
In my tests, custom function wrappers without memoization (as suggested by Szabolcs) consistently add overhead of about 1 µs, functions with memoization add 2-3 µs overhead, compared to built-ins. This overhead is measurable only for small lists, for larger lists it's completely negligible.
Important thing is that results of AbsoluteTiming
are very volatile with median deviation from minimal value, for larger lists, from few to ten percent. I'm sure there are better ways to measure this volatility, I used median deviation just to have any estimate.
Code used for timings:
$HistoryLength = 0;
minDev // ClearAll
minDev // Attributes = HoldFirst;
minDev[expr_, n_Integer?Positive] := Module[{res, min},
res = Table[expr, n];
min = Min@res;
{min, Median[res - min]}
]
testBuiltin@data_ := (
ClearSystemCache[];
Pick[data, Unitize@data, 1] // AbsoluteTiming // First
)
testCustom@data_ := (
ClearSystemCache[];
ClearAll[unitize, pick];
unitize[x_] := Unitize[x];
pick[xs_, sel_, patt_] := Pick[xs, sel, patt];
pick[data, unitize@data, 1] // AbsoluteTiming // First
)
testMemo@data_ := (
ClearSystemCache[];
ClearAll[unitize, pick];
unitize[x_] := unitize[x] = Unitize[x];
pick[xs_, sel_, patt_] := pick[xs] = Pick[xs, sel, patt];
pick[data, unitize@data, 1] // AbsoluteTiming // First
)
testAll[k_, n_] :=
With[{data = (SeedRandom[1]; RandomChoice[Range[0, 10], k])},
minDev[#@data, n] & /@ {testMemo, testCustom, testBuiltin}
]
format = TableForm@Map[
NumberForm[#, ExponentFunction -> (Null &)] &,
SetAccuracy[#, Min[Accuracy@SetPrecision[Min@#, 2], 7]],
{-1}
] &;
First argument of testAll
is size of used data
, second is number of repeated timings. First column of result is minimal absolute timing, second is median deviation from this minimal value. First rows are results for memoized custom pick
and unitize
, second rows are results for non-memoized custom functions, third rows are for built-in Pick
and Unitize
.
testAll[10^1, 10^5]//format
(* 0.000004 0.*10^(-7)
0.000002 0.000001
0.000001 0.000001 *)
testAll[10^2, 10^5]//format
(* 0.000005 0.000001
0.000003 0.000001
0.000002 0.000001 *)
testAll[10^3, 10^5]//format
(* 0.000015 0.000001
0.000014 0.*10^(-7)
0.000013 0.000001 *)
testAll[10^4, 10^5]//format
(* 0.000124 0.000002
0.000122 0.000002
0.000121 0.000001 *)
testAll[10^5, 10^4]//format
(* 0.001297 0.000093
0.001296 0.000069
0.001295 0.000103 *)
testAll[10^6, 10^3]//format
(* 0.0201 0.0014
0.0201 0.0011
0.0201 0.0012 *)
testAll[10^7, 10^2]//format
(* 0.2004 0.0148
0.2003 0.0099
0.2004 0.0088 *)
testAll[5 10^7, 2 10^1]//format
(* 0.972 0.021
0.974 0.017
0.973 0.022 *)
Fresh kernel
To make sure that we're not using any Mathematica's internal cache, that might not be cleared by ClearSystemCache
, we can launch
separate kernel for each test using:
freshKernelEvaluate // ClearAll
freshKernelEvaluate // Attributes = HoldAll;
freshKernelEvaluate@expr_ := Module[{link, result},
link = LinkLaunch[First@$CommandLine <> " -mathlink -noprompt"];
LinkWrite[link, Unevaluated@EvaluatePacket@expr];
result = LinkRead@link;
LinkClose@link;
Replace[result, ReturnPacket@x_ :> x]
]
Timings of built-ins:
resBuiltin = Table[
freshKernelEvaluate[
SeedRandom@1;
data = RandomChoice[Range[0, 10], 5 10^7];
Pick[data, Unitize@data, 1] // AbsoluteTiming // First
],
100
]
{1.28392, 1.23527, 1.25863, 1.23625, 1.33601, 1.24361, 1.26809,
1.23502, 1.34473, 1.23813, 1.24654, 1.23617, 1.27127, 1.25661,
1.22674, 1.58978, 1.26939, 1.37024, 1.24581, 1.54075, 1.23516,
1.23805, 1.3053, 1.40044, 1.42726, 1.39822, 1.46109, 1.27038,
1.39617, 1.2588, 1.29047, 1.23082, 1.25069, 1.34985, 1.27281,
1.24016, 1.2642, 1.2511, 1.23745, 1.27978, 1.24066, 1.38282, 1.32234,
1.30623, 1.26118, 1.58021, 1.27522, 1.24706, 1.27051, 1.2493,
1.24819, 1.28184, 1.46254, 1.24269, 1.26356, 1.24011, 1.35468,
1.27491, 1.35288, 1.24462, 1.27119, 1.26811, 1.23685, 1.33249,
1.23138, 1.29139, 1.23725, 1.28638, 1.23906, 1.27579, 1.3872,
1.31602, 1.29556, 1.26464, 1.27076, 1.24602, 1.25735, 1.24667,
1.27297, 1.23757, 1.34311, 1.26616, 1.35083, 1.24861, 1.23788,
1.25357, 1.24262, 1.28117, 1.25753, 1.28231, 1.23406, 1.27971,
1.22885, 1.27199, 1.24191, 1.23346, 1.26387, 1.24803, 1.27653,
1.23953}
Timings of memoized custom functions:
resMemo = Table[
freshKernelEvaluate[
SeedRandom@1;
data = RandomChoice[Range[0, 10], 5 10^7];
unitize[x_] := unitize[x] = Unitize[x];
pick[xs_, sel_, patt_] := pick[xs] = Pick[xs, sel, patt];
pick[data, unitize@data, 1] // AbsoluteTiming // First
],
100
]
{1.35284, 1.23307, 1.27167, 1.23678, 1.27437, 1.25009, 1.27847,
1.2418, 1.23227, 1.39655, 1.26371, 1.26179, 1.27424, 1.27965, 1.236,
1.28489, 1.25988, 1.26318, 1.24007, 1.24381, 1.2672, 1.25462,
1.26703, 1.24123, 1.28868, 1.24192, 1.27177, 1.23488, 1.23468,
1.27525, 1.26571, 1.27287, 1.23757, 1.26981, 1.25737, 1.2729,
1.23705, 1.24429, 1.26927, 1.23292, 1.28266, 1.23352, 1.28423,
1.23743, 1.26883, 1.23515, 1.27272, 1.25892, 1.23213, 1.23746,
1.3435, 1.27545, 1.23472, 1.49113, 1.42916, 1.56421, 1.5238, 1.37695,
1.27734, 1.23146, 1.2388, 1.24054, 1.27661, 1.23467, 1.43818,
1.51605, 1.28172, 1.24674, 1.34043, 1.36447, 1.28034, 1.23788,
1.3027, 1.25299, 1.26136, 1.24514, 1.23405, 1.26157, 1.24994,
1.27737, 1.23637, 1.26785, 1.411, 1.24163, 1.2301, 1.29223, 1.25492,
1.25177, 1.26862, 1.25825, 1.23715, 1.25327, 1.2694, 1.6624, 1.24317,
1.26682, 1.27915, 1.25705, 1.23258, 1.25804}
I don't see any consistent difference, distribution of results seems similar:
res = <|"Built-in" -> resBuiltin, "Memo" -> resMemo|>;
ListPlot[res, PlotRange -> All]
Histogram[res, PlotRange -> All, ChartStyle -> {Blue, Orange}, ChartLegends -> Automatic]

Edit
UnchartedWorks writes in a comment that unitize
is enough to show difference in speed and pick
is not necessary. Carl Woll points out that data
matrix should be used instead of data
vector.
After changing above I see a difference between memoized and built-in version. Memoized function is consistently slower than built-in.
Used test functions:
testBuiltin = freshKernelEvaluate[
$HistoryLength = 0;
SeedRandom@1;
data = RandomChoice[Range[0, 10], {#, 3}];
Pick[data, Unitize@data[[All, -1]], 1] // AbsoluteTiming // First
] &;
testCustom = freshKernelEvaluate[
$HistoryLength = 0;
SeedRandom@1;
data = RandomChoice[Range[0, 10], {#, 3}];
unitize[x_] := Unitize[x];
Pick[data, unitize@data[[All, -1]], 1] // AbsoluteTiming // First
] &;
testMemo = freshKernelEvaluate[
$HistoryLength = 0;
SeedRandom@1;
data = RandomChoice[Range[0, 10], {#, 3}];
unitize[x_] := unitize[x] = Unitize[x];
Pick[data, unitize@data[[All, -1]], 1] // AbsoluteTiming // First
] &;
Timings:
SetDirectory@NotebookDirectory[];
s = OpenWrite@"results.dat";
k = 3 10^7;
Do[
Write[s,
If[OddQ@i,
{testBuiltin@k, testCustom@k, testMemo@k},
Reverse@{testMemo@k, testCustom@k, testBuiltin@k}
]
],
{i, 10^2}
] // AbsoluteTiming
file = Close@s;
(* {898.653, Null} *)
Result analysis:
results = AssociationThread[{"Built-in", "Custom", "Memo"} -> Transpose@ReadList@file]
colors = {Blue, Orange, Darker@Green};
ListPlot[results, PlotRange -> All, PlotStyle -> colors]
Histogram[results, PlotRange -> All, ChartStyle -> colors, ChartLegends -> Automatic]
<|"Built-in" -> {1.22985, 1.22461, 1.23061, 1.23184, 1.2402, 1.22937,
1.25221, 1.21342, 1.23612, 1.22765, 1.23061, 1.23409, 1.25464,
1.21786, 1.23144, 1.24461, 1.24803, 1.24498, 1.24818, 1.23294,
1.2348, 1.51256, 1.51016, 1.46498, 1.48277, 1.49113, 1.38432,
1.23417, 1.23139, 1.23475, 1.23356, 1.22846, 1.23629, 1.25202,
1.23593, 1.24975, 1.22473, 1.23137, 1.2266, 1.25627, 1.21828,
1.2525, 1.23725, 1.24693, 1.24163, 1.23324, 1.28597, 1.23083,
1.22618, 1.23927, 1.22844, 1.23095, 1.21823, 1.23546, 1.23057,
1.22338, 1.22514, 1.23199, 1.23086, 1.21832, 1.22947, 1.22668,
1.2302, 1.24527, 1.23862, 1.48311, 1.48445, 1.47365, 1.24457,
1.25607, 1.26731, 1.22819, 1.23567, 1.23589, 1.27261, 1.22645,
1.22554, 1.23832, 1.22731, 1.2334, 1.25166, 1.26591, 1.22114,
1.24653, 1.22359, 1.22788, 1.22567, 1.25535, 1.23223, 1.24091,
1.24912, 1.23169, 1.23663, 1.23177, 1.2278, 1.55135, 1.4796,
1.49146, 1.49611, 1.23101},
"Custom" -> {1.23652, 1.23587, 1.23412, 1.22896, 1.22707, 1.23646,
1.25783, 1.26341, 1.24158, 1.22581, 1.22999, 1.24083, 1.23376,
1.23851, 1.24782, 1.22384, 1.2431, 1.23661, 1.23801, 1.24318,
1.23982, 1.53433, 1.48343, 1.54463, 1.48097, 1.47601, 1.23676,
1.24323, 1.2311, 1.22642, 1.23351, 1.23296, 1.23254, 1.23407,
1.23169, 1.24395, 1.24042, 1.24769, 1.23167, 1.21756, 1.2301,
1.23421, 1.24282, 1.23704, 1.23525, 1.2351, 1.25029, 1.23524,
1.22839, 1.22839, 1.23667, 1.26583, 1.22544, 1.22955, 1.22292,
1.22819, 1.27443, 1.24958, 1.24789, 1.22195, 1.21883, 1.22279,
1.21813, 1.22052, 1.23921, 1.5044, 1.49484, 1.50915, 1.23095,
1.23694, 1.22373, 1.24806, 1.22945, 1.24085, 1.23373, 1.22282,
1.2362, 1.23099, 1.23932, 1.24258, 1.25047, 1.26868, 1.23042,
1.22579, 1.2229, 1.23243, 1.2368, 1.22925, 1.2387, 1.23014,
1.21772, 1.2259, 1.22549, 1.23208, 1.26501, 1.33781, 1.48822,
1.48658, 1.25979, 1.26228},
"Memo" -> {1.29497, 1.29798, 1.29918, 1.29907, 1.31014, 1.29503,
1.29095, 1.3249, 1.29036, 1.30051, 1.2789, 1.29959, 1.2988, 1.2882,
1.29519, 1.28946, 1.31952, 1.32948, 1.32447, 1.29627, 1.31841,
1.5721, 1.57097, 1.55392, 1.56358, 1.55974, 1.28744, 1.3029,
1.28567, 1.2914, 1.29167, 1.29062, 1.29471, 1.29797, 1.30193,
1.30423, 1.30097, 1.29706, 1.29027, 1.29005, 1.29543, 1.2929,
1.29996, 1.29386, 1.29502, 1.31621, 1.31506, 1.29105, 1.30462,
1.28348, 1.30922, 1.28715, 1.30386, 1.29361, 1.29596, 1.30149,
1.28943, 1.29833, 1.31909, 1.2911, 1.31163, 1.28986, 1.29063,
1.28847, 1.29451, 1.46695, 1.55118, 1.55433, 1.29779, 1.29201,
1.29947, 1.29045, 1.28494, 1.29003, 1.29385, 1.2856, 1.31603,
1.33432, 1.28929, 1.29873, 1.29259, 1.28694, 1.28868, 1.28838,
1.29824, 1.29435, 1.29401, 1.30137, 1.2971, 1.29248, 1.29333,
1.2847, 1.28666, 1.28647, 1.29923, 1.30116, 1.56112, 1.56282,
1.29155, 1.2936}|>

SeedRandom[42]
right after theClear
statement, so that I could repeatedly havePick
act on the same data. I don't witness the speedup from your answer. $\endgroup$