# Lua, does tex.print only accumulate

I can either build the output first then use tex.print or I can use tex.print for each string. Is there a performance penalty?

It's easier to use tex.print than having to build the string but if it is slow(for thousands of calls) then it is not worth it.

I've tried a small benchmark and it seems that concatenation with .. is a bit faster than repeated tex.print. Note that it is a very simple benchmark and the performance might change if there are other factors involved, for example if the concatenated string becomes very long the typesetting may be more complicated than a sequence of shorter strings (with line breaks/hyphenation, paragraph layout etc).

First a benchmark with time lualatex testfile.tex (Linux), which computes the time for the full process. The code below concatenates 10 strings, of which one is a random number to prevent caching, either with the .. operator or with repeated tex.print calls. This is repeated 50,000 times, which generates 1087 pages of output.

\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
function dotconcat()
a = "abc "
c = "def "
d = "ghi "
e = "\\par"
for i=1,50000 do
b = math.random(1000)
x = a .. b .. c .. d .. a .. b .. c .. d .. a .. b .. e
tex.print(x)
end
end
function texprintconcat()
a = "abc "
c = "def "
d = "ghi "
e = "\\par"
for i=1,50000 do
b = math.random(1000)
tex.print(a) tex.print(b)
tex.print(c) tex.print(d)
tex.print(a) tex.print(b)
tex.print(c) tex.print(d)
tex.print(a) tex.print(b)
tex.print(e)
end
end

\end{luacode*}
\begin{document}
% either
\directlua{dotconcat()}
% or
%\directlua{texprintconcat()}
\end{document}


Timing results with dotconcat():

real    0m6,548s
user    0m6,435s
sys     0m0,108s


With texprintconcat():

real    0m7,016s
user    0m6,890s
sys     0m0,120s


Second benchmark with l3benchmark, which measures only the time used in the function call:

\documentclass{article}
\usepackage{luacode}
\usepackage{l3benchmark}
\begin{luacode*}
function dotconcat()
a = "abc "
c = "def "
d = "ghi "
e = "\\par"
b = math.random(1000)
x = a .. b .. c .. d .. a .. b .. c .. d .. a .. b .. e
tex.print(x)
end
function texprintconcat()
a = "abc "
c = "def "
d = "ghi "
e = "\\par"
b = math.random(1000)
tex.print(a) tex.print(b)
tex.print(c) tex.print(d)
tex.print(a) tex.print(b)
tex.print(c) tex.print(d)
tex.print(a) tex.print(b)
tex.print(e)
end

\end{luacode*}
\begin{document}
\ExplSyntaxOn
\benchmark_once:n{%
\prg_replicate:nn {50000} {\directlua{dotconcat()}}% or texprintconcat()
}
\ExplSyntaxOff
\end{document}


Results:

dotconcat()
7.98 seconds (3.03e7 ops)
texprintconcat()
8.16 seconds (2.61e7 ops)


Note that the benchmark itself adds another 1 second to the compilation. The difference is between 200ms (l3benchmark) and 400ms (Linux time), which is a few percent of the total - so not fully negligible but also not very substantial.

Edit: to simplify the benchmark code I used \benchmark_once:n{\directlua{dotconcat()}} (and the same for texprintconcat()) on the code with the for loop, which amplifies the results:

dotconcat: 8.93 seconds (2.84e7 ops)
texprintconcat: 9.9 seconds (2.8e7 ops)


Even though the number of operations is now more equal, the time taken for tex.print is now 1 second more than the .. case. This shows once more that benchmarking is tricky, and you should be careful when interpreting the results.

• Thanks for doing the test. It seems it is not a huge difference but probably worth using ... – user184375 Sep 22 at 10:29