Speeding up PGFPlots using LuaTeX
Posted on October 21, 2018
In the previous post, I compared the speed of Metapost and TikZ for drawing similar graphics and found that TikZ is three to five times slower than Metapost. Although TikZ is slower, it does provide a much higher level interface to drawing graphics and shifting to Metapost can take a lot of user time, which is often more valuable than computer time.
As an example, consider the PGFPlots package, which provides a high-level interface for drawing function plots. Although there is a Metapost package for drawing graphs, PGFPlots has more features and better documentation. In this post, I show that we can speed up plotting functions using PGFPlots by offloading the function computation to LuaTeX.
As an example, consider the plot of the following function
0.5 - 0.5*exp(-3*x)*(cos(5.196*x) + 0.5774*sin(5.196*x))
which is the step response of a second order dynamical system (the exact details are not relevant for this post). PGFPlots makes it very simple to plot such functions:
\usemodule[pgfplots]
\starttext
\starttikzpicture
\startaxis
[
width=6cm,
trig format plots=rad,
xmin=0, xmax=4,
]
\addplot[domain=0:4, samples=500]
{0.5 - 0.5*exp(-3*x)*(cos(5.196*x) + 0.5774*sin(5.196*x)) };
\stopaxis
\stoptikzpicture
\stoptext
On my laptop, the above code takes 1.341s (measured using the
\testfeatureonce
macro explained in the previous post). Most of the time is
spent in calculating the algebric expression. We can speed this up by defining
a Lua function that does the calculation.
\usemodule[pgfplots]
\starttext
\startluacode
local exp = math.exp
local cos = math.cos
local sin = math.sin
local step = function(x)
return 0.5 - 0.5*exp(-3*x)*(cos(5.196*x) + 0.5774*sin(5.196*x))
end
thirddata = thirddata or {}
thirddata.step = function(x)
return context(step(x))
end
\stopluacode
\starttikzpicture
[declare function={
step(\x) = \ctxlua{thirddata.step(\x)};
}]
\startaxis
[
use fpu=false,
width=6cm,
xmin=0, xmax=4,
]
\addplot[domain=0:4, samples=500] {step(x)};
\stopaxis
\stoptikzpicture
\stoptext
On my laptop, this takes 0.333s. So, plotting in PGFPlots is four times faster if we use Lua to do more complicated calculations.
I don’t know the internals of PGFPlot well, but recent versions of PGFPlot do use Lua for some of the calcuations. So, I don’t completely understand why I get such a big speedup by doing the calculations in Lua.
Addendum
Henri Menke pointed out in the comments that using fpu=false
only works for
simple plots and fails for plots with log scale. As explained in his excellent
article on using LuaTeX
FFI, a more robust
solution is to convert back and forth between fpu
float and lua
float. The
code below does that:
\usemodule[pgfplots]
\starttext
\startluacode
local exp = math.exp
local cos = math.cos
local sin = math.sin
local step = function(x)
return 0.5 - 0.5*exp(-3*x)*(cos(5.196*x) + 0.5774*sin(5.196*x))
end
thirddata = thirddata or {}
thirddata.step = function(x)
return context(step(x))
end
\stopluacode
\pgfmathdeclarefunction{step}{1}
{\pgfmathfloatparsenumber
{\ctxlua{thirddata.step(\pgfmathfloatvalueof{#1})}}}
\starttikzpicture
\startaxis
[
width=6cm,
xmin=0, xmax=4,
]
\addplot[domain=0:4, samples=500] {step(x)};
\stopaxis
\stoptikzpicture
\stoptext
In his article, Henri mentions in PGF version 3, one can use Lua functions to
convert between fpu
float and lua
float, but that version is not available
in current texlive or context standalone. Running the above code on my laptop
takes 0.377s; slightly slower than the previous code but still in the
ballpark of a factor of four improvement.
Addendum 2
Henri Menke pointed on in the comments that we ned to add
\pgfplotsset{compat=1.12}
or higher to activate the lua
backend in pgfplots. Being a ConTeXt user, I
always assumed that pgfplot always uses the latest version and I needed to add
compat=something
only if I wanted to use old syntax. It is actually the
other way round! Like most LaTeX package, pgfplots errs on the side of being
more conservative and uses newer features if they are explicitly activated.
Adding:
\pgfplotsset{compat=newest}
reduces the runtime of the vanilla version to 0.144s, which is a factor
of three faster than my handwritten lua code, and thus almost a factor of
ten faster than the old code. So, the simple way to speed up pgfplots is
to add \pgfplotsset{compat=1.12}
or higher in your code!
I’ll leave this post here as a reminder to RTFM.
This entry was posted in Tikz and tagged tikz, luatex, efficiency.