Итак, если я правильно понимаю, типы данных Lua — строки, числа — это значения, а функции и таблицы — это ссылки. Означает ли это, что если я это сделаю:
local x = 5
local y = x
Является ли переменная y копией x и не является ссылкой, а это означает, что выполнение таким образом потребует вдвое больше памяти?
Пока делаю:
local myTab = { one = "hello" }
local myTab2 = myTab
Означает ли это, что myTab и myTab2 ссылаются на одну и ту же таблицу и назначение myTab myTab2 не будет потреблять дополнительную память?
Я спрашиваю об этом, поскольку обычно для повышения скорости я склонен переназначать глобальные переменные локальным.
Такой как:
Config = {} -- This is in file1.lua
-- On file2. lua we do:
local Config = Config
Таким образом, теоретически это должно немного повысить скорость, поскольку мы присваиваем ее локальной переменной, но мне интересно, будет ли это потреблять значительно больше памяти, особенно когда мы имеем дело с более крупными программами с большими объемами данных.
Итак, вот краткая информация о переменных Lua и использовании памяти. Когда вы назначаете переменную типа local x = 5
, а затем local y = x
, y
становится копией x
, а не ссылкой. Это означает, что он занимает дополнительную память, поскольку это отдельная копия.
С другой стороны, когда у вас есть такие таблицы, как local myTab = { one = "hello" }
и local myTab2 = myTab
, обе myTab
и myTab2
указывают на одну и ту же таблицу. Таким образом, назначение одного другому не требует больше памяти.
Что касается переназначения глобальных переменных локальным переменным, например local Config = Config
, это действительно может немного ускорить процесс, поскольку доступ к локальным переменным происходит быстрее, чем к глобальным. Но с точки зрения памяти это не должно привести к значительному увеличению, если только вы не имеете дело с огромными объемами данных.
Надеюсь, это прояснит ситуацию!
Ах я вижу. Спасибо за разъяснение. Так что, я думаю, это скорее просто решение, предпочитаете ли вы иметь более высокую скорость выполнения или использовать меньше памяти? Но я полагаю, что, поскольку в Lua есть автоматическая сборка мусора, и чаще всего вы будете использовать меньшие типы данных, память не должна вызывать особого беспокойства?
Точнее, оба используют одинаковый объем дополнительной памяти для хранения значения Lua/upvalue. Для примитивных типов это просто тег типа + прямое значение (например, число), в другом случае это тег типа + указатель/"ссылка".
Итак, если я правильно понимаю, типы данных Lua — строки, числа — это значения, а функции и таблицы — это ссылки.
Поскольку ваш вопрос в конечном итоге касается деталей реализации (например, использования памяти): он немного более тонкий, чем этот.
Значения Lua по сути представляют собой «объединение тегов»: есть тег типа, а затем значение, обычно одно машинное слово. Для типов, которые не умещаются в одно машинное слово, это значение будет ссылкой, и, таким образом, эти типы будут «ссылочными».
nil
, логические значения и числа в Lua являются «примитивными» типами: их дешево копировать (они используют всего одно машинное слово или меньше, возможно, два машинных слова вместе с тегом типа, если ваша реализация Lua не использует NaN box), поэтому они копируются при каждом задании. Значение представляет собой просто логическое число или число с плавающей запятой. Не копировать их (ссылаться на них) будет дороже, чем копировать.
Строки ведут себя как тип «значение»: они неизменяемы, поэтому нет никакой разницы между копированием и ссылкой на них. Но они, тем не менее, реализованы как ссылочные типы по соображениям производительности. Если вы присваиваете строку переменной, строка не «копируется» — копируется только ссылка (указатель с тегом типа).
Это означает, что когда вы делаете local x = ...
, вы используете только постоянный объем дополнительной памяти: дополнительная память необходима для хранения одного значения Lua, которое будет составлять около одного или двух машинных слов. Здесь не происходит копирования таблиц, строк, пользовательских данных, потоков или функций: все типы, которые невозможно скопировать тривиально, реализуются как ссылочные типы, независимо от того, изменяемы они или нет.
Теоретически это должно немного повысить скорость, поскольку мы присваиваем ее локальной переменной.
Действительно, обычно так и происходит, поскольку это экономит вам поиск по хешу в глобальной таблице, но я бы, тем не менее, рекомендовал воздержаться от преждевременной оптимизации, особенно учитывая «умные» реализации, такие как LuaJIT.
но мне интересно, будет ли это потреблять значительно больше памяти
Обычно это незначительно, но вам следует знать о следующих последствиях:
local
. (Это лишь небольшое постоянное использование памяти, хотя фактически оно незначительно.)local
используется в функциях, оно становится восходящим значением, выделенным в куче. (Опять же, только небольшое постоянное использование, независимо от того, насколько велико значение, на которое вы ссылаетесь или копируете.)local
, потребуется «слот повышения значения», чтобы ссылаться на значение повышения.Так что в конечном итоге это компромисс между временем и пространством: если вы не выполняете локализацию, функции должны обращаться к глобальной таблице (технически их таблице окружения, которая по умолчанию соответствует глобальной таблице), чтобы получить доступ к глобальным переменным. Если вы локализуете, вы объединяете функцию со значениями на момент локализации с небольшими затратами памяти (поскольку теперь функции фактически необходимо хранить указатель на верхнее значение).
Также обратите внимание, что локализация мешает исправлению обезьян: предположим, вы делаете Config = {frobnicate = true}
, затем загружаете файл, который локализует local Config = {}
, затем другой файл, который переопределяет Config = {frobnicate = false}
. Второй файл по-прежнему будет работать с устаревшим определением.
Очень хорошо написанный ответ, спасибо. Итак, это означает, что если я готов справиться с проблемой немного более длинного кода для локализации переменных, компромисс в скорости стоит по сравнению с очень небольшими дополнительными затратами памяти на локализацию?
@Мей, я бы не решился сказать, что оно «стоит того». Если в вашем коде возникли проблемы с производительностью, попробуйте такие оптимизации, а затем измерьте эффект. Не засоряйте свой код «преждевременной» оптимизацией. На реализацию PUC-Rio может быть заметное влияние, на LuaJIT я бы не ожидал такого большого влияния. Но да, стоимость памяти практически не волнует.
Строки Lua ведут себя так, как будто они скопированы, но я подозреваю, что на самом деле они не копируются глубоко. Возможно, я рассмотрю реализацию C.