I start to appreciate the Latex3 expl library for the nice abstractions it gives (e.g. strings, lists…), but I also want to create a code as efficient as possible as it is often my bottle neck. In practice, I often do a mix of legacy code and expl code, like \def\mymacro and \ifdefined\mymacro instead of \cs_set:Nn \mymacro: {} and \cs_exist:NTF \mymacro:… So here are my questions:

In general, is legacy code more efficient than expl functions, and what is the order of magnitude of the difference? For instance, is using \def quicker than \cs_set? I would expect expl code to be always slower since I would expect it to expand to the legacy code first, but I might be wrong.

To give maybe a more concrete example, wipet provided this code, while I would naturally try to implement it myself using a token list and \tl_map_inline with some checks like \if_cs_exist:w… He also recreates the concept of list using \addto while I would use a \seq (which are in my experience quite inefficient to build, but maybe I used them poorly?)… Is it possible to create a code as efficient in LaTeX3, or should I just stick to legacy code for parts that must be efficient?

And more generally, what are the structures that I could efficiently use from LaTeX3?

And is it fine to mix both legacy and expl3 codes?

  • 1
    It depends partly what you mean by 'efficient': for example, both l3regex and l3fp are basically written in primitives-renamed, but something like the hooks code or siunitx mainly uses higher-level constructs
    – Joseph Wright
    Nov 9, 2023 at 13:13
  • 1
    BTW: IMHO the common definition of efficiency is to much "execution based". By this I mean that only execution resources such as memory and time are taken into account. Additional work during implementation is not taken into account, nor are extensive tests of the code and its correctness. If these are taken into account, well-documented and tested functions such as those of LaTeX3 have a clear advantage over constantly reinventing the wheel with the help of TeX primitives.
    – cabohah
    Nov 9, 2023 at 13:38
  • 4
    most expl3 is far more efficient than a typical user would write to do the same thing, but most users would never write a regex engine in tex, so efficiency is the wrong thing to ask about here. Nov 9, 2023 at 13:43
  • 1
    Mixing legacy code an l3 code is another question. I would not use legacy commands or primitives in l3 code if a corresponding l3 function exists. For example in l3 code I would always use \exp_after:wN instead of \expandafter. But for several typeset features you still need to use legacy code, because there do not exist l3 functions, e.g., footnotes, page headers (but there are things already changing), sections, tables, environments, …
    – cabohah
    Nov 9, 2023 at 13:43
  • 1
    is using \def quicker than \cs_set? I well yes of course it is, as all commands are defined using \def in the end. It is impossible to speed things up by adding layers of macro expansion. Nov 9, 2023 at 13:46

1 Answer 1


Any abstraction will always add to the time taken for operations. However, there is always a balance between absolute maximum speed, flexibility of code and clarity. For example, the higher-level conditionals in expl3 (\tl_if_eq:NNTF, for example) are set up such that they can handle arbitrary material in the two branches. That has a non-zero impact on performance compared with \if_meaning:w (the \ifx primitive renamed). However, it is very rare that the team worry about this, even in relatively low-level code. The only places where 'just the primitives renamed' are used are

  • In early set-up (l3basics)
  • In l3regex
  • In l3fp

For l3fp and l3regex, the need to rally 'push' TeX means we need to use every trick we can. However, that applies almost nowhere else. If you want an extreme example, the tl data type uses \edef\<name>{\unexpanded{<content>} under the hood: that is very very slightly slower than \def\<name>{<content>} but allows # tokens, and it's so little real-world impact we prefer consistency over performance.

As an example of more 'user level' code, in siunitx I don't worry too much about using higher-level expl3 constructs as and when they are useful. What I do worry about is the nature of data structures. If you look at the v3 code, I use a prop for collecting units: these are open-ended lists with multiple parts, and this flexible data structure makes sense. However, for numbers, I use a custom approach: a fixed number of brace groups inside a tl. This makes sense there as I can do 'classical' argument number manipulation to extract and add material.

It's also worth remembering we are comparing here expl3 code that has decades of work (either directly by the team or by incorporating ideas from across the TeX world) with primitive code written by you. My own experience is that expl3 is better written than arbitrary stuff I've done. In siunitx, v1 is slower than v2 even though v2 is written in expl3 and v1 is not: it's all hand-coded by me. Now, I could (today) take all of the expl3 experience and use that to write out myself each loop, data structure, etc., I use: I thought about that, and it was a complete pain in the neck.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .