Ответ 1
Хотя Lua выполняет простую оптимизацию при использовании ..
, вы все равно должны быть осторожны, чтобы использовать его в узком цикле, особенно при подключении очень больших строк, потому что это создаст много мусора и, таким образом, повлияет на производительность.
Лучший способ конкатенировать многие строки - table.concat
.
table.concat
позволяет использовать таблицу в качестве временного буфера для всех конкатенированных строк и выполнять конкатенацию только тогда, когда вы закончите добавление строк в буфер, например, в следующем глупом примере:
local buf = {}
for i = 1, 10000 do
buf[#buf+1] = get_a_string_from_somewhere()
end
local final_string = table.concat( buf )
Простую оптимизацию для ..
можно увидеть, анализируя дизассемблированный байт-код следующего script:
-- file "lua_06.lua"
local a = "hello"
local b = "cruel"
local c = "world"
local z = a .. " " .. b .. " " .. c
print(z)
вывод luac -l -p lua_06.lua
следующий (для Lua 5.2.2):
main (13 instructions at 003E40A0) 0+ params, 8 slots, 1 upvalue, 4 locals, 5 constants, 0 functions 1 [3] LOADK 0 -1 ; "hello" 2 [4] LOADK 1 -2 ; "cruel" 3 [5] LOADK 2 -3 ; "world" 4 [7] MOVE 3 0 5 [7] LOADK 4 -4 ; " " 6 [7] MOVE 5 1 7 [7] LOADK 6 -4 ; " " 8 [7] MOVE 7 2 9 [7] CONCAT 3 3 7 10 [9] GETTABUP 4 0 -5 ; _ENV "print" 11 [9] MOVE 5 3 12 [9] CALL 4 2 1 13 [9] RETURN 0 1
Вы можете видеть, что генерируется только один код операции CONCAT
, хотя в script используются многие операторы ..
.
Чтобы полностью понять, когда использовать table.concat
, вы должны знать, что строки Lua неизменяемы. Это означает, что всякий раз, когда вы пытаетесь объединить две строки, вы действительно создаете новую строку (если результирующая строка уже интернирована интерпретатором, но это обычно маловероятно). Например, рассмотрим следующий фрагмент:
local s = s .. "hello"
и предположим, что s
уже содержит огромную строку (например, 10 МБ). Выполнение этого оператора создает новую строку (10 Мбайт + 5 символов) и отбрасывает старую. Таким образом, вы только что создали мертвый объект размером 10 МБ для сборщика мусора. Если вы это сделаете повторно, вы в конечном итоге засоряете сборщик мусора. Это реальная проблема с ..
, и это типичный вариант использования, когда необходимо собрать все части финальной строки в таблице и использовать table.concat
на ней: это не позволит избежать образования мусора (все куски будут мусором после вызова table.concat
), но вы значительно уменьшите ненужный мусор.
Выводы
- Используйте
..
всякий раз, когда вы объединяете несколько, возможно, коротких строк, или вы не находитесь в узком цикле. В этом случаеtable.concat
может дать вам худшую производительность, потому что:- вы должны создать таблицу (которая обычно выбрасывается);
- вам нужно вызвать функцию
table.concat
(служебные данные вызова функции влияют на производительность больше, чем использование встроенного оператора..
несколько раз).
- Используйте
table.concat
, если вам нужно объединить многие строки, особенно если выполнено одно или несколько из следующих условий:- вы должны сделать это на последующих шагах (оптимизация
..
работает только внутри одного выражения); - вы находитесь в узком цикле;
- строки большие (скажем, несколько кБ или более).
- вы должны сделать это на последующих шагах (оптимизация
Обратите внимание, что это просто эмпирические правила. Там, где производительность действительно важна, вы должны профилировать свой код.
В любом случае Lua довольно быстро сравнивается с другими языками сценариев при работе со строками, поэтому обычно вам не нужно так много заботиться.