Comparing the speed of Metapost and TikZ

Posted on October 10, 2018

As a frequent user of both Metapost and TikZ, I often observe that TikZ is considerably slower than Metapost. But what’s the actual difference in speed? Let’s consider basic operations: drawing straight lines and drawing circles.

\usemodule[tikz]

\starttext

\testfeatureonce{1000}
    {\setbox0\hbox
        {\startMPcode draw (0,0) -- (1cm, 1cm); \stopMPcode}}

\elapsedtime

\testfeatureonce{1000}
    {\setbox0\hbox
        {\starttikzpicture \draw (0,0) -- (1cm, 1cm); \stoptikzpicture}}

\elapsedtime

\testfeatureonce{1000}
    {\setbox0\hbox
        {\startMPcode draw fullcircle scaled 1cm; \stopMPcode}}

\elapsedtime

\testfeatureonce{1000}
    {\setbox0\hbox
        {\starttikzpicture \draw (0,0) circle (1cm); \stoptikzpicture}}

\elapsedtime

\stoptext

The function \testfeatureonce{n}{...} runs the code inside the second argument n times and stores the elapsed time in the macro \elapsedtime. So, in the above example, we are drawing a straight line (or a circle) 1000 times, saving the output to a box (so that we don’t include the overhead of writing the content to PDF), and measuring the elapsed time. The result is as follows (formatted for clarity).

Shape Metapost TikZ
Line 0.434s 1.239s
Circle 0.533s 1.597s

So, TikZ is almost 3 times slower than Metapost for basic shapes.

Now, let’s try something more complicated: drawing a 2 state Markov chain. One of the selling features of TikZ is that drawing more “complicated” graphics is easy. Here is the code for drawing such a chain in TikZ:

\usemodule[tikz]
\usetikzlibrary[positioning]

\starttext
\tikzset{state/.style={circle, draw,
                line width=2bp,
                inner sep=0.5em,
                draw=blue!85,
                fill=blue!25!white,
                }}

\starttikzpicture[line width=1bp]
   \node[state] (off) {$0$};
   \node[state, right=of off] (on) {$1$};

   \draw[every loop]
       (off) edge[bend right, auto=right] node {$p$} (on)
       (on)  edge[bend right, auto=right] node {$q$} (off)
       (off) edge[loop left]  node {$1-p$} (off)
       (on)  edge[loop right] node {$1-q$} (off)
       ;

\stoptikzpicture
\stoptext
Two state Markov chain in TikZ

Using \testfeatureonce{100}{...} on this code takes 4.278s on my laptop. So, drawing one Markov chain takes about 43ms.

Metapost is a more declarative language, so Metapost code is more elaborate. Here is one way to draw the same figure in Metapost:

\startMPinclusions
  input boxes;
\stopMPinclusions
\starttext
\startMPcode
   pickup pencircle scaled 1bp;

   circleit.Off("$0$");
   circleit.On ("$1$");

   circmargin := 1EmWidth;

   Off.c = origin;
   On.w - Off.e = (1cm, 0);

   forsuffixes Box=Off, On :
      fill bpath Box withcolor 0.75[blue,white];
      drawunboxed(Box);
      draw bpath Box withcolor 0.8blue withpen pencircle scaled 2bp;
    endfor

   newpath p, q;
   p := Off.c {dir -40} .. On.c
        cutbefore bpath Off
        cutafter  bpath On
        cutends   (0,1mm);

   q:= On.c {dir 140} .. Off.c
        cutbefore bpath On
        cutafter  bpath Off
        cutends   (0,1mm);

   drawarrow p;
   drawarrow q;

   label.bot ("$p$", point 0.5 along p);
   label.top ("$q$", point 0.5 along q);

   p := Off.c {dir 150} .. (Off.w - (0.75cm, 0))  .. {dir 30} Off.c
        cutbefore bpath Off
        cutafter  bpath Off
        cutends   (0,1mm);

   q := On.c {dir -30} .. (On.e + (0.75cm, 0)) .. {dir -150} On.c
        cutbefore bpath On
        cutafter  bpath On
        cutends   (0,1mm);

   drawarrow;
   drawarrow;

   label.lft("$1-p$", point 0.5 along p);
   label.rt ("$1-q$", point 0.5 along q);
\stopMPcode
\stoptext
Two state Markov chain in TikZ

This code could have been made somewhat terser by defining macros Edge and Loop but I am not doing so. Running \testfeatureonce{100}{...} on the above code takes 0.790s on my laptop. So drawing one Markov chain takes about 8ms. For a somewhat more complicated drawing, TikZ is almost 5 times slower than Metapost.

But the speed tradeoff comes at a cost. The TikZ code is terser than Metapost and hides some of the complexity behind convenience macros. So, it is easier to understand (at least for beginners). This also explains why TikZ is so much more popular than Metapost. But the speed advantage makes me wonder: would there be significant speedup in TikZ code if Lua is used for parsing the input and metapost is used for drawing. In other words, is it worthwhile to rewrite the PGF backend for LuaTeX?


This entry was posted in Metapost and tagged metapost, tikz, efficiency.