Почему это Lua оптимизация hack повысить производительность?

Я смотрю на документ, который описывает различные методы для повышения производительности Lua код сценария, и я в шоке, что такие трюки потребуются. (Хотя я цитирую Lua, я видел подобные хаки в Javascript).

зачем нужна эта оптимизация:

например, код

for i = 1, 1000000 do 
   local x = math.sin(i) 
end

работает на 30% медленнее, чем этот:

local sin = math.sin 
for i = 1, 1000000 do
    local x = sin(i) 
end

они повторно объявляют sin функция локально.

почему это было бы полезно? В любом случае, это работа компилятора. Почему программист должен выполнять работу компилятора?

Я видел похожие вещи в Javascript; и, очевидно, должен быть очень хорошая причина, почему интерпретация компилятор не выполняет свою работу. Что это?


Я вижу это неоднократно в среде Lua, в которой я играю; люди объявляют переменные как местный:

local strfind = strfind
local strlen = strlen
local gsub = gsub
local pairs = pairs
local ipairs = ipairs
local type = type
local tinsert = tinsert
local tremove = tremove
local unpack = unpack
local max = max
local min = min
local floor = floor
local ceil = ceil
local loadstring = loadstring
local tostring = tostring
local setmetatable = setmetatable
local getmetatable = getmetatable
local format = format
local sin = math.sin

что здесь происходит, что люди должны делать работу компилятора? Компилятор запутался в том, как найти format? Почему это проблема, с которой приходится иметь дело программисту? Почему об этом не позаботились в 1993 году?


я тоже, кажется, попал в логический парадокс:

  1. оптимизация не должна выполняться без профилирования
  2. Lua не имеет возможности профилироваться
  3. Lua должен не оптимизировано

6 ответов


почему это было бы полезно? В любом случае, это работа компилятора. Почему программист должен выполнять работу компилятора?

Lua-динамический язык. Компиляторы могут делать много рассуждений на статических языках, например, вытаскивать постоянные выражения из цикла. В динамических языках ситуация несколько иная.

основная (и только) структура данных Lua-это таблица. math также просто таблица, хотя она используется как пространство имен здесь. Никто не может остановить вас от изменения math.sin функция где-то в цикле (даже думал, что это было бы неразумно делать), и компилятор не может знать об этом при компиляции кода. Поэтому компилятор делает именно то, что вы ему указываете: на каждой итерации цикла найдите


причина, по которой это не делается по умолчанию, я не знаю. Однако это быстрее, потому что местные жители записываются в регистр, а глобальный означает поиск в таблице (_G), которая, как известно, несколько медленнее.

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

Если вы действительно хотите быстро Lua, вы можете попробовать LuaJIT


Я вижу это неоднократно в среде Lua, в которой я играю; люди объявляют переменные локальными:

делать это по умолчанию просто неправильно.

возможно, полезно использовать локальные ссылки вместо доступа к таблице, когда функция используется снова и снова, как в вашем примере цикла:

local sin = math.sin 
for i = 1, 1000000 do
  local x = sin(i) 
end

однако, внешние циклы, накладные расходы на добавление доступа к таблице совершенно незначительны.

что здесь происходит, что люди должны делать работу компилятора?

потому что два примера кода, которые вы сделали выше, не означают одно и то же.

это не так, как функция может измениться во время работы моей функции.

Lua - очень динамичный язык, и вы не можете делать те же предположения, что и в других более ограничительных языках, таких как C. функция can изменение во время выполнения цикла. Учитывая динамический характер языка компилятор не может предположить, что функция не изменится. Или, по крайней мере, не без сложного анализа вашего кода и его последствий.

фокус в том, что, даже если ваши две части кода выглядят эквивалентными, в Lua они не являются. На первом вы явно говорите ему "получить функцию sin внутри математической таблицы на каждой итерации". На втором вы снова используете одну ссылку на ту же функцию и снова.

рассмотрим следующий пример:

-- The first 500000 will be sines, the rest will be cosines
for i = 1, 1000000 do 
   local x = math.sin(i)
   if i==500000 then math.sin = math.cos end 
end

-- All will be sines, even if math.sin is changed
local sin = math.sin
for i = 1, 1000000 do 
   local x = sin(i)
   if i==500000 then math.sin = math.cos end 
end

хранение функций в локальных переменных удаляет индексацию таблицы, чтобы искать функциональную клавишу на каждой итерации цикла, математические очевидны, так как им нужно искать хэш в математической таблице, другие нет, они индексируются в _G (глобальная таблица), которая теперь _ENV (таблица среда) в 5.2.

кроме того, нужно иметь возможность профилировать lua с помощью API отладочных крючков или с помощью отладчиков lua, лежащих вокруг.


мое предположение заключается в том, что в оптимизированной версии, поскольку ссылка на функцию хранится в локальной переменной, обход дерева не должен выполняться на каждой итерации цикла for (для поиска в math.sin).

Я не уверен в локальных ссылках на имена функций, но я бы предположил, что требуется какой-то глобальный поиск пространства имен, если локальный не найден.

затем снова, я мог бы быть ;)

Edit: я также предположим, что компилятор Lua немой (что является общим предположением для меня о компиляторах в любом случае;))


это не просто ошибка / особенность Lua многие языки, в том числе Java и C будет работать быстрее, если вы получаете доступ к локальным значениям вместо значений вне области, например, из класса или массива.

на C++ например, доступ к локальному члену быстрее, чем к переменным членам некоторого класса.

это будет считать до 10 000 быстрее:

for(int i = 0; i < 10000, i++)
{
}

чем:

for(myClass.i = 0; myClass.i < 10000; myClass.i++)
{
}

причина Lua проводит глобальную значения внутри таблицы-это потому, что она позволяет программисту быстро сохранять и изменять глобальную среду, просто изменяя таблицу ссылок _G. Я согласен, что было бы неплохо иметь некоторый "синтаксический сахар", который рассматривал глобальную таблицу _G как частный случай; переписывая их все как локальные переменные в области файла (или что-то подобное), конечно, ничто не мешает нам делать это самим; возможно, функция optGlobalEnv(...), который "локализует" таблицу _G и ее члены / значения в "области файлов" с помощью unpack() или что-то еще.