Подтвердить что ты не робот

Руководство по оптимизации LuaJIT 2

Я ищу хорошее руководство по оптимизации кода Lua для LuaJIT 2. Он должен сосредоточиться на спецификациях LJ2, например, как определить, какие трассировки компилируются, а какие нет, и т.д.

Любые указатели? Сбор ссылок на сообщения Lua ML будет делать в качестве ответа (бонусные баллы для суммирования этих ссылок здесь.)

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

4b9b3361

Ответ 1

Update

Майк недавно создал и выпустил замечательный легкий профайлер для LuaJIT, вы можете найти его здесь.

Update

В этой области wiki получил еще несколько страниц, особенно этот, в котором подробно описаны некоторые дополнительные материалы, не упомянутые в исходном ответе, и основан на сообщении почтового рассылки от Майка.


Недавно LuaJIT запустил собственный wiki и рассылка список, и с такими вещами приходит много, еще много камней об ускорении кода для LuaJIT.

Сейчас вики довольно тонкие (но всегда ищут людей, чтобы добавить к ней), однако одна замечательная страница, которая была добавлена ​​недавно, - a список функций NYI. Функции NYI заставляют JIT выходить из строя и возвращаться к интерпретатору, поэтому вполне очевидно, что следует избегать функций NYI как можно больше на горячем пути, особенно в циклах.

Некоторые интересующие темы из списка рассылки:

И просто повторить сказанное ниже (потому что это просто полезно), -jv - лучший инструмент для настройки производительности, он также должен стать вашей первой остановкой при устранении неполадок.


Оригинальный ответ

Я сомневаюсь, что вы найдете много на этом самом деле, главным образом, причина LJ2 все еще находится в стадии бета-тестирования, и поэтому большинство профилей выполняются наивно, поскольку нет никаких отладочных крюков для конкретных объектов LJ2, таких как рекордер.

С другой стороны, новый модуль FFI позволяет прямое обращение к таймерам с высоким разрешением (или профилирующим API, таким как VTune/CodeAnalyst), вы можете профилировать этот путь, но что-то еще требует расширений ядра LJ2 JIT, что не должно быть слишком жестким, так как код ясен и прокомментирован.


Один параметр командной строки рекордера (взятый из здесь):

Команды -jv и -jdump являются модулями расширения, написанными в Lua. Oни в основном используются для отладки самого компилятора JIT. Для описание их вариантов и формата вывода, пожалуйста, прочитайте блок комментариев в начале их источника. Их можно найти в lib каталога исходного дистрибутива или установленного под jit каталог. По умолчанию это /usr/local/share/luajit -2.0.0-beta8/jit на системах POSIX.

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


Обновление

С обновлением вопроса, это становится немного легче ответить. Поэтому давайте начнем с источника, LuaJIT.org:

до ручного оптимизации кода, всегда полезно проверить настройки оптимизации оптимизации JIT:

Компиляция

На странице Running мы можем увидеть все параметры настройки параметров JIT, для оптимизации мы сосредоточимся на -O. Сразу же Майк говорит нам, что включение всех оптимизаций имеет минимальное влияние на производительность, поэтому убедитесь, что вы работаете в -O3 (теперь это значение по умолчанию), поэтому единственными возможными здесь для нас реальными значениями являются пороги JIT и Trace.

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

-jv также полезен, чтобы помочь избежать проблем/ "резервных копий" , что приведет к спасению JIT.

Сам сайт не предлагает многое о том, как писать лучший или более оптимизированный код, за исключением некоторых небольших лакомых кусочков в учебное пособие FFI

Кэширование функций

Кэширование функций - хороший ускоритель производительности в Lua, но менее важно сосредоточиться на LuaJIT, поскольку JIT выполняет большинство этих оптимизаций, важно отметить, что кеширование функций FFI C плохое, желательно кэшировать пространство имен, в котором они находятся.

Пример со страницы:

плохо:

local funca, funcb = ffi.C.funcb, ffi.C.funcb -- Not helpful!
local function foo(x, n)
  for i=1,n do funcb(funca(x, i), 1) end
end

хорошо:

local C = ffi.C          -- Instead use this!
local function foo(x, n)
  for i=1,n do C.funcb(C.funca(x, i), 1) end
end

Проблемы с производительностью FFI

раздел Status описывает различные конструкции и операции, которые ухудшают производительность кода (главным образом потому, что они не скомпилированы, но используют вместо этого резерв VM).

Теперь мы перейдем на источник для всех драгоценностей LuaJIT, список рассылки Lua:

  • Избегайте вызовов C и NYI Lua в циклах: если вы хотите, чтобы LJ2-трассировщик ударил и дал полезную обратную связь, вам необходимо избегать функций NYI (еще не реализовано) или вызовов C, в которых компилятор трассировки не может идти. Поэтому, если у вас есть небольшие вызовы C, которые можно импортировать в lua и использовать в цикле, импортируйте их, в худшем случае они могут быть на 6% медленнее, чем реализация компилятора C, в лучшем случае быстрее.

  • Используйте линейные массивы по ipairs: согласно Майку, пары /next будут всегда медленнее по сравнению с другими методами (есть также малый лакомый кусочек там о кэшировании символов для трассировщика).

  • Избегайте вложенных циклов: каждый уровень вложенности принимает дополнительный проход к трассировке и будет немного менее оптимизирован, особенно избегать внутренних петли с более низкими итерациями.

  • Вы можете использовать 0-базовые массивы: Майк говорит здесь, что LuaJIT не имеет штрафа за производительность для массивов на основе 0, в отличие от стандартных Lua.

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

  • Избегайте множества крошечных циклов: это испортит эвристику разворачивания и замедлит код.

Меньшие лакомые кусочки:

Инструменты для профилирования доступны для обычного Lua, однако есть еще один новый проект, который официально совместим с LuaJIT (я сомневаюсь, что он будет использовать любую из функций LuaJIT в учетной записи), luatrace. В Liki wiki также есть страница советы по оптимизации для нормального Lua, они должны быть проверены на их эффективность в LuaJIT (большинство из этих оптимизаций вероятно, уже выполняются внутри), однако LuaJIT по-прежнему использует GC по умолчанию, это оставляет его в качестве одной из областей, где доходы от ручной оптимизации все еще могут быть большими (пока Майк не добавит пользовательский GC, о котором он говорил здесь и там).

Источник LuaJIT содержит несколько настроек для работы с внутренними компонентами JIT, однако для этого потребуется тщательное тестирование, чтобы настроить их для одного конкретного кода, на самом деле, возможно, было бы лучше избегать их полностью, особенно для тех, кто не знакомы с внутренними элементами JIT.

Ответ 2

Не совсем то, что вы ищете, но мне удалось провести обратную разработку объектов jit. * tracing. Ниже следует немного грубая, неточная, подверженная изменениям и очень неполная. Я начну использовать его в luatrace в ближайшее время. Эти функции используются в нескольких файлах -j. dump.lua, вероятно, хорошее место для начала.

jit.attach

Вы можете присоединить обратные вызовы к числу событий компилятора с помощью jit.attach. Обратный вызов можно вызвать:

  • когда функция была скомпилирована в байт-код ( "bc" );
  • при запуске или остановке записи трассировки ( "трассировка" );
  • когда записывается трасса ( "запись" );
  • или когда трассировка выходит через боковой выход ( "texit" ).

Установите обратный вызов с помощью jit.attach(callback, "event") и очистите тот же обратный вызов с помощью jit.attach(callback)

Аргументы, переданные обратному сообщению, зависят от сообщенного события:

  • "bc": callback(func). func - это только что записанная функция.
  • "trace": callback(what, tr, func, pc, otr, oex)
    • what - это описание события трассировки: "flush", "start", "stop", "abort". Доступно для всех событий.
    • tr - номер трассировки. Недоступно для флеша.
    • func - отслеживаемая функция. Доступно для запуска и прерывания.
    • pc - это программный счетчик - номер байт-кода записываемой функции (если это функция Lua). Доступно для запуска и прерывания.
    • otr start: родительский номер трассировки, если это боковая трассировка, прервать: прервать код (целое число)?
    • oex start: номер выхода для родительской трассировки, прерывание: причина прерывания (строка)
  • "record": callback(tr, func, pc, depth). Первые аргументы такие же, как и для начала трассировки. depth - глубина вложения текущего байт-кода.
  • "texit": callback(tr, ex, ngpr, nfpr).
    • tr - это номер трассировки, как раньше
    • ex - номер выхода
    • ngpr и nfpr - количество регистров общего назначения и регистров с плавающей запятой, которые активны на выходе.

jit.util.funcinfo(func, pc)

При передаче func и pc из обратного вызова jit.attach jit.util.funcinfo возвращает таблицу информации о функции, как debug.getinfo.

Поля таблицы:

  • linedefined: как для debug.getinfo
  • lastlinedefined: как для debug.getinfo
  • params: количество параметров, которые выполняет функция
  • stackslots: количество слотов стека использует локальную переменную функции
  • upvalues: количество upvalues, которое использует функция
  • bytecodes: число байт-кодов, скомпилированных функцией
  • gcconsts:??
  • nconsts:??
  • currentline: как для debug.getinfo
  • isvararg: если функция является функцией vararg
  • source: как для debug.getinfo
  • loc: строка, описывающая исходную и текущую строку, например "<source> : <line> "
  • ffid: быстрый идентификатор функции (если он один). В этом случае действительны только upvalues и addr ниже
  • addr: адрес функции (если она не является функцией Lua). Если это функция C, а не быстрая функция, то действителен только upvalues

Ответ 3

Я использовал ProFi в прошлом и нашел это весьма полезным!