Переменные Lua и их влияние на скорость и память

Итак, если я правильно понимаю, типы данных 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 ведут себя так, как будто они скопированы, но я подозреваю, что на самом деле они не копируются глубоко. Возможно, я рассмотрю реализацию C.

aschepler 23.06.2024 14:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
60
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Итак, вот краткая информация о переменных Lua и использовании памяти. Когда вы назначаете переменную типа local x = 5, а затем local y = x, y становится копией x, а не ссылкой. Это означает, что он занимает дополнительную память, поскольку это отдельная копия.

С другой стороны, когда у вас есть такие таблицы, как local myTab = { one = "hello" } и local myTab2 = myTab, обе myTab и myTab2 указывают на одну и ту же таблицу. Таким образом, назначение одного другому не требует больше памяти.

Что касается переназначения глобальных переменных локальным переменным, например local Config = Config, это действительно может немного ускорить процесс, поскольку доступ к локальным переменным происходит быстрее, чем к глобальным. Но с точки зрения памяти это не должно привести к значительному увеличению, если только вы не имеете дело с огромными объемами данных.

Надеюсь, это прояснит ситуацию!

Ах я вижу. Спасибо за разъяснение. Так что, я думаю, это скорее просто решение, предпочитаете ли вы иметь более высокую скорость выполнения или использовать меньше памяти? Но я полагаю, что, поскольку в Lua есть автоматическая сборка мусора, и чаще всего вы будете использовать меньшие типы данных, память не должна вызывать особого беспокойства?

Mey 23.06.2024 09:53

Точнее, оба используют одинаковый объем дополнительной памяти для хранения значения Lua/upvalue. Для примитивных типов это просто тег типа + прямое значение (например, число), в другом случае это тег типа + указатель/"ссылка".

Luatic 23.06.2024 15:01
Ответ принят как подходящий

Итак, если я правильно понимаю, типы данных Lua — строки, числа — это значения, а функции и таблицы — это ссылки.

Поскольку ваш вопрос в конечном итоге касается деталей реализации (например, использования памяти): он немного более тонкий, чем этот.

Значения Lua по сути представляют собой «объединение тегов»: есть тег типа, а затем значение, обычно одно машинное слово. Для типов, которые не умещаются в одно машинное слово, это значение будет ссылкой, и, таким образом, эти типы будут «ссылочными».

nil, логические значения и числа в Lua являются «примитивными» типами: их дешево копировать (они используют всего одно машинное слово или меньше, возможно, два машинных слова вместе с тегом типа, если ваша реализация Lua не использует NaN box), поэтому они копируются при каждом задании. Значение представляет собой просто логическое число или число с плавающей запятой. Не копировать их (ссылаться на них) будет дороже, чем копировать.

Строки ведут себя как тип «значение»: они неизменяемы, поэтому нет никакой разницы между копированием и ссылкой на них. Но они, тем не менее, реализованы как ссылочные типы по соображениям производительности. Если вы присваиваете строку переменной, строка не «копируется» — копируется только ссылка (указатель с тегом типа).

Это означает, что когда вы делаете local x = ..., вы используете только постоянный объем дополнительной памяти: дополнительная память необходима для хранения одного значения Lua, которое будет составлять около одного или двух машинных слов. Здесь не происходит копирования таблиц, строк, пользовательских данных, потоков или функций: все типы, которые невозможно скопировать тривиально, реализуются как ссылочные типы, независимо от того, изменяемы они или нет.

Теоретически это должно немного повысить скорость, поскольку мы присваиваем ее локальной переменной.

Действительно, обычно так и происходит, поскольку это экономит вам поиск по хешу в глобальной таблице, но я бы, тем не менее, рекомендовал воздержаться от преждевременной оптимизации, особенно учитывая «умные» реализации, такие как LuaJIT.

но мне интересно, будет ли это потреблять значительно больше памяти

Обычно это незначительно, но вам следует знать о следующих последствиях:

  • Вам понадобится место для хранения этого local. (Это лишь небольшое постоянное использование памяти, хотя фактически оно незначительно.)
  • Если это local используется в функциях, оно становится восходящим значением, выделенным в куче. (Опять же, только небольшое постоянное использование, независимо от того, насколько велико значение, на которое вы ссылаетесь или копируете.)
  • Всем функциям, которые используют это local, потребуется «слот повышения значения», чтобы ссылаться на значение повышения.

Так что в конечном итоге это компромисс между временем и пространством: если вы не выполняете локализацию, функции должны обращаться к глобальной таблице (технически их таблице окружения, которая по умолчанию соответствует глобальной таблице), чтобы получить доступ к глобальным переменным. Если вы локализуете, вы объединяете функцию со значениями на момент локализации с небольшими затратами памяти (поскольку теперь функции фактически необходимо хранить указатель на верхнее значение).

Также обратите внимание, что локализация мешает исправлению обезьян: предположим, вы делаете Config = {frobnicate = true}, затем загружаете файл, который локализует local Config = {}, затем другой файл, который переопределяет Config = {frobnicate = false}. Второй файл по-прежнему будет работать с устаревшим определением.

Очень хорошо написанный ответ, спасибо. Итак, это означает, что если я готов справиться с проблемой немного более длинного кода для локализации переменных, компромисс в скорости стоит по сравнению с очень небольшими дополнительными затратами памяти на локализацию?

Mey 24.06.2024 14:01

@Мей, я бы не решился сказать, что оно «стоит того». Если в вашем коде возникли проблемы с производительностью, попробуйте такие оптимизации, а затем измерьте эффект. Не засоряйте свой код «преждевременной» оптимизацией. На реализацию PUC-Rio может быть заметное влияние, на LuaJIT я бы не ожидал такого большого влияния. Но да, стоимость памяти практически не волнует.

Luatic 26.06.2024 21:47

Другие вопросы по теме