Руководство по оптимизации LuaJIT 2 - PullRequest
28 голосов
/ 24 августа 2011

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

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

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

Ответы [ 3 ]

31 голосов
/ 26 августа 2011

Обновление

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

Обновление

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


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

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

Некоторые темы, представляющие интерес из списка рассылки:

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


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

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

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


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

Команды -jv и -jdump являются модулями расширения, написанными на Lua,Они в основном используются для отладки самого 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 плохо , предпочтительно кэшировать пространство имен, в котором они находятся.

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

* * 1106 плохо: * * 1107
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 подробно описывает различные конструкции и операции, которые ухудшают производительность кода (в основном из-за того, что они не скомпилированы, а вместо этого используют запасной вариант виртуальной машины).

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

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

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

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

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

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

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

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

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

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

7 голосов
/ 30 августа 2011

Не совсем то, что вы ищете, но я справился с некоторым реверс-инжинирингом средств трассировки jit. *То, что следует, немного грубовато, неточно, может быть изменено и очень неполно.Я скоро начну использовать его в 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", "остановить "," отменить ".Доступно для всех событий.
    • tr - номер трассы.Недоступно для промывки.
    • func - отслеживаемая функция.Доступно для запуска и отмены.
    • pc - счетчик программы - номер байт-кода записываемой функции (если это функция Lua).Доступно для запуска и отмены.
    • otr start: номер родительской трассы, если это боковая трасса, abort: код отмены (целое число)?
    • oex start: номер выходадля родительской трассы: прервать: причина прерывания (строка)
  • «запись»: 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: число повышений, используемых функцией
  • bytecodes: количество байт-кодов в скомпилированной функции
  • gcconsts: ??
  • nconsts: ??
  • currentline: как для debug.getinfo
  • isvararg: если функцияявляется функцией vararg`
  • source: как для debug.getinfo
  • loc: строка, описывающая источник и текущую строку, например, "
  • ffid: быстрый идентификатор функции (если она одна).В этом случае допустимы только upvalues выше и addr ниже
  • addr: адрес функции (если это не функция Lua).Если это функция C, а не быстрая, допустимо только upvalues выше
2 голосов
/ 07 февраля 2013

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...