Wolfram Language Speed Guide

By Xah Lee. Date: . Last updated: .

Intro

Here's a guide to speed up your WolframLang code.

Things discussed here are sensible only if your data is huge, such as list with thousands or millions items. Or, when you are in a coding competition for speed. Some tips sacrifice code convention and readability for speed.

General Principle

Most important are listed first.

  1. Avoid any procedural programing such as using Do, For, While, and lots assignment and variable updates. Doing them is a magnitude slower than using functional programing code.
  2. Do not repeatedly add/delete element to a List. Because WolframLang List is an array in computer science sense. Everytime list length changes, the list is recreated, and that is slow, especially when there are large number of items. Instead, leave the items there as is, extract element in final step. Or, create a large list in the beginning, and change items to some random symbol (e.g x1234) when you don't need them. Or, use SparseArray or Association
  3. The less number of function calls, the faster. Try to find a WolframLang function that does exactly what you want, instead of using multiple function calls to construct result.
  4. Use of Pattern Matching is in general an order slower than not using pattern. This means, when you define a function, best to define it using Function when you can. However, if you have one or more conditionals such as If, pattern matching is usually faster.
  5. Using Set and SetDelayed, slows down your entire program. 〔see Set, SetDelayed〕 because they are global pattern matching, and they are tried for every evaluation of expressions.
  6. When using Pattern Matching on large expression, try to narrow down parts of expression to match, or a level. e.g. use ReplaceAt at a Position , Replace at a level, instead of ReplaceAll.
  7. Avoid Patterns that match nested structure or repetition of a complex pattern.
  8. With is faster than Module

In the following, most useful tip comes first.

For much less significant speed differences, see Wolfram Language Speed Trivia

Table vs Append

Append in a loop is mega slow. The longer the list, the exponentially slower.

(* 
Append in a loop is very slow. The longer the list, the exponentially slower.
 *)

$HistoryLength = 1;

Clear[ "x*" ];

xdatasize = 10000;

xtime1 = Timing[ xout1 = Table[ 1, {xdatasize} ]; ];

xout2 = {};
xtime2 = Timing[ Do[ AppendTo[ xout2, 1 ], {xdatasize} ]; ];

Print@ xtime1;
Print@ xtime2;

Print[ "Same output:", (xout1 === xout2)];

Print@ $Version

(*
{0., Null}
{0.171875, Null}
True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
Null
 *)

Select, Cases, Exit When Found

When using Select, and if you just want first found, give an argument that specifies the max count of item to return. It basically exit the search when the item is found. Or use SelectFirst

(* 
using Select, exist when found first item.
 *)

$HistoryLength = 1;

Clear[ "x*" ];

xdata = ReplacePart[ Table[ 0, {1000000} ], 9 -> 1 ];

Print@ First@ Timing[ xout1 = Select[ xdata, OddQ ]; ];

(* exit when found first *)
Print@ First@ Timing[ xout2 = Select[ xdata, OddQ, 1 ]; ];

Print[ "Same output:", (xout1 === xout2)];

Print@ $Version;

(* 
0.171875
0.015625
Same output:True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
Null
 *)

When using Cases, and if you just want first found, give an argument that specifies the max count of item to return. Or use FirstCase

(* When using Cases, and if you just want first found, give an argument
that specifies the max count of item to return. Or use FirstCase
 *)

$HistoryLength = 1;

Clear[ "x*" ];

xdata = RandomInteger[ {0,100}, 1000000 ] ;

xtime1 = Timing[ xout1 = First@ Cases[ xdata, 2 ]; ];

(* exit immediately when found *)
xtime2 = Timing[ xout2 = First@ Cases[ xdata, 2, {1}, 1 ]; ];

Print@ First@ xtime1;
Print@ First@ xtime2;
Print[ "Same output:", (xout1 === xout2)];

Print@ $Version;

(* 
0.03125
0.015625
Same output:True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
Null
 *)

Transpose is constant time and instantaneous

Transpose is constant time and instantaneous, regardless of matrix size. It is a great trick for many situations.

(* Transpose is instantaneous and constant time. regardless of matrix size *)

Clear[ "x*" ];

xx = Table[ {x, y}, {x, 3}, {y, 3} ];
x2 = Table[ {x, y}, {x, 10^3}, {y, 10^3} ];

Print@ First@ Timing[ Transpose[ xx ]; ];
Print@ First@ Timing[ Transpose[ x2 ]; ];

Print@ $Version;

(* 
0.
0.
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
 *)

Save Repeating Costy Expression to a constant

xtodo need to make better example. result so far puzzling.

if you have a complex expression repeated, use a temp constant for the expression to compute it just once.

For example, you need to check if the length of vector is zero, if so, return zero, else do something with that vector involving that length.

$HistoryLength = 1;

Clear[ "x*" ];

xdata = RandomReal[{-10, 10}, {10, 1000000}];

(* suppose this is slow function *)
xCostly = Function[#1 . #1];

(* compute just once, save to a constant *)
xf1 = Function[With[{x = xCostly[#1] }, If[ x < 0.001 , #1, #1/x] ]];

(* repeated computation *)
xf2 = Function[If[ (xCostly[#1]) < 0.001 , #1, #1/(xCostly[#1])]];

ClearSystemCache[];
xtime1 = First@ Timing[ xout1 = xf1 /@ xdata];

ClearSystemCache[];
xtime2 = First@ Timing[ xout2 = xf2 /@ xdata];

Print@ xtime1;
Print@ xtime2;
Print[ "Same output:", (xout1 === xout2)];

Print@ $Version;

(* 
0.015625
0.03125
Same output:True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
 *)

(*
result inconclusive
*)

Pure Function vs Pattern Matching Function

Using pattern matching for simple function, such as f[x_] := body is about 20 times slower than using f = Function[body].

(* speed diff, of pattern matching vs Function.
2024-02-11

*)

$HistoryLength = 1;

Clear[ "x*" ];

xf1[x_] := x+1;
xf2 = Function[#+1];

xdata = RandomInteger[ 100, 1000000 ];

xtime1 = Timing[ xout1 = Map[xf1, xdata];];
xtime2 = Timing[ xout2 = Map[xf2, xdata];];

Print@ xtime1;
Print@ xtime2;
Print[ "Same output:", (xout1 === xout2)];

Print@ $Version;

(* 
{0.4375, Null}
{0.015625, Null}
Same output:True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
 *)
(* speed diff, of pattern matching vs Function.
2024-08-03
*)

$HistoryLength = 1;

Clear[ "x*"];

xf1[x_,y_] := {x,y};
xf2 = Function[{#1,#2}];

xdata = Transpose@ Table[ {1,1}, {1000000}];

(* Print @ bigdata *)

xtiming1 = Timing[ xout1 = MapThread[xf1, xdata];];
xtiming2 = Timing[ xout2 = MapThread[xf2, xdata];];

Print@ xtiming1;
Print@ xtiming2;
Print[ "Same output:", (xout1 === xout2)];

Print[ $Version ];

(* 
{0.5, Null}
{0.34375, Null}
True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
 *)
(* speed diff, of pattern matching vs Function.
2024-08-03
*)

$HistoryLength = 1;

Clear[ xf1, xf2, xdata, xtime1, xtime2, xout1, xout2 ];

xf1[x_,y_] := {x,y};
xf2 = Function[{x,y},{x,y}];

(* bigdata = Transpose@ Partition[ RandomInteger[ 10, 10 ] ,2 ] *)

xdata = Transpose@ Table[ {1,1}, {1000000}];

(* Print @ bigdata *)

xtime1 = Timing[ xout1 = MapThread[xf1, xdata];];
xtime2 = Timing[ xout2 = MapThread[xf2, xdata];];

Print@ xtime1;
Print@ xtime2;
Print[ "Same output:", (xout1 === xout2)];

Print[ $Version ];

(* 
{0.484375, Null}
{0.546875, Null}
True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)

 *)

Area vs Handcrafted Triangle Area

the builtin Area function for computing triangle area is some 10 times slower than a hand crafted one.

(* 2024-02-09
the builtin Area function for computing triangle area is some 10 times slower than a hand crafted one
 *)

Clear[ "x*" ];

xTriangleSignedArea[{{x1_, y1_}, {x2_, y2_}, {x3_, y3_}}] := Det[{{x1, y1, 1}, {x2, y2, 1}, {x3, y3, 1}}]/2;

xdatasize = 3000;
xdata = RandomReal[ {-10,10}, {xdatasize,3,2} ];

xtime1 = First@ Timing[ xout1 = Map[ Abs@ xTriangleSignedArea @# &, xdata];];
xtime2 = First@ Timing[ xout2 = Map[ Function[Area[ Triangle[ # ] ] ] , xdata];];

Print@ xtime1;
Print@ xtime2;
Print@ (And@@ Flatten@ Map[LessThan[0.00001], Abs@Chop[xout1 - xout2], {-1}])

Print@ $Version;

(* 
0.03125
0.296875
True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
 *)

RegionCentroid vs Handcrafted Centroid for Triangle

(* 2024-02-09
using builtin RegionCentroid to compute triangle centroid, is some 20 times slower
*)

Clear[ "x*" ];

xTriangleCentroid[pts:{{_, _}..}] := Total[ pts ] /Length[pts];

xdatasize = 3000;
xdata = RandomReal[ {-10,10}, {xdatasize,3,2} ];

xtime1 = First@ Timing[ xout1 = Map[ xTriangleCentroid @# &, xdata];];
xtime2 = First@ Timing[ xout2 = Map[ Function[ RegionCentroid@ Triangle@ # ] , xdata];];

Print@ xtime1;
Print@ xtime2;

Print[ "Same output:", (xout1 === xout2)];

Print@ $Version;

(* 
0.015625
0.28125
Same output:True
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
 *)

TriangleCenter vs Handcrafted Triangle Center

Using builtin TriangleCenter is some 20 times slower. Probably because TriangleCenter is an experimental function.

(* 2024-02-09
Speed comparison of computing triangle orthocenter.
Using builtin TriangleCenter is some 20 times slower than hand crafted one.
 *)

Clear[ "x*" ];

xTriangleOrthocenter[{{a1_, a2_}, {b1_, b2_}, {c1_, c2_}}] :=
   {a1*((-b1)*b2 + a2*(b1 - c1) + c1*c2) +
      (b2 - c2)*(a2^2 + b1*c1 + b2*c2 - a2*(b2 + c2)),
     -((a1 - b1)*(a1*(b1 - c1) + a2*(b2 - c2)) +
       (b1 - c1)*((-a1)*c1 + b1*c1 + (-a2 + b2)*c2))}/
    (a2*b1 - a1*b2 - a2*c1 + b2*c1 + a1*c2 - b1*c2);

xdatasize = 1000;
(* bigdata = Table[ RandomReal[ {-10,10} ], {datasize}, {3}, {2}]; *)
xdata = RandomReal[ {-10,10}, {xdatasize, 3, 2} ] ;

xtime1 = First@ Timing[ xout1 = Map[ xTriangleOrthocenter, xdata];];
xtime2 = First@ Timing[ xout2 = Map[ Function[ TriangleCenter[#, "Orthocenter" ] ] , xdata];];

Print@ xtime1;
Print@ xtime2;
Print@ (And@@ Flatten@ Map[LessThan[0.00001], Abs@Chop[xout1 - xout2], {-1}]);

Print@ xTriangleOrthocenter[xdata[[1]]];

Print@ TriangleCenter[ xdata[[1]], "Orthocenter" ];

Print@ $Version;

(* 
0.015625
0.171875
True
{-36.28710155863487, 7.755262435449298}
{-36.287101558634035, 7.7552624354489605}
14.0.0 for Microsoft Windows (64-bit) (December 13, 2023)
 *)

WolframLang Code Competition