Lua DISCUSSION

Building a large string in a Lua loop with .. is slow: is table.concat really better?

Started by tawonga Lua string concatenationtable.concatimmutable stringsmemory usageNodeMCU heap
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Building a large string in a Lua loop with .. is slow: is table.concat really better?

tawonga Lua Forum
#1

My logger on an ESP8266 running NodeMCU builds a CSV block by doing buf = buf .. line .. "\n" inside a loop. With a few hundred lines it is fine, but with several thousand it gets very slow and I sometimes run out of memory, even though the final string is not that big.

I have read that table.concat should be used instead. Why is repeated concatenation so expensive in Lua, and what is the recommended pattern on a device with little RAM?

Community replies 5

Re: Building a large string in a Lua loop with .. is slow: is table.concat really better?

#2

Lua strings are immutable. buf = buf .. line cannot extend the existing string; it allocates a new one and copies the old contents plus the new piece. With n pieces of average length L, the total copied is about L × n² / 2.

For 10,000 lines of 50 bytes the final string is only 500 kB, but the loop copies roughly 50 × 10,000 × 10,001 / 2 bytes, about 2.5 GB, and every intermediate string becomes garbage for the collector to clean up.

Re: Building a large string in a Lua loop with .. is slow: is table.concat really better?

#3

The standard pattern is to collect the pieces in a table and join once: local parts = {}, then parts[#parts + 1] = line in the loop and local buf = table.concat(parts, "\n") at the end. table.concat builds the result in one pass, so the work is proportional to the final size. The second argument is the separator, which also removes the need to append the newline to every piece.

It only accepts strings and numbers. A nil, boolean or table in the list raises an error, so convert with tostring first where needed.

Re: Building a large string in a Lua loop with .. is slow: is table.concat really better?

#4

A single expression is not a problem. a .. b .. c .. d is compiled as one concatenation of all four operands, so it creates one result, not three intermediates. The quadratic behaviour only comes from accumulating into the same variable across loop iterations. So parts[#parts + 1] = id .. "," .. value inside the loop is fine.

string.format("%d,%.2f", id, value) is an alternative that also controls the number formatting.

Re: Building a large string in a Lua loop with .. is slow: is table.concat really better?

#5

On a small device, the better fix is often not to build the big string at all. With only a few tens of kilobytes of heap, both the table of pieces and the joined result have to fit in RAM at the same time. Write each line to the file or socket as it is produced, or flush in chunks: collect, say, 20 lines, join and write them, then clear the table and continue. Peak memory then depends on the chunk size instead of the total log size.

On standard Lua, io.write(a, b, c) and file:write(a, b, c) accept several arguments, so you can output pieces without concatenating them first.

Re: Building a large string in a Lua loop with .. is slow: is table.concat really better?

#6

To confirm where the memory goes, print collectgarbage("count") before and after the loop; it returns the memory in use by Lua in kilobytes. With the accumulate-in-a-string version you will see it climb and fall as the collector chases the discarded intermediates, and an allocation can fail when a new copy is needed while the old one is still alive.

NodeMCU also has node.heap() for the free heap in bytes. Check the documentation of your firmware build, since the available modules and the file API differ between versions.

TEP COMMUNITY